diff --git a/CLAUDE.md b/CLAUDE.md index 945a331..deac08e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -233,6 +233,9 @@ Presentational config separate from `config.yml`. Controls accent colour, dark/l | `nav-link` | `--nav-link-colour` | falls back to `text` | | `nav-link-active` | `--nav-link-active-colour` | falls back to `accent` | | `nav-section-heading` | `--nav-section-heading-colour` | falls back to `text-muted` | +| `nav-sitename` | `--nav-sitename-colour` | falls back to `nav-link` | +| `nav-description` | `--nav-description-colour` | falls back to `nav-section-heading` | +| `nav-toggle` | `--nav-toggle-colour` | falls back to `nav-section-heading` | | `divider` | `--divider` | `color-mix(in srgb, background 85%, text)` | **When to use nav-link keys:** When `nav-background` matches or is very close to `accent`, the default behaviour (active link coloured with `accent`) makes links invisible. Set `nav-link`, `nav-link-active`, and `nav-section-heading` explicitly so all three are legible against `nav-background`. Example: a red nav background needs white (`#FFFFFF`) for all three nav colour keys. diff --git a/app/index.html b/app/index.html index a910816..b0a722b 100644 --- a/app/index.html +++ b/app/index.html @@ -59,6 +59,9 @@ if ('serviceWorker' in navigator) { --nav-link-colour: var(--nav-font-colour); --nav-link-active-colour: var(--accent); --nav-section-heading-colour: var(--font-colour-muted); + --nav-sitename-colour: var(--nav-link-colour); + --nav-description-colour: var(--nav-section-heading-colour); + --nav-toggle-colour: var(--nav-section-heading-colour); --nav-active-bg: rgba(var(--accent-rgb), 0.10); --nav-hover-bg: rgba(var(--accent-rgb), 0.05); --font-colour: #1E293B; @@ -91,6 +94,9 @@ if ('serviceWorker' in navigator) { --bg-nav: #1E293B; --nav-font-colour: #E2E8F0; --nav-link-colour: var(--nav-font-colour); + --nav-sitename-colour: var(--nav-link-colour); + --nav-description-colour: var(--nav-section-heading-colour); + --nav-toggle-colour: var(--nav-section-heading-colour); --nav-link-active-colour: var(--accent); --nav-section-heading-colour: var(--font-colour-muted); --nav-active-bg: rgba(96, 165, 250, 0.15); @@ -196,14 +202,14 @@ body { font-family: var(--font-title); font-weight: var(--font-title-weight); font-size: 1.15rem; - color: var(--nav-link-colour, var(--font-colour)); + color: var(--nav-sitename-colour); line-height: 1.3; text-decoration: none; display: block; } .sidebar-sitename:hover { color: var(--nav-link-active-colour, var(--accent)); } -.sidebar-description { font-size: 0.8rem; color: var(--nav-section-heading-colour, var(--font-colour-muted)); margin-top: 0.25rem; line-height: 1.4; } +.sidebar-description { font-size: 0.8rem; color: var(--nav-description-colour); margin-top: 0.25rem; line-height: 1.4; } /* Search */ .search-container { padding: 0.75rem 1.25rem; flex-shrink: 0; } @@ -341,9 +347,9 @@ body { gap: 0.5rem; padding: 0.4rem 0.6rem; font-size: 0.8rem; - color: var(--nav-section-heading-colour, var(--font-colour-muted)); + color: var(--nav-toggle-colour); background: none; - border: 1px solid color-mix(in srgb, var(--bg-nav) 70%, var(--nav-link-colour, var(--font-colour))); + border: 1px solid color-mix(in srgb, var(--bg-nav) 70%, var(--nav-toggle-colour)); border-radius: 6px; cursor: pointer; font-family: var(--font-body); @@ -351,7 +357,7 @@ body { width: 100%; justify-content: center; } -.theme-toggle:hover { color: var(--nav-link-colour, var(--font-colour)); border-color: var(--nav-section-heading-colour, var(--font-colour-muted)); } +.theme-toggle:hover { color: var(--nav-link-colour, var(--font-colour)); border-color: var(--nav-toggle-colour); } .theme-toggle svg { width: 16px; height: 16px; flex-shrink: 0; } /* ═══════════════════════════════════════════ @@ -1259,6 +1265,9 @@ body { if (m['nav-link']) vars.push(`--nav-link-colour: ${m['nav-link']}`); if (m['nav-link-active']) vars.push(`--nav-link-active-colour: ${m['nav-link-active']}`); if (m['nav-section-heading']) vars.push(`--nav-section-heading-colour: ${m['nav-section-heading']}`); + if (m['nav-sitename']) vars.push(`--nav-sitename-colour: ${m['nav-sitename']}`); + if (m['nav-description']) vars.push(`--nav-description-colour: ${m['nav-description']}`); + if (m['nav-toggle']) vars.push(`--nav-toggle-colour: ${m['nav-toggle']}`); if (m['divider']) vars.push(`--divider: ${m['divider']}`); if (vars.length) modeCss += `:root[data-theme="${mode}"] { ${vars.join('; ')}; }\n`; }); diff --git a/app/theme.yml b/app/theme.yml index e5ff3fe..2fe1838 100644 --- a/app/theme.yml +++ b/app/theme.yml @@ -15,6 +15,9 @@ light: # nav-link: "#1E293B" # inactive nav link text (defaults to text) # nav-link-active: "#2563EB" # active nav link text (defaults to accent) # nav-section-heading: "#64748B" # nav section label text (defaults to text-muted) + # nav-sitename: "#1E293B" # site name in sidebar header (defaults to nav-link) + # nav-description: "#64748B" # site description in sidebar header (defaults to nav-section-heading) + # nav-toggle: "#64748B" # dark/light mode toggle (defaults to nav-section-heading) # divider: "#CBD5E1" # border/hr colour (defaults to color-mix of background + text) dark: @@ -26,6 +29,9 @@ dark: # nav-link: "#E2E8F0" # inactive nav link text (defaults to text) # nav-link-active: "#60A5FA" # active nav link text (defaults to accent) # nav-section-heading: "#94A3B8" # nav section label text (defaults to text-muted) + # nav-sitename: "#E2E8F0" # site name in sidebar header (defaults to nav-link) + # nav-description: "#94A3B8" # site description in sidebar header (defaults to nav-section-heading) + # nav-toggle: "#94A3B8" # dark/light mode toggle (defaults to nav-section-heading) # divider: "#334155" # border/hr colour (defaults to color-mix of background + text) # ────────────────────────────────── diff --git a/docs/claude-design.md b/docs/claude-design.md index f66c70c..375eb6e 100644 --- a/docs/claude-design.md +++ b/docs/claude-design.md @@ -21,10 +21,13 @@ light: nav-background: "#F8FAFC" # sidebar/nav panel background text: "#1E293B" # body text text-muted: "#64748B" # secondary text, captions - nav-link: "#1E293B" # inactive nav link text - nav-link-active: "#2563EB" # active (current page) nav link text + nav-link: "#1E293B" # inactive nav link text + nav-link-active: "#2563EB" # active (current page) nav link text nav-section-heading: "#64748B" # nav section label text (uppercase, small) - # divider: "#CBD5E1" # omit to auto-derive via color-mix(background, text) + nav-sitename: "#1E293B" # site name in sidebar header + nav-description: "#64748B" # site description below the site name + nav-toggle: "#64748B" # dark/light mode toggle button + # divider: "#CBD5E1" # omit to auto-derive via color-mix(background, text) dark: accent: "#60A5FA" @@ -35,7 +38,10 @@ dark: nav-link: "#E2E8F0" nav-link-active: "#60A5FA" nav-section-heading: "#94A3B8" - # divider: "#334155" # omit to auto-derive via color-mix(background, text) + nav-sitename: "#E2E8F0" + nav-description: "#94A3B8" + nav-toggle: "#94A3B8" + # divider: "#334155" # omit to auto-derive via color-mix(background, text) # ────────────────────────────────── # Semantic colours @@ -95,38 +101,53 @@ nav-width: 20em --- -## Critical rule: nav contrast +## Nav colour keys: when to set them -The renderer defaults `nav-link-active` to `accent` and `nav-link` to `text`. -When `nav-background` and `accent` share the same hue (or are very close), -active nav links become invisible — the coloured text disappears into a -coloured background. +There are six nav colour keys divided into two groups: -The nav colour keys also control the **site name, site description, and -dark/light mode toggle** — all three live inside the sidebar and inherit from -the same variables. On muted or neutral nav backgrounds the content-area -fallbacks (`text`, `text-muted`) are fine. On any saturated or bold nav -background the contrast between `text` and `text-muted` is likely too low for -these elements to remain legible, so all three nav colour keys must be set -explicitly. +**Nav links and labels** — control the navigation list itself: +- `nav-link` — inactive link text (defaults to `text`) +- `nav-link-active` — active/current page link text (defaults to `accent`) +- `nav-section-heading` — uppercase section labels (defaults to `text-muted`) -**Rule of thumb:** if `nav-background` has a saturation above roughly 20 % or -a lightness below 30 % (dark sidebar) or above 85 % (near-white sidebar that -differs noticeably from the page background), set `nav-link`, -`nav-link-active`, and `nav-section-heading` explicitly for that mode. +**Sidebar header elements** — control the branding area above the nav list: +- `nav-sitename` — site name (defaults to `nav-link`) +- `nav-description` — subtitle below the site name (defaults to `nav-section-heading`) +- `nav-toggle` — dark/light mode toggle button (defaults to `nav-section-heading`) -**Always set all three nav colour keys explicitly** whenever `nav-background` -is anything other than a neutral near-white (light) or near-black (dark). +### When the defaults are fine + +On themes where `nav-background` is a neutral near-white (light mode) or +near-black (dark mode), `text` and `text-muted` read well against the nav +background. All six keys can be omitted and the fallback chain works correctly. + +### When to set the keys explicitly + +Set all six keys whenever `nav-background` is anything other than a neutral: +any saturated brand colour (red, navy, forest green, teal), any noticeably +dark sidebar in an otherwise light design, or any light-but-tinted background. + +The two groups can be set independently. On a subtly tinted nav where the +link defaults look fine but the site name needs slightly more weight or a +different shade, set only the header keys (`nav-sitename`, `nav-description`, +`nav-toggle`) and leave the nav link keys to their defaults. + +**Rule of thumb:** if `nav-background` has saturation above ~20 % or lightness +below 30 % (dark sidebar) or differs from `background` by more than a slight +tint, set all six explicitly for that mode. ### Pattern: accent-coloured nav (e.g. brand red, navy, forest green) ```yaml light: accent: "#D00C33" - nav-background: "#D00C33" # same as accent — nav links MUST be overridden + nav-background: "#D00C33" # same as accent — all nav keys must be set nav-link: "#FFFFFF" nav-link-active: "#FFFFFF" nav-section-heading: "rgba(255,255,255,0.65)" + nav-sitename: "#FFFFFF" + nav-description: "rgba(255,255,255,0.65)" + nav-toggle: "rgba(255,255,255,0.65)" dark: accent: "#D00C33" @@ -134,6 +155,9 @@ dark: nav-link: "#E2E2E2" nav-link-active: "#FFFFFF" nav-section-heading: "#888888" + nav-sitename: "#FFFFFF" + nav-description: "#888888" + nav-toggle: "#888888" ``` ### Pattern: dark nav in light mode (sidebar darker than content) @@ -144,6 +168,9 @@ light: nav-link: "#CBD5E1" nav-link-active: "#FFFFFF" nav-section-heading: "#64748B" + nav-sitename: "#FFFFFF" + nav-description: "#64748B" + nav-toggle: "#64748B" ``` ### Pattern: transparent / very light nav (default behaviour) @@ -189,9 +216,10 @@ uses its own per-callout colour settings rather than the semantic variables. ## Checklist before finalising a theme -- [ ] `nav-link`, `nav-link-active`, `nav-section-heading` specified for both - `light` and `dark` whenever `nav-background` is non-neutral -- [ ] All three nav link colours contrast against `nav-background` (WCAG AA minimum) +- [ ] All six nav colour keys (`nav-link`, `nav-link-active`, `nav-section-heading`, + `nav-sitename`, `nav-description`, `nav-toggle`) set for both `light` and + `dark` whenever `nav-background` is non-neutral +- [ ] All nav colours contrast against `nav-background` (WCAG AA minimum) - [ ] `colours-semantic-dark` provided with lighter variants of each colour - [ ] `callouts` `primary-colour` matches `colours-semantic` values for consistency - [ ] `divider` omitted unless the auto-derived value looks wrong (check hr and table borders)