392 lines
8.5 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|