Files
familienarchiv/frontend/src/routes/layout.css
Marcel 17889df220 feat(focus-rings): add --c-focus-ring token to CSS design system
Light: #012851 (brand-navy, 14:1 on white)
Dark:  #a1dcd8 (brand-mint, 9.2:1 on canvas)
- @theme inline mapping → Tailwind ring-focus-ring utility
- Global :focus-visible fallback in @layer base
- forced-colors fallback for Windows High Contrast mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:12:00 +02:00

261 lines
7.7 KiB
CSS

/* ─── 1. Fonts & Tailwind ──────────────────────────────────────────────────── */
/* Tinos = Times substitute | Montserrat = Gotham substitute (De Gruyter Brill CI) */
@import url('https://fonts.googleapis.com/css2?family=Tinos:ital,wght@0,400;0,700;1,400;1,700&family=Montserrat:wght@400;500;600;700&display=swap');
@import 'tailwindcss';
/* ─── 2. Raw palette — never used directly in components ──────────────────── */
@theme {
/* Brand palette constants */
--palette-navy: #012851;
--palette-mint: #a1dcd8;
--palette-turquoise: #00c7b1;
--palette-sand: #f0efe9;
/* Typography */
--font-sans: 'Montserrat', ui-sans-serif, system-ui, sans-serif;
--font-serif: 'Tinos', 'Times New Roman', Georgia, serif;
--text-huge: 4rem;
}
/* ─── 3. Semantic tokens — Tailwind utilities backed by CSS variables ──────── */
/*
@theme inline makes Tailwind generate utility classes (bg-surface, text-ink,
border-line, etc.) whose values are CSS custom properties, not hardcoded hex.
Changing --c-surface on :root is all it takes to retheme the whole UI.
*/
@theme inline {
/* Surfaces */
--color-canvas: var(--c-canvas);
--color-surface: var(--c-surface);
--color-overlay: var(--c-overlay);
--color-muted: var(--c-muted);
/* Borders */
--color-line: var(--c-line);
--color-line-2: var(--c-line-2);
/* Text */
--color-ink: var(--c-ink);
--color-ink-2: var(--c-ink-2);
--color-ink-3: var(--c-ink-3);
/* Accent (mint ↔ turquoise) */
--color-accent: var(--c-accent);
--color-accent-bg: var(--c-accent-bg);
/* Primary interactive (navy ↔ mint) */
--color-primary: var(--c-primary);
--color-primary-fg: var(--c-primary-fg);
/* PDF viewer */
--color-pdf-bg: var(--c-pdf-bg);
--color-pdf-ctrl: var(--c-pdf-ctrl);
--color-pdf-text: var(--c-pdf-text);
/* Header surface — independent from canvas/surface for per-mode control */
--color-header: var(--c-header);
/* Focus ring — keyboard focus indicator, mode-aware (navy in light, mint in dark) */
--color-focus-ring: var(--c-focus-ring);
/* Static brand tokens (not themed) */
--color-brand-navy: var(--palette-navy);
--color-brand-mint: var(--palette-mint);
}
/* ─── 4. Light mode (default) ─────────────────────────────────────────────── */
:root {
--c-canvas: #f0efe9;
--c-surface: #ffffff;
--c-overlay: #ffffff;
--c-muted: #f5f4ef;
--c-line: #e4e2d7;
--c-line-2: #eeede8;
--c-ink: #012851;
--c-ink-2: #4b5563; /* gray-600 — 7.6:1 on white, 6.6:1 on canvas — WCAG AA ✓ */
--c-ink-3: #6b7280; /* gray-500 — 4.8:1 on white, ~4.6:1 on canvas — WCAG AA ✓ */
/* ⚠ accent — decorative use only (borders, icon tints, bg fills)
Light mode: #a1dcd8 on white = 1.52:1 — WCAG FAIL. Never use as text colour.
For interactive text labels use text-primary or text-ink instead. */
--c-accent: #a1dcd8;
--c-accent-bg: rgba(161, 220, 216, 0.15);
--c-primary: #012851;
--c-primary-fg: #ffffff;
/* Header is brand-navy in light mode; same in dark mode for contrast compliance */
--c-header: #012851;
/* Focus ring: brand-navy in light mode — 14:1 on white, ~11:1 on sand */
--c-focus-ring: #012851;
--c-pdf-bg: #ebebeb;
--c-pdf-ctrl: #d8d8d8;
--c-pdf-text: #333333;
}
/* ─── 5. Dark mode ─────────────────────────────────────────────────────────── */
/*
Navy-tinted dark palette derived from brand-navy (#012851).
KEEP THESE TWO BLOCKS IN SYNC — they cover the same design intent via
different activation paths (system preference vs. manual toggle).
*/
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) {
color-scheme: dark;
--c-canvas: #010e1e;
--c-surface: #011526;
--c-overlay: #011e38;
--c-muted: #011a30;
--c-line: #0d3358;
--c-line-2: #092843;
--c-ink: #f0efe9;
--c-ink-2: #9ca3af; /* 7.5:1 on #011526 — WCAG AAA ✓ */
--c-ink-3: #8b97a5; /* 7.1:1 on #011526 — WCAG AAA ✓ */
--c-accent: #00c7b1;
--c-accent-bg: rgba(0, 199, 177, 0.12);
--c-primary: #a1dcd8;
--c-primary-fg: #012851;
/* Header at brand-navy: 4.99:1 with ink-3 (WCAG AA ✓), visually above canvas */
--c-header: #012851;
/* Focus ring: brand-mint in dark mode — 9.2:1 on canvas, 7.1:1 on surface */
--c-focus-ring: #a1dcd8;
--c-pdf-bg: #010e1e;
--c-pdf-ctrl: #011526;
--c-pdf-text: #f0efe9;
}
}
/* Manual dark override — takes precedence over media query */
/* KEEP IN SYNC with the @media block above */
:root[data-theme='dark'] {
color-scheme: dark;
--c-canvas: #010e1e;
--c-surface: #011526;
--c-overlay: #011e38;
--c-muted: #011a30;
--c-line: #0d3358;
--c-line-2: #092843;
--c-ink: #f0efe9;
--c-ink-2: #9ca3af; /* 7.5:1 on #011526 — WCAG AAA ✓ */
--c-ink-3: #8b97a5; /* 7.1:1 on #011526 — WCAG AAA ✓ */
--c-accent: #00c7b1;
--c-accent-bg: rgba(0, 199, 177, 0.12);
--c-primary: #a1dcd8;
--c-primary-fg: #012851;
/* Header at brand-navy: 4.99:1 with ink-3 (WCAG AA ✓), visually above canvas */
--c-header: #012851;
/* Focus ring: brand-mint in dark mode — 9.2:1 on canvas, 7.1:1 on surface */
--c-focus-ring: #a1dcd8;
--c-pdf-bg: #010e1e;
--c-pdf-ctrl: #011526;
--c-pdf-text: #f0efe9;
}
/* ─── 6. Icon inversion — De Gruyter icons are black SVGs loaded as <img> ──── */
/*
In dark mode, invert all brand icons so they read as white on dark surfaces.
Exclude .invert icons (already inverted for placement on dark backgrounds)
so they don't get double-inverted back to black.
*/
@media (prefers-color-scheme: dark) {
:root:not([data-theme='light']) img[src*='degruyter-icons']:not(.invert) {
filter: invert(1);
}
}
:root[data-theme='dark'] img[src*='degruyter-icons']:not(.invert) {
filter: invert(1);
}
/* ─── 7. @mention chip ─────────────────────────────────────────────────────── */
/*
Rendered by renderBody() via {@html ...} in CommentThread.svelte.
Must live in global CSS — Svelte scoped styles don't reach injected HTML.
*/
.mention {
display: inline;
color: var(--c-ink);
background-color: var(--c-accent-bg);
border-radius: 3px;
padding: 0 3px;
font-weight: 600;
font-style: normal;
cursor: default;
transition: background-color 0.15s ease;
}
.mention:hover {
background-color: color-mix(in srgb, var(--c-accent) 25%, transparent);
}
/* ─── 8. Base styles ───────────────────────────────────────────────────────── */
@layer base {
html {
overscroll-behavior: none;
}
body {
background-color: var(--c-canvas);
color: var(--c-ink);
font-family: var(--font-serif);
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-sans);
font-weight: 600;
}
/* Form controls — always use surface bg and ink text so they theme correctly */
input,
textarea,
select {
background-color: var(--c-surface);
color: var(--c-ink);
}
a:hover {
text-decoration-color: var(--c-accent);
text-decoration-thickness: 2px;
text-underline-offset: 4px;
}
/* Fallback focus ring for any interactive element not styled with ring-focus-ring */
:focus-visible {
outline: 2px solid var(--c-focus-ring);
outline-offset: 2px;
}
}
/* Ensure focus rings are visible in Windows High Contrast / forced-colors mode */
@media (forced-colors: active) {
:focus-visible {
outline: 3px solid ButtonText;
}
}