Files
desa-darmasaba/src/utils/themeTokens.ts
2026-02-25 21:24:39 +08:00

392 lines
8.5 KiB
TypeScript

/**
* Unified Theme Tokens for Admin Dashboard
*
* Berdasarkan spesifikasi: darkMode.md
*
* Semua styling constants disimpan di sini untuk konsistensi
* Edit di sini = edit di seluruh aplikasi
*
* Usage:
* import { themeTokens } from '@/utils/themeTokens';
*
* // Light mode (default)
* const tokens = themeTokens(false);
*
* // Dark mode
* const tokens = themeTokens(true);
*/
export type ThemeTokens = {
// Colors
colors: {
primary: string;
primaryLight: string;
primaryDark: string;
gradient: {
from: string;
to: string;
};
// Backgrounds
bg: {
base: string;
main: string;
app: string;
surface: string;
surfaceElevated: string;
header: string;
navbar: string;
card: string;
hover: string;
tableHeader: string;
tableHover: string;
};
// Text
text: {
primary: string;
secondary: string;
tertiary: string;
muted: string;
brand: string;
inverse: string;
link: string;
};
// Borders
border: {
default: string;
soft: string;
strong: string;
};
// Status
success: string;
warning: string;
error: string;
info: string;
};
// Typography
typography: {
h1: {
fz: string;
fw: number;
lh: number;
};
h2: {
fz: string;
fw: number;
lh: number;
};
h3: {
fz: string;
fw: number;
lh: number;
};
h4: {
fz: string;
fw: number;
lh: number;
};
body: {
fz: string;
fw: number;
lh: number;
};
small: {
fz: string;
fw: number;
lh: number;
};
label: {
fz: string;
fw: number;
lh: number;
};
};
// Spacing
spacing: {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
};
// Border Radius
radius: {
sm: string;
md: string;
lg: string;
xl: string;
};
// Shadows
shadows: {
none: string;
sm: string;
md: string;
lg: string;
};
// Layout
layout: {
headerHeight: number;
navbarWidth: {
base: number;
sm: number;
lg: number;
};
};
};
export const themeTokens = (isDark: boolean = false): ThemeTokens => {
// Base colors - tetap menggunakan colors.ts sebagai base untuk light mode
const baseColors = {
'orange': '#FCAE00',
'blue-button': '#0A4E78',
'blue-button-1': '#E5F2FA',
'blue-button-2': '#B8DAEF',
'blue-button-3': '#8AC1E3',
'blue-button-4': '#5DA9D8',
'blue-button-5': '#2F91CC',
'blue-button-6': '#083F61',
'blue-button-7': '#062F49',
'blue-button-8': '#041F32',
'blue-button-trans': '#628EC6',
'white-1': '#FBFBFC',
'white-trans-1': 'rgba(255, 255, 255, 0.5)',
'white-trans-2': 'rgba(255, 255, 255, 0.7)',
'white-trans-3': 'rgba(255, 255, 255, 0.9)',
'grey-1': '#F4F5F6',
'grey-2': '#CBCACD',
'Bg': '#D1d9e8',
'BG-trans': '#B1C5F2',
};
/**
* DARK MODE PALETTE
* Berdasarkan spesifikasi: darkMode.md
*/
const darkColors = {
// Background Layers
bgBase: '#0B1220',
bgApp: '#0F172A',
bgCard: '#162235',
bgSurface: '#1E2A3D',
// Borders
borderDefault: '#2A3A52',
borderSoft: '#22314A',
// Text
textPrimary: '#E5E7EB',
textSecondary: '#9CA3AF',
textMuted: '#6B7280',
textInverse: '#020617',
// Accent & Actions
primaryAction: '#3B82F6',
primaryHover: '#2563EB',
primaryActive: '#1D4ED8',
link: '#60A5FA',
// Status
success: '#22C55E',
warning: '#FACC15',
error: '#EF4444',
info: '#38BDF8',
// Hover states
hoverSoft: 'rgba(255,255,255,0.03)',
hoverMedium: 'rgba(255,255,255,0.04)',
activeAccent: 'rgba(59,130,246,0.15)',
};
/**
* LIGHT MODE PALETTE
* Original light theme
*/
const lightColors = {
bgBase: '#f6f9fc',
bgApp: '#ffffff',
bgCard: '#ffffff',
bgSurface: '#f8fafc',
borderDefault: '#e2e8f0',
borderSoft: '#e9ecef',
textPrimary: '#1a1b1e',
textSecondary: '#495057',
textMuted: '#868e96',
textInverse: '#ffffff',
primaryAction: baseColors['blue-button'],
primaryHover: '#083F61',
primaryActive: '#062F49',
link: '#2563eb',
hoverSoft: 'rgba(25, 113, 194, 0.03)',
hoverMedium: 'rgba(25, 113, 194, 0.05)',
activeAccent: 'rgba(25, 113, 194, 0.1)',
success: '#22c55e',
warning: '#facc15',
error: '#ef4444',
info: '#38bdf8',
};
const current = isDark ? darkColors : lightColors;
return {
colors: {
primary: current.primaryAction,
primaryLight: isDark ? current.activeAccent : baseColors['blue-button-1'],
primaryDark: current.primaryActive,
gradient: {
from: current.primaryAction,
to: isDark ? '#60A5FA' : '#228be6',
},
bg: {
base: current.bgBase,
main: isDark ? current.bgBase : 'linear-gradient(180deg, #fdfdfd, #f6f9fc)',
app: current.bgApp,
surface: current.bgSurface,
surfaceElevated: isDark ? '#253347' : '#ffffff',
header: isDark
? `linear-gradient(180deg, ${current.bgApp} 0%, ${current.bgBase} 100%)`
: 'linear-gradient(90deg, #ffffff, #f9fbff)',
navbar: current.bgApp,
card: current.bgCard,
hover: current.hoverMedium,
tableHeader: current.bgSurface,
tableHover: isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.02)',
},
text: {
primary: current.textPrimary,
secondary: current.textSecondary,
tertiary: current.textMuted,
muted: current.textMuted,
brand: current.primaryAction,
inverse: current.textInverse,
link: current.link,
},
border: {
default: current.borderDefault,
soft: current.borderSoft,
strong: isDark ? '#3A4A62' : '#ced4da',
},
success: current.success,
warning: current.warning,
error: current.error,
info: current.info,
},
typography: {
h1: {
fz: isDark ? '2rem' : '2.25rem',
fw: 700,
lh: 1.2,
},
h2: {
fz: isDark ? '1.75rem' : '2rem',
fw: 700,
lh: 1.25,
},
h3: {
fz: isDark ? '1.5rem' : '1.75rem',
fw: 700,
lh: 1.3,
},
h4: {
fz: isDark ? '1.25rem' : '1.5rem',
fw: 600,
lh: 1.35,
},
body: {
fz: '1rem',
fw: 400,
lh: 1.5,
},
small: {
fz: '0.875rem',
fw: 400,
lh: 1.4,
},
label: {
fz: '0.75rem',
fw: 600,
lh: 1.4,
},
},
spacing: {
xs: '0.625rem',
sm: '1rem',
md: '1.5rem',
lg: '2rem',
xl: '2.5rem',
},
radius: {
sm: '0.5rem', // 8px
md: '0.75rem', // 12px
lg: '1rem', // 16px
xl: '1.25rem', // 20px
},
shadows: {
none: 'none',
sm: isDark ? '0 1px 3px rgba(0,0,0,0.3)' : '0 1px 3px rgba(0,0,0,0.1)',
md: isDark ? '0 4px 6px rgba(0,0,0,0.3)' : '0 4px 6px rgba(0,0,0,0.1)',
lg: isDark ? '0 10px 15px rgba(0,0,0,0.3)' : '0 10px 15px rgba(0,0,0,0.1)',
},
layout: {
headerHeight: 64,
navbarWidth: {
base: 260,
sm: 280,
lg: 300,
},
},
};
};
// Export default theme instances
export const lightTheme = themeTokens(false);
export const darkTheme = themeTokens(true);
// Helper untuk mendapatkan responsive font size
export const getResponsiveFz = (isDark: boolean = false) => ({
base: isDark ? 'md' : 'lg',
md: isDark ? 'lg' : 'xl',
});
// Helper untuk mendapatkan color berdasarkan state
export const getActiveColor = (isActive: boolean, isDark: boolean = false) =>
isActive ? themeTokens(isDark).colors.primary : isDark ? themeTokens(isDark).colors.text.secondary : 'gray';
// Helper untuk mendapatkan background hover
export const getHoverBackground = (isActive: boolean, isDark: boolean = false) => {
const tokens = themeTokens(isDark);
return isActive
? tokens.colors.bg.hover
: tokens.colors.bg.hover;
};
// Helper untuk active state dengan accent bar (sidebar)
export const getActiveStateStyles = (isActive: boolean, isDark: boolean = false) => {
const tokens = themeTokens(isDark);
if (isActive) {
return {
backgroundColor: isDark ? tokens.colors.bg.hover : 'rgba(25, 113, 194, 0.1)',
borderLeft: isDark ? `3px solid ${tokens.colors.primary}` : '3px solid #1971c2',
};
}
return {
'&:hover': {
backgroundColor: tokens.colors.bg.hover,
},
};
};