Skip to content

Feat/design alignment - #151

Merged
Shrinks99 merged 32 commits into
mainfrom
feat/design-alignment
Sep 21, 2026
Merged

Shrinks99 merged 32 commits into
mainfrom
feat/design-alignment

Conversation

@matthewmr-eqty

@matthewmr-eqty matthewmr-eqty commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Body

The docs site was a vibe code experiment. It worked, but it looked nothing like our brand and
didn't use much of Equality. This branch reskins it by building the design out of some of the
components we ship, and uses that as a test of the system. The gap list it
produced is the point.

What changed:

  • Sidebar rows are Equality's dropdown item and the gov studio rail .
  • Below lg there was no navigation except search. There is now a drawer.
  • The table of contents tracks where you are as you scroll.
  • Prose runs on one spacing scale instead of three.
  • The theme control is one button, not a menu.
  • A header link can point at an SVG, which the GitHub link.
  • Converts most external CSS styling into in-line Tailwind classes

Ten places still needed hand rolling: no highlight token, no dark values on the
brand colours, collapsible content that cannot start open, no height prop on
CodeBlock. All logged as gaps rather than worked around quietly. Two are one
line fixes in packages/ui.

To check: pnpm build, then pnpm dev

Scaffolding

Commits: eight, one concern each, need/problem/solution in each body.

Commit Files
wip(docs): restyle the chrome against the Gov Studio board header, sidebar, search, page header, mobile drawer
feat(docs): show the product name in the header chrome/Header.*, styles/chrome.css, config.ts, demo config, eqty-mark.svg
feat(docs): let a header link supply its own SVG icon chrome/LinkIcon.*, chrome/Header.astro, chrome/NavDrawer.astro, config.ts, github.svg
feat(docs): make the theme control a cycling button chrome/ThemeToggle.*, chrome/Header.astro
feat(docs): rebuild the sidebar from Equality's own components chrome/NavTree.*, chrome/NavProduct.*, chrome/Sidebar.astro, chrome/NavDrawer.astro, scripts/eq-nav-*.ts
feat(docs): track the reading position on the table of contents chrome/TableOfContents.*, chrome/TocElbow.astro, chrome/TocList.astro, scripts/eq-toc.ts
fix(docs): give prose a single vertical rhythm chrome/Prose.*, styles/prose.css
feat(docs): end a page with a footer and some runway chrome/PageFooter.*, routes/docs.astro, layouts/DocsPage.astro.module.css

matthewmr-eqty and others added 27 commits September 18, 2026 12:23
Need. Every value a consumer can put in `search.provider` should do what it says.

Problem. It offered three and only two worked. `titles` rendered a search box
that could never return a result: the route emitted `/_docs/search.json`, but
nothing in the runtime ever read it, and the index itself was only titles,
descriptions and headings with no body text. `devProvider` was read by nothing
at all.

Solution. `provider` narrows to `pagefind | none`, `devProvider` goes, and the
route goes with them.
Need. Readers search the prose of every page, not just its title.

Problem. The package was instrumented for Pagefind and never wired to it.
`Prose.astro` already stamped `data-pagefind-body` and `search.provider`
already defaulted to `pagefind`, but nothing built an index.

`astro-pagefind` exists to do exactly this, and we cannot use it: it ships
TypeScript rather than JavaScript. A consumer importing it in
`astro.config.mjs` gets it compiled by Astro. We import it from code that is
already built and running in Node, and Node does not compile TypeScript inside
`node_modules`.

Solution. A small integration of our own, added by `autoIntegrations` when
`search.provider` is `pagefind`. It runs in `astro:build:done`, the first hook
where every page exists on disk, and every failure throws so a broken index
fails the build rather than shipping dead search.

`pagefind` is a plain dependency, not a peer: a consumer should get working
search from the four lines of config it already writes.
Need. A search result should say what the page is and where it sits, without opening it.

Problem. The index carried only the title. Nothing published the section, and a
page with no `description` fell back to Pagefind's excerpt, which is chosen for
the query and so usually starts mid-clause.

Solution. Three keys, all from data that already exists: `title` from the
heading, `crumbs` from `breadcrumbsFor` so the trail is the authored
`_group.yaml` labels at any depth, and `description` from frontmatter, falling
back to the first paragraph of prose. The fallback is indexed, never rendered.
Need. A reader reaches any page from anywhere, by keyboard or by tap.

Problem. There was no search UI, and the header's `search` slot could not be
filled

Solution. A modal over a dimmed page, opened by click, ⌘K or /. Composed from
Equality's exported `Dialog` and `Command` primitives, so `packages/ui` stays
untouched

Results are flat and in Pagefind's order, five at a time. An earlier version
grouped them by section, which reordered the ranking we adopted Pagefind for;
the section is a breadcrumb on each row instead.

Three lines are load-bearing and commented where they sit:
`shouldFilter={false}`, because cmdk would re-filter what Pagefind ranked;
`client:only`, because a server-rendered trigger would never wake up; and a
`showSearch` prop rather than slot fallback, because Astro registers a named
slot at compile time, so a forwarded slot always counts as filled.
Co-authored-by: Henry Wilkinson <henry@wilkinson.graphics>
Baseline for feedback. Six areas, built from nineteen logged decisions in
.anchor.local/living-docs/decisions.md, slug design-alignment.

- Header: brand renders the EQTY mark, inverted for light mode; 69px from the
  board; edges move to border-raised, which is the token Gov Studio pairs with a
  raised fill; utility controls go bare at every width.
- Sidebar: rows take Equality's house list recipe with Gov Studio's height cap and
  weight. Selected state is the board's node 2014:2145, an opaque lilac-700 to
  lilac-900 gradient. Opaque rather than alpha over brand-primary, whose contrast
  inverts between themes.
- Search: the same selected treatment, so the two agree.
- Page header: the breadcrumb renders at last. The trail was already computed on
  every page and thrown away into the search index. It shows ancestors only; the
  index keeps the page, because a result row needs it to identify itself.
- Mobile: a navigation drawer on the Popover API. Below lg the sidebar was
  display:none with no replacement, so a phone could only navigate by search.

Closes a live accessibility defect. headerLink.icon is optional, Equality's Icon is
Lucide-backed and ships no brand marks, and the label was hidden below sm, so
equality.eqtylab.io renders a 16x12px link with no accessible name. Configured links
move into the drawer, where their labels show.

Verified: root build green at 68 pages, check-no-utilities clean, 33 unit tests, 196
browser tests across five device profiles, lint and format clean. Not verified on real
hardware.

Browser tests live in .local/, which .gitignore covers, so they are not in this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Need. A reader should see what the site is and which product it documents.

Problem. The header showed only the logo, and `replacesTitle` hid the site name. Below
lg it swapped to a cropped mark, on a premise about width that measurement disproved.

Solution. Full wordmark at every width, then a constant "Docs" label. The product name
heads the sidebar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. The configured header links want icons, and the demo's only link is GitHub.

Problem. `icon` took a Lucide name, and Lucide ships no brand marks. So the GitHub link
rendered no icon, and below sm, with its label hidden, no accessible name either.

Solution. `icon` also takes a path to an SVG in the consumer's `public/`, rendered the
same way as the logo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. Changing theme is rare and low-stakes. It should cost one press.

Problem. The dropdown cost two, and made the reader open a menu and read three options
to make a choice that hardly matters. As a modal Radix menu it also died on iOS.

Solution. One button cycling System, Light, Dark, with the current state on its face.
Nothing to open, nothing to think through and select.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. The sidebar is the most-used surface here, and it should look like the design
system it documents.

Problem. It was styled from the Governance Studio board: a gradient selected row, a
shadow, a row height cap, none of which Equality ships. It read as a third language
beside the search palette and the theme menu.

Solution. Rows take Equality's dropdown item, the rail takes the Separator's colour,
and the current page holds the hover recipe, which search now shares.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. A reader scrolling a long page should see where they are.

Problem. The scroll-spy watched a narrow band, so nothing was marked at load or between
two headings. The rail was broken into dashes and its nesting had no connector.

Solution. The active heading is the last one above the reading line. The rail is
continuous, curves in and out of each nested level, and carries a lit segment that
travels along it as you scroll.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. Documentation is mostly body text, so block spacing is most of the page.

Problem. Blocks sat 8px apart while lines inside a paragraph sat 28px apart, so
consecutive paragraphs read as one. Headings sat the same distance above and below,
marking nothing.

Solution. One scale: 12 / 16 / 32 / 48 / 64. A heading binds tighter to the text it
owns than paragraphs bind to each other.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. A reader who finishes a page needs somewhere to go next, and a way to fix what
they just read.

Problem. The page stopped at its last line, 40px from the bottom. `footer.editUrl`,
`footer.showPrevNext` and `prevNextFor` were all built and none of them rendered.

Solution. Previous and next links, then a quiet row with "Edit this page". Space
separates it rather than a rule, and every page now ends with 128px of runway.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need: comments that warn you off a change, and nothing else.

Problem: those sat among comments describing what the code does.

Solution: the descriptions are gone, the warnings are one line each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Need: to try search locally before shipping a page.

Problem: Pagefind writes its index into dist, and astro dev never serves dist,
so the palette 404'd and asked for the build you had just run.

Solution: in dev, a request under /pagefind/ is read straight from dist on disk.
Results describe the last build; rebuild while dev runs and the next search sees it.

Reverses 748ba55, which had dev refuse to search rather than answer from the
last build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Need: to know in dev that the page you are editing is not in the index.

Problem: those last-build results look exactly like current ones.

Solution: a footer says so in dev; it compiles out of production.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Baseline for feedback. Six areas, built from nineteen logged decisions in
.anchor.local/living-docs/decisions.md, slug design-alignment.

- Header: brand renders the EQTY mark, inverted for light mode; 69px from the
  board; edges move to border-raised, which is the token Gov Studio pairs with a
  raised fill; utility controls go bare at every width.
- Sidebar: rows take Equality's house list recipe with Gov Studio's height cap and
  weight. Selected state is the board's node 2014:2145, an opaque lilac-700 to
  lilac-900 gradient. Opaque rather than alpha over brand-primary, whose contrast
  inverts between themes.
- Search: the same selected treatment, so the two agree.
- Page header: the breadcrumb renders at last. The trail was already computed on
  every page and thrown away into the search index. It shows ancestors only; the
  index keeps the page, because a result row needs it to identify itself.
- Mobile: a navigation drawer on the Popover API. Below lg the sidebar was
  display:none with no replacement, so a phone could only navigate by search.

Closes a live accessibility defect. headerLink.icon is optional, Equality's Icon is
Lucide-backed and ships no brand marks, and the label was hidden below sm, so
equality.eqtylab.io renders a 16x12px link with no accessible name. Configured links
move into the drawer, where their labels show.

Verified: root build green at 68 pages, check-no-utilities clean, 33 unit tests, 196
browser tests across five device profiles, lint and format clean. Not verified on real
hardware.

Browser tests live in .local/, which .gitignore covers, so they are not in this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Need. A reader should see what the site is and which product it documents.

Problem. The header showed only the logo, and `replacesTitle` hid the site name. Below
lg it swapped to a cropped mark, on a premise about width that measurement disproved.

Solution. Full wordmark at every width, then a constant "Docs" label. The product name
heads the sidebar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. The configured header links want icons, and the demo's only link is GitHub.

Problem. `icon` took a Lucide name, and Lucide ships no brand marks. So the GitHub link
rendered no icon, and below sm, with its label hidden, no accessible name either.

Solution. `icon` also takes a path to an SVG in the consumer's `public/`, rendered the
same way as the logo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. Changing theme is rare and low-stakes. It should cost one press.

Problem. The dropdown cost two, and made the reader open a menu and read three options
to make a choice that hardly matters. As a modal Radix menu it also died on iOS.

Solution. One button cycling System, Light, Dark, with the current state on its face.
Nothing to open, nothing to think through and select.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. The sidebar is the most-used surface here, and it should look like the design
system it documents.

Problem. It was styled from the Governance Studio board: a gradient selected row, a
shadow, a row height cap, none of which Equality ships. It read as a third language
beside the search palette and the theme menu.

Solution. Rows take Equality's dropdown item, the rail takes the Separator's colour,
and the current page holds the hover recipe, which search now shares.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. A reader scrolling a long page should see where they are.

Problem. The scroll-spy watched a narrow band, so nothing was marked at load or between
two headings. The rail was broken into dashes and its nesting had no connector.

Solution. The active heading is the last one above the reading line. The rail is
continuous, curves in and out of each nested level, and carries a lit segment that
travels along it as you scroll.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. Documentation is mostly body text, so block spacing is most of the page.

Problem. Blocks sat 8px apart while lines inside a paragraph sat 28px apart, so
consecutive paragraphs read as one. Headings sat the same distance above and below,
marking nothing.

Solution. One scale: 12 / 16 / 32 / 48 / 64. A heading binds tighter to the text it
owns than paragraphs bind to each other.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Need. A reader who finishes a page needs somewhere to go next, and a way to fix what
they just read.

Problem. The page stopped at its last line, 40px from the bottom. `footer.editUrl`,
`footer.showPrevNext` and `prevNextFor` were all built and none of them rendered.

Solution. Previous and next links, then a quiet row with "Edit this page". Space
separates it rather than a rule, and every page now ends with 128px of runway.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Shrinks99 and others added 3 commits September 21, 2026 14:44
main's docs work landed alongside the inline-Tailwind conversion. Resolutions:

- package.json: kept our 0.3.0 and check:source; took main's scripts/dev.mjs
  runner and build:dev.
- Header/Prose/DocsPage/docs.astro: took ours. main's changes here introduce
  GlobalSearch, the pagefind meta and `suggested`, all of which this branch
  already carries. Kept main's note on the breadcrumb trail's shape.
- Header.module.css: stays deleted; its rules are inline on Header.astro.
- GlobalSearch: took our converted files, then applied main's changes on top -
  the selected row narrows to a background tint, with no text recolour and no
  shadow, plus main's reworded comments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Shrinks99
Shrinks99 merged commit 16e68a6 into main Sep 21, 2026
1 check passed
@Shrinks99
Shrinks99 deleted the feat/design-alignment branch September 21, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants