Skip to content

Site shell: design tokens, dark mode, ALCops header, sticky matrix, favicon, Open Graph, the docs map, count tests and the grid keyboard route (#138, #124, #113) - #147

Merged
Arthurvdv merged 8 commits into
mainfrom
wp16/shell
Oct 28, 2026
Merged

Arthurvdv merged 8 commits into
mainfrom
wp16/shell

Conversation

@Arthurvdv

Copy link
Copy Markdown
Member

Closes #138
Closes #124
Closes #113

The design system and app shell of the dashboard site on own CSS (D59), the engine side of the docs map (D58), the template count test and the roving tabindex of the grid. / stays the matrix of the first stage (D54 holds until #139). Screenshots and the look check: #138 (comment)

The shell

  • Tokens and dark mode: site.css holds the light tokens under :root, the dark ones under [data-theme="dark"], and the same dark set again in a prefers-color-scheme block (a Site test checks the two dark blocks are equal line for line). Print uses the light set. An inline script first in <head> sets has-js and applies localStorage['rulebook-theme'] before the first paint. The toggle in js/shell.js sets the choice and goes back to the system setting on the second click. Dark mode follows the system without JavaScript.
  • Header: inline ALCops logo (partials/logo.html, currentColor), "Rulebook", the owner of repository as span.brand-org, the stage and level links labelled "Stage" and "Level", Set up, and Help (docsUrl; G repoints it). div.header-tools holds the theme toggle, the cart with its count and the menu button, and stays beside the brand below 768 px. shell.js measures the header into --header-h.
  • Mobile: below 768 px the navigation folds behind the menu from the first paint, and the open menu scrolls within the screen. Without JavaScript the navigation wraps and the header does not stick. The setup, level and rule pages have no horizontal scroll at 390 px; wide tables scroll inside themselves.
  • Matrix: div.matrix-scroll with a sticky header row and rule column, each action as a span.chip, and every data-* kept. role="grid" is on the table and there is one Tab stop per grid (Site: a roving tabindex for the matrix grid #113).
  • Head: title, meta description, og:title, og:description, og:type, og:url and og:site_name. The description comes from the content adapter (_content.gotmpl, _index.md). favicon.svg and a 180 px favicon.png sit in static/. There is no generator tag. <main class="page page-<kind>"> has a p.lead after each <h1>.
  • Footer: generatedAt, engineRef, the organization repository, the rulebook.json link and "Powered by ALCops" linking docs.home. Assets are at fixed paths: css/site.css, js/shell.js, js/matrix.js, js/cart.mjs, js/cart-core.mjs.

The docs map

Get-RulebookDocsMap -Ref in Rulebook.Site.psm1 writes docs after alProjectDocsUrl, one link per line ($script:MultiLineKeys). The keys are home (https://alcops.dev), dashboard, levels, overrides, quarantine, twins (pte-or-appsource.md), changingARule and alProject, all built through Get-RulebookDocsUrl. docsUrl and alProjectDocsUrl are unchanged and equal docs.dashboard and docs.alProject (asserted). The fixture is regenerated, and the key order is asserted in the Site and Publish suites. Contract: docs/dashboard.md section 5 and template/docs/dashboard.md section 6.

#124

  • One test counts template/ (without the site build output) against docs/reference/template-content.md, README.md and CONTRIBUTING.md. These now say 96 files, 64 by hand, 30 in site/ and 32 generated.
  • One test pins the site page count, stages + levels + rules + 2, at 39 on the fixture.
  • The regeneration test title, ARCHITECTURE and authoring-levels now say 14 hand-written files.
  • issueState lists closed-duplicate and closed.

Dropped from the spike

The landing page of wp16/spike-own-css (head 8babfc2, kept on origin for #139) is not in this PR:

  • the landing layouts/index.html;
  • the what-and-why text of content/_index.md;
  • the stage switcher's first link to stages/<first>/;
  • data-rulebook-landing on <main>;
  • the landing rules of site.css (hero, landing grid, prose, facts panel, stats, steps, cards, button).

The setup page's id="levels" from the spike stays.

Deviations

  • Row links in the keyboard route: with JavaScript the rule links of the row headers also leave the tab order and form the first column of the arrow-key route (Home goes there). Without that, a stage page kept 628 link Tab stops. Without JavaScript only the first cell is focusable, and the row links keep their Tab stops; the popover needs JavaScript anyway.
  • Deploy test: the forbidden-literal test skips .png, because the favicon's bytes matched \bv1\b.
  • Page descriptions: they are the top-level description key of the content adapter's AddPage call (read as .Description, checked with Hugo 0.165).
  • favicon.png is a committed binary from a one-off Playwright render. The steps are in CONTRIBUTING; there is no build step.
  • Footer link: the footer hard-codes https://github.com/<repository>, as the cart's empty-form link already does (GHES is unsupported, an existing limit).
  • Shared theme key: localStorage['rulebook-theme'] is shared by every project site of one github.io host, like the cart key (documented).

Contrast (WCAG, from the tokens of site.css)

Pair Light Dark Needed
Text on background 15.80 16.02 4.5
Text on surface (header, footer) 14.84 14.65 4.5
Muted on background 6.11 6.50 4.5
Muted on surface (footer, labels) 5.74 5.94 4.5
Muted on surface-2 (group rows) 5.39 5.23 4.5
Link (accent) on background 5.19 6.11 4.5
Link (accent) on surface 4.88 5.59 4.5
Current nav item (accent-text on accent) 5.19 6.11 4.5
Chip Error 6.03 7.04 4.5
Chip Warning 6.10 7.25 4.5
Chip Info 6.07 7.48 4.5
Chip Hidden 6.09 7.15 4.5
Chip None (on background) 5.33 6.15 4.5
Badge O (mark-text on override) 6.35 7.47 4.5
Badge Q (mark-text on quarantine) 6.43 7.48 4.5

Tests

  • Pester (local, Hugo 0.165 extended, the real-Hugo Site case ran):

    Suite Passed
    Rulebook.Site 54
    Rulebook.Deploy 76
    Publish.Action 52
    Rulebook.Template 77
    Rulebook.Publish 100
    Docs 340

    No failures and no skips.

  • node --test tests/site/*.test.mjs: 21 passed.

  • Playwright (throwaway, in a scratch folder, fixture build): 36 of 36 checks passed.

    • Toggle and system default, persistence across a reload.
    • No page scroll at 390 px.
    • Sticky header row and column.
    • Cart on a cell click.
    • The keyboard route.
    • The menu and the tools beside it; --header-h.
    • Anchors below the header.
    • Print in light.
    • No JavaScript.
    • No foreign request.

🤖 Generated with Claude Code

Arthurvdv and others added 7 commits October 28, 2026 15:40
…heet (#137)

Scratch branch of the WP16 spike, never merged. Tokens under :root, [data-theme=dark] and the
prefers-color-scheme block; header with the inline ALCops logo, Rulebook and the owner slug, labelled
stage and level navigation, Set up, Help, theme toggle, cart badge and a menu below 768 px (js/shell.js);
the landing page at / with cards, facts and the three-step path; the matrix in a scroll container with a
sticky header row and rule column; the action as a chip in each cell; footer with the publish facts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…concept (#138)

Get-RulebookDocsMap writes docs after alProjectDocsUrl: home (https://alcops.dev), dashboard, levels,
overrides, quarantine, twins (pte-or-appsource.md), changingARule and alProject, built through
Get-RulebookDocsUrl on the engine ref like docsUrl, which stays and equals docs.dashboard
(alProjectDocsUrl equals docs.alProject). The writer puts the map one link per line. Fixture
regenerated; the key order of the Publish suite and the cart node test follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ooter from the data file, one Tab stop per grid (#138, #113)

The landing layout of the spike leaves / (D54 holds until #139): index.html, content/_index.md and the
stage switcher are main's again, the landing rules leave site.css and main carries page-matrix. Every
page gets its description from the content adapter (stage and level descriptions, a fixed sentence
otherwise) for the meta description and the og: tags, a lead under its title, favicon.svg and a 180 px
favicon.png (static/), and the footer links docs.home. A grid is role=grid with one cell at tabindex 0;
matrix.js moves it with the arrow keys, Home, End, Ctrl+Home and Ctrl+End, and keeps it on a shown row
after filtering. Site tests: the shell markup, the head tags, the footer, the Tab stop and equal dark
token blocks; the stub, the CI job and the Publish suite count js/shell.js and the favicons; the Deploy
literal test skips the PNG.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nd the site page count (#124)

Rulebook.Template.Tests.ps1 counts template/ (without the site build output) and checks the numbers of
docs/reference/template-content.md, README.md and CONTRIBUTING.md, now 96 files with 30 in site/, and
pins stages + levels + rules + 2 = 39 on the site data fixture. The regeneration test and two docs say
14 hand-written files, as the list has. The issueState output names closed-duplicate and closed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on one line on a phone (#113, #138)

With JavaScript the rule links of the row headers leave the tab order and become the first column of
the arrow-key route, so a stage page is one Tab stop for its grid; Home goes to the row link. Below
768 px the endpoint table of the setup page scrolls inside itself instead of breaking each URL.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the release note (#138)

docs/dashboard.md: section 3 says the shell is built, section 4 has the layout with shell.js, the
partials and static/, and a new part on the shell (head, colour scheme, tokens, header, main, matrix,
keyboard route, footer, mobile); section 5 has the docs key. template/docs/dashboard.md: what every page
shows, dark mode and the keys in section 1, the tokens an organization changes in section 5, the docs key
in section 6. CONTRIBUTING: how to render favicon.png again. RELEASENOTES: the beta.8 line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nu, has-js from the head, anchors and print (#138)

shell.js measures the sticky header into --header-h (ResizeObserver, resize fallback; the open menu is left
out) and repeats the jump to an anchor once it is known; html gets scroll-padding-top from it and the
matrix container a left scroll padding for the sticky rule column. The theme toggle, the cart and the
menu button move out of #site-nav into div.header-tools, so the cart count stays in view below 768 px;
the brand gives way with an ellipsis on the organization. The inline head script sets has-js, so the
menu folds from the first paint; without JavaScript the header does not stick on a phone. The open menu
scrolls within the screen. matrix.js leaves Shift, Ctrl+Arrow and the other modifiers to the browser and
closes an open popover when the focus moves. Print uses the light tokens (compared with :root by the
token test); the SVG favicon link says sizes="any". The docs follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…px, dvh and the anchor padding without JavaScript (#138)

The header's min-height is the literal 3rem, not the measured --header-h, so a wrapped header shrinks back
when the window widens. shell.js skips the measurement only while the menu is open below 768 px, and
closes an open menu (aria-expanded false) when the window grows past 768 px. The open menu's height limit
has a 100dvh value after the 100vh fallback; without JavaScript on a phone the header does not stick, so
html keeps only 0.5rem of scroll padding.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant