fix(theme): align initial theme and visual tokens

This commit is contained in:
2026-09-06 21:58:55 +03:30
parent 463e72a404
commit 05c304adf4
4 changed files with 44 additions and 28 deletions

View File

@@ -15,13 +15,24 @@ export const metadata: Metadata = {
],
};
const themeScript = `
try {
const savedTheme = localStorage.getItem('portfolio-theme');
const systemTheme = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.dataset.theme = savedTheme || systemTheme;
} catch {}
`;
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" data-theme="light">
<html lang="en" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>

View File

@@ -18,14 +18,15 @@ const ThemeContext = createContext<ThemeContextType>({
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setThemeState] = useState<Theme>('light');
const [mounted, setMounted] = useState(false);
useEffect(() => {
const savedTheme = localStorage.getItem('portfolio-theme') as Theme | null;
const initialTheme = savedTheme || 'light';
const systemTheme: Theme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
const initialTheme = savedTheme || systemTheme;
setThemeState(initialTheme);
document.documentElement.setAttribute('data-theme', initialTheme);
setMounted(true);
}, []);
const setTheme = (newTheme: Theme) => {

View File

@@ -21,18 +21,6 @@ html {
color-scheme: light;
}
@media (max-width: 1024px) {
html {
font-size: 93.75%;
}
}
@media (max-width: 650px) {
html {
font-size: 87.5%;
}
}
html[data-theme="dark"] {
color-scheme: dark;
}
@@ -117,9 +105,11 @@ body {
}
.section-title {
font-size: var(--font-size-h2);
line-height: var(--line-height-h2);
font-weight: 700;
max-width: 24ch;
font-size: clamp(1.875rem, 3vw, 2.5rem);
line-height: 1.15;
font-weight: 760;
letter-spacing: -0.035em;
color: var(--text-primary);
margin-bottom: 0.75rem;
}
@@ -158,6 +148,20 @@ body {
box-shadow var(--motion-duration-default) ease,
transform var(--motion-duration-fast) var(--motion-ease-out);
border: 1px solid transparent;
white-space: nowrap;
}
.pill-btn:focus-visible,
a:focus-visible,
button:focus-visible,
input:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
}
.pill-btn:active,
button:active:not(:disabled) {
transform: translateY(1px) scale(0.99);
}
.pill-btn-outline {

View File

@@ -11,7 +11,7 @@
--color-blue-800: #091C3F;
--color-blue-900: #07132F;
--color-blue-950: #010D27;
--color-blue-white: #FFFFFF;
--color-blue-white: #F8FAFF;
--color-slate-300: #C3CFE0;
--color-slate-400: #A3B1C6;
--color-slate-600: #667085;
@@ -20,7 +20,7 @@
--color-border-dark: #1E3458;
--color-border-strong-light: #A9BEDA;
--color-border-strong-dark: #426B9D;
--color-black: #000000;
--color-black: #07132F;
--space-0: 0;
--space-1: 0.25rem;
--space-2: 0.5rem;
@@ -67,7 +67,7 @@
--font-size-h3: 1.25rem;
--font-size-body-large: 1.125rem;
--font-size-body-default: 1rem;
--font-size-body-small: 0.875rem;
--font-size-body-small: 0.9375rem;
--font-size-label-small: 0.75rem;
--font-size-button: 0.9375rem;
--line-height-display-hero: 4.5rem;
@@ -77,7 +77,7 @@
--line-height-h3: 1.75rem;
--line-height-body-large: 1.875rem;
--line-height-body-default: 1.625rem;
--line-height-body-small: 1.375rem;
--line-height-body-small: 1.5rem;
--line-height-label-small: 1.125rem;
--line-height-button: 1.25rem;
--letter-spacing-eyebrow: 0.16em;
@@ -87,9 +87,9 @@
--motion-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
/* Semantic Tokens (Light) */
--bg-page: #FFFFFF;
--bg-surface: #FAFAFA;
--bg-raised: #FFFFFF;
--bg-page: #F8FAFF;
--bg-surface: #F1F6FD;
--bg-raised: #FCFDFF;
--bg-subtle: #E6F0FE;
--text-primary: #0B2F5F;
--text-secondary: #38526E;
@@ -100,7 +100,7 @@
--accent-default: #3D8AFA;
--action-link: #1557A6;
--action-primary-bg: #103F7F;
--action-primary-fg: #FFFFFF;
--action-primary-fg: #F8FAFF;
--action-primary-hover: #0B3268;
--action-primary-pressed: #08284F;
--brand-band: #103F7F;
@@ -131,7 +131,7 @@
--text-primary: #F8FAFF;
--text-secondary: #C3CFE0;
--text-muted: #A3B1C6;
--text-on-band: #FFFFFF;
--text-on-band: #F8FAFF;
--border-default: #1E3458;
--border-strong: #426B9D;
--accent-default: #82B5FF;