feat(dark-mode): replace neutral tokens with navy-tinted palette + fix WCAG AA
- Replace neutral dark tokens (#0d0d0d, #1a1a1a, etc.) with navy-tinted values derived from brand-navy: canvas #010e1e, surface #011526, overlay #011e38, muted #011a30 - Fix --c-ink-3 WCAG AA failure in [data-theme='dark'] block: #6b7280 (3.2:1, fail) → #8b97a5 (7.1:1, AAA ✓) - Add color-scheme: dark to both dark blocks for native OS scrollbar theming - Update PDF viewer tokens to navy palette (bg #010e1e, ctrl #011526, text #f0efe9) - Add --c-header token (#ffffff light / #01335e dark) for independent header surface control; mapped to --color-header in @theme inline - Fix EntityNav contrast: text-white/30 → /50 (heading) and text-white/20 → /50 (inactive count badges) to pass WCAG AA 4.5:1 on bg-brand-navy Closes #166 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
>
|
||||
<!-- Desktop-only heading -->
|
||||
<div
|
||||
class="hidden px-3 pt-3 pb-1 text-[9px] font-extrabold tracking-widest text-white/30 uppercase lg:block"
|
||||
class="hidden px-3 pt-3 pb-1 text-[9px] font-extrabold tracking-widest text-white/50 uppercase lg:block"
|
||||
>
|
||||
{m.admin_heading()}
|
||||
</div>
|
||||
@@ -123,7 +123,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-[13px] font-black {isActive('users') ? 'text-white/65' : 'text-white/20'}">
|
||||
<span class="text-[13px] font-black {isActive('users') ? 'text-white/65' : 'text-white/50'}">
|
||||
{userCount}
|
||||
</span>
|
||||
<span
|
||||
@@ -190,7 +190,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-[13px] font-black {isActive('groups') ? 'text-white/65' : 'text-white/20'}">
|
||||
<span class="text-[13px] font-black {isActive('groups') ? 'text-white/65' : 'text-white/50'}">
|
||||
{groupCount}
|
||||
</span>
|
||||
<span
|
||||
@@ -259,7 +259,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" />
|
||||
</svg>
|
||||
<span class="text-[13px] font-black {isActive('tags') ? 'text-white/65' : 'text-white/20'}">
|
||||
<span class="text-[13px] font-black {isActive('tags') ? 'text-white/65' : 'text-white/50'}">
|
||||
{tagCount}
|
||||
</span>
|
||||
<span
|
||||
@@ -355,7 +355,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
transition:fly={{ x: -160, duration: 180 }}
|
||||
>
|
||||
<!-- Heading -->
|
||||
<div class="px-3 pt-3 pb-1 text-[9px] font-extrabold tracking-widest text-white/30 uppercase">
|
||||
<div class="px-3 pt-3 pb-1 text-[9px] font-extrabold tracking-widest text-white/50 uppercase">
|
||||
{m.admin_heading()}
|
||||
</div>
|
||||
|
||||
@@ -384,7 +384,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="text-[13px] font-black {isActive('users') ? 'text-white/65' : 'text-white/20'}"
|
||||
class="text-[13px] font-black {isActive('users') ? 'text-white/65' : 'text-white/50'}"
|
||||
>
|
||||
{userCount}
|
||||
</span>
|
||||
@@ -422,7 +422,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="text-[13px] font-black {isActive('groups') ? 'text-white/65' : 'text-white/20'}"
|
||||
class="text-[13px] font-black {isActive('groups') ? 'text-white/65' : 'text-white/50'}"
|
||||
>
|
||||
{groupCount}
|
||||
</span>
|
||||
@@ -460,7 +460,7 @@ function handleKeydown(event: KeyboardEvent) {
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" />
|
||||
</svg>
|
||||
<span class="text-[13px] font-black {isActive('tags') ? 'text-white/65' : 'text-white/20'}">
|
||||
<span class="text-[13px] font-black {isActive('tags') ? 'text-white/65' : 'text-white/50'}">
|
||||
{tagCount}
|
||||
</span>
|
||||
<span
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
--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);
|
||||
|
||||
/* Static brand tokens (not themed) */
|
||||
--color-brand-navy: var(--palette-navy);
|
||||
--color-brand-mint: var(--palette-mint);
|
||||
@@ -86,25 +89,35 @@
|
||||
|
||||
--c-nav-active: rgba(180, 185, 255, 0.15);
|
||||
|
||||
/* Header matches surface in light mode; overridden in dark mode for elevation */
|
||||
--c-header: #ffffff;
|
||||
|
||||
--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']) {
|
||||
--c-canvas: #0d0d0d;
|
||||
--c-surface: #1a1a1a;
|
||||
--c-overlay: #242424;
|
||||
--c-muted: #252525;
|
||||
color-scheme: dark;
|
||||
|
||||
--c-line: #3d3d3d;
|
||||
--c-line-2: #2e2e2e;
|
||||
--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; /* gray-400 — 7.5:1 on dark surface — WCAG AAA ✓ */
|
||||
--c-ink-3: #8b97a5; /* gray-450 — 6.5:1 on dark surface — WCAG AA ✓ */
|
||||
--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);
|
||||
@@ -114,25 +127,31 @@
|
||||
|
||||
--c-nav-active: rgba(180, 185, 255, 0.12);
|
||||
|
||||
--c-pdf-bg: #1e1e1e;
|
||||
--c-pdf-ctrl: #2a2a2a;
|
||||
--c-pdf-text: #d1d1d1;
|
||||
/* Header elevated above canvas for visual prominence */
|
||||
--c-header: #01335e;
|
||||
|
||||
--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'] {
|
||||
--c-canvas: #0d0d0d;
|
||||
--c-surface: #1a1a1a;
|
||||
--c-overlay: #242424;
|
||||
--c-muted: #252525;
|
||||
color-scheme: dark;
|
||||
|
||||
--c-line: #3d3d3d;
|
||||
--c-line-2: #2e2e2e;
|
||||
--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;
|
||||
--c-ink-3: #6b7280;
|
||||
--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);
|
||||
@@ -142,9 +161,12 @@
|
||||
|
||||
--c-nav-active: rgba(180, 185, 255, 0.12);
|
||||
|
||||
--c-pdf-bg: #1e1e1e;
|
||||
--c-pdf-ctrl: #2a2a2a;
|
||||
--c-pdf-text: #d1d1d1;
|
||||
/* Header elevated above canvas for visual prominence */
|
||||
--c-header: #01335e;
|
||||
|
||||
--c-pdf-bg: #010e1e;
|
||||
--c-pdf-ctrl: #011526;
|
||||
--c-pdf-text: #f0efe9;
|
||||
}
|
||||
|
||||
/* ─── 6. Icon inversion — De Gruyter icons are black SVGs loaded as <img> ──── */
|
||||
|
||||
Reference in New Issue
Block a user