Add configurable nav section toggle icons

Two new top-level theme.yml keys — nav-section-expand-icon and
nav-section-collapse-icon — replace the hardcoded arrow_right/arrow_drop_down
used on collapsible nav sections. Defaults preserve existing behaviour.

Eight new icon SVGs added to app/assets/icons/:
  keyboard_arrow_right / keyboard_arrow_down     (chevrons)
  keyboard_double_arrow_right / keyboard_double_arrow_down
  expand_content / collapse_content              (corner arrows)
  add / minimize                                 (plus/minus)

All eight added to STANDARD_ICONS so fetch-deps bundles them correctly.
CLAUDE.md key reference and docs/claude-design.md updated with the full
icon catalogue, pairing guidance, and style notes for Claude Design.

https://claude.ai/code/session_01NQKywehSj8Ku4yKhwB4VNB
This commit is contained in:
Claude 2026-05-18 15:08:59 +00:00
parent 648f1afcfd
commit 737049f19e
No known key found for this signature in database
12 changed files with 74 additions and 2 deletions

View file

@ -247,6 +247,17 @@ Presentational config separate from `config.yml`. Controls accent colour, dark/l
Keys in both blocks: `info`, `warning`, `success`, `error`.
**Nav section toggle icons** (top-level keys, not inside `light:`/`dark:`):
| Key | Default | Purpose |
|---|---|---|
| `nav-section-expand-icon` | `arrow_right` | icon shown when section is collapsed |
| `nav-section-collapse-icon` | `arrow_drop_down` | icon shown when section is expanded |
Available icon names: `arrow_right`, `arrow_drop_down`, `keyboard_arrow_right`, `keyboard_arrow_down`, `keyboard_double_arrow_right`, `keyboard_double_arrow_down`, `expand_content`, `collapse_content`, `add`, `minimize`.
These only apply to nav sections with `pagesvisibility: hidden` (collapsible sections).
### Icon system
All UI icons served as local SVGs from `app/assets/icons/`. No Google Fonts or external icon font. Icon names are normalised (lowercase, spaces → hyphens).

1
app/assets/icons/add.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>

After

Width:  |  Height:  |  Size: 134 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg>

After

Width:  |  Height:  |  Size: 176 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>

After

Width:  |  Height:  |  Size: 177 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>

After

Width:  |  Height:  |  Size: 144 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>

After

Width:  |  Height:  |  Size: 143 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18 6.41L16.59 5 12 9.58 7.41 5 6 6.41l6 6zm0 6l-1.41-1.41L12 15.58l-4.59-4.59L6 12.41l6 6z"/></svg>

After

Width:  |  Height:  |  Size: 190 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.41 6L5 7.41 9.58 12 5 16.59 6.41 18l6-6zm6 0l-1.41 1.41L15.58 12l-4.58 4.59L12.41 18l6-6z"/></svg>

After

Width:  |  Height:  |  Size: 191 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13H5v-2h14v2z"/></svg>

After

Width:  |  Height:  |  Size: 116 B

View file

@ -959,7 +959,7 @@ body {
let loadedFonts = new Set(); // track which font files have been loaded
// ─── Icons ────────────────────────────────────────────────
const STANDARD_ICONS = ['dark_mode','light_mode','menu','search','arrow_right','arrow_drop_down','mobile_arrow_down','language','info','warning','error','success','exclamation','dangerous','report','history','text_compare'];
const STANDARD_ICONS = ['dark_mode','light_mode','menu','search','arrow_right','arrow_drop_down','mobile_arrow_down','language','info','warning','error','success','exclamation','dangerous','report','history','text_compare','keyboard_arrow_right','keyboard_arrow_down','keyboard_double_arrow_right','keyboard_double_arrow_down','expand_content','collapse_content','add','minimize'];
const iconCache = {};
function normaliseIconName(name) {
@ -2427,8 +2427,10 @@ function fmtDatetime(dtStr) {
if (isHidden) {
const expanded = sectionExpanded(section.code);
const expandIcon = themeConfig['nav-section-expand-icon'] || 'arrow_right';
const collapseIcon = themeConfig['nav-section-collapse-icon'] || 'arrow_drop_down';
heading.innerHTML = '';
heading.appendChild(iconEl(expanded ? 'arrow_drop_down' : 'arrow_right', 'toggle-icon'));
heading.appendChild(iconEl(expanded ? collapseIcon : expandIcon, 'toggle-icon'));
heading.appendChild(el('span', { textContent: name }));
heading.addEventListener('click', () => {
toggleSection(section.code);

View file

@ -81,6 +81,17 @@ font-heading: "bunny:Noto Sans:700"
font-size: 1.0 # unitless multiplier (1.0 = 16px base)
line-height: 1.7 # unitless multiplier
# ──────────────────────────────────
# Nav section toggle icons
# Used on sections with pagesvisibility: hidden (collapsible sections).
# expand-icon shown when section is collapsed; collapse-icon when expanded.
# Options: arrow_right/arrow_drop_down (default) | keyboard_arrow_right/keyboard_arrow_down
# keyboard_double_arrow_right/keyboard_double_arrow_down
# expand_content/collapse_content | add/minimize
# ──────────────────────────────────
# nav-section-expand-icon: arrow_right
# nav-section-collapse-icon: arrow_drop_down
# ──────────────────────────────────
# Layout
# ──────────────────────────────────

View file

@ -92,6 +92,14 @@ font-heading: "bunny:IBM Plex Sans:700"
font-size: 1.0 # unitless multiplier (1.0 = 16px base)
line-height: 1.7 # unitless multiplier
# ──────────────────────────────────
# Nav section toggle icons
# expand-icon: shown when section is collapsed
# collapse-icon: shown when section is expanded
# ──────────────────────────────────
nav-section-expand-icon: arrow_right # default
nav-section-collapse-icon: arrow_drop_down # default
# ──────────────────────────────────
# Layout
# ──────────────────────────────────
@ -101,6 +109,38 @@ nav-width: 20em
---
## Nav section toggle icons
Sections with `pagesvisibility: hidden` in `nav.yml` are collapsible. The
expand and collapse icons are set independently at the top level of `theme.yml`
(not inside `light:` or `dark:` — they are not per-mode).
| Key | Default | Shown when |
|---|---|---|
| `nav-section-expand-icon` | `arrow_right` | section is collapsed |
| `nav-section-collapse-icon` | `arrow_drop_down` | section is expanded |
**Available pairs and their character:**
| Expand icon | Collapse icon | Character |
|---|---|---|
| `arrow_right` | `arrow_drop_down` | Solid filled triangles — compact, classic |
| `keyboard_arrow_right` | `keyboard_arrow_down` | Chevrons (/˅) — lighter, more modern |
| `keyboard_double_arrow_right` | `keyboard_double_arrow_down` | Double chevrons (»/⌄) — emphatic |
| `expand_content` | `collapse_content` | Corner-arrows — editorial, spatial |
| `add` | `minimize` | Plus/minus — very minimal, utilitarian |
Mix and match freely — the expand and collapse icons do not have to come from
the same pair, but keeping them visually related (same weight and style)
usually reads better.
**Matching icon style to nav style:** bold high-contrast themes (filled
triangle, plus/minus) suit designs with strong typographic weight. Lighter
themes pair better with chevrons. Editorial or magazine-style designs work
well with `expand_content`/`collapse_content`.
---
## Nav colour keys: when to set them
There are six nav colour keys divided into two groups: