Skip to content

fix(site): render docs prose through the shared type module - #167

Open
tnunamak wants to merge 23 commits into
mainfrom
fix/site-ui-consistency-0818
Open

fix(site): render docs prose through the shared type module#167
tnunamak wants to merge 23 commits into
mainfrom
fix/site-ui-consistency-0818

Conversation

@tnunamak

@tnunamak tnunamak commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The site had two typography systems, and they had drifted apart.

Marketing pages sized their text through Text, a React module that maps a size token name such as lede onto one step in the site's type scale — a fixed list of font-size/line-height pairs, each pair called a rung below. The specification pages sized the same elements through hand-written CSS aimed at bare h1/h2/p selectors. Neither knew about the other, so an h3 was 17.9px on docs and something else on marketing, and every future change had to be made twice or drift again.

The duplication also caused a live bug. The two systems compete through the browser's cascade — the mechanism that resolves conflicting CSS rules, normally by picking whichever loaded last — so which rule won depended on stylesheet load order. A duplicate .flex rule beat a responsive rule shipped by fumadocs, the framework that renders the Markdown for these pages, and the table of contents never collapsed on narrow screens.

This branch routes documentation prose through the same Text module and deletes the CSS that duplicated it. Documentation keeps its own content source — Markdown rendered by fumadocs — but no longer its own type system.

Prose on /specification is deliberately larger: body 17px → 19px, h3 17.9px → 20px, h2 28.8px → 27px, now the same rungs as the rest of the site. Every value was measured in a browser before and after, not read off the stylesheet.

Four smaller pieces of the same problem came with it. The component tree was flattened — everything lived under components/pdpp-concept/, a folder named after a prototype route that no longer exists, and now sits under layout/, sections/, elements/, typography/. Two components that each carried their own copy-to-clipboard state machine now share one. Boolean props that switched a component between two rendering modes were replaced by one named module per mode, or deleted where nothing called them. And /self-host/coverage, the last page on the old CSS classes, moved onto Text — fixing a quiet bug where .pdpp-caption, the class styling that page's small table labels, was defined twice under the same name and the wrong one always won, rendering table headers in italic serif for no reason.

The duplicate-CSS bug got a real fix rather than a better patch. This site runs Tailwind — a CSS framework that generates classes like .flex on demand from source code — twice: once for the whole site, once more for pages needing extra CSS variables the main build can't see. Tailwind scans the whole source tree by default, so each build scanned pages the other exclusively owns and generated the same classes twice. Scoping each build's scan with @source not — a directive telling one build to ignore files it doesn't serve — cut overlapping selectors from 680 to 4, without merging the two builds. Two earlier attempts tried merging the builds instead and both broke --container-page, the variable that sets the site's maximum content width: a page came out a different width depending on which merged build produced its width rule. The override lived in specification.css, the stylesheet holding the spec pages' own rules — the same file the duplicated type CSS came out of — and is now deleted outright.

Review notes

  • The typeface trap. Text deliberately doesn't set font-family; it inherits whatever font its parent wrapper already declares (the marketing pages' outer wrapper, for example, sets a sans-serif default). An earlier attempt at this migration silently switched headings from serif to sans because of it. Here the ambient default already matches what the old CSS hardcoded, verified by measurement, but a different parent wrapper would change that.
  • <strong> keeps an explicit weight. The shared module styles <strong> at 500 (medium weight), but the specification uses it as inline mini-headers where that's nearly invisible against body text. A scoped 700 (bold) rule remains. No marketing page uses <strong>, so there was no second place to check a shared weight change against.
  • The deferred rename is done. pdpp-concept — the retired prototype name — survived in data-slot attributes and --pdpp-concept-* CSS variables across 295 places and 27 distinct names, all now pdpp-editorial, matching the term the brand README already used for this surface. Verified with the same computed-style differ described above, run both as a self-control (baseline served against itself, to separate real diffs from two known-flaky pages) and as the real before/after comparison: zero differences on every route beyond the disclosed flakes. The built CSS was also grepped directly for the old name: zero matches.
  • .flex still appears once per build. Not leftover overlap — docs-only and marketing-only elements each use it independently, so removing it from either build would mean merging both.
  • Verification. This branch adds apps/site/scripts/style-differ.mjs, which opens this branch's build and main's build side by side in a real browser and compares each element's computed style — the final font-size, colour, and position after the cascade resolves, not what the source CSS alone suggests. It checks six representative pages at two widths (desktop and mobile). Marketing routes show zero differences; the specification route's differences are the intended type changes plus the rows that shift beneath them as taller text pushes content down. The table of contents sidebar switches from hidden to visible at exactly 1280px; both sides of that line (1279px and 1281px) were measured directly and matched before and after.
  • What I have not verified. Only the light colour scheme was measured. The Text rungs are shared, so a dark-mode regression is unlikely but untested.

Assisted-by: AI

The self-host command panel and the terminal block each kept an
independent copy-to-clipboard state machine: the same copied/failed
state, the same try/catch around navigator.clipboard.writeText, and
the same 2s auto-reset, but only one of the two had grown an
aria-live announcement. A third copy button would have had to choose
which copy to clone.

Extract useCopyToClipboard (src/lib/use-copy-to-clipboard.ts,
alongside the existing use-protocol.ts) and a pure copyStatusText
helper for the idle/copied/failed label and announcement text, then
point both components at it. Behaviour is unchanged — verified with
before/after screenshots at 1440x900 and 390x844 across /,
/specification, /self-host, /participate, and /maintainers: 8 of 10
captures are byte-identical, the other 2 (the home page, which
imports neither component) differ only in a decorative background
element's animation frame.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The self-host coverage matrix page hand-wrote pdpp-eyebrow, pdpp-display,
pdpp-body-lg, and pdpp-title class strings for its headings and stat
labels. Every other page on the concept surface (/, /self-host,
/participate, /maintainers) already renders type through the shared
Text component, per the migration this repo's own style doc
(docs/design-system/styling-in-apps.md) already commits to: this page
was the one call site left behind, not a new pattern.

Text owns the same rungs these classes name (eyebrow, display, lede,
body) with the surface's rebound values, so swapping in Text doesn't
change the rendered type, it removes the second way of asking for it.

pdpp-caption is left as-is here: on this surface it resolves to a
15px italic serif that has no matching rung in Text's ladder (the
closest rungs are 14px sans or 15px/1.6 sans), so mapping it would
require adding a new token or accepting a visible size change -- a
call for whoever owns the type ladder, not a mechanical rename.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
Self-host's "What you get" list and maintainers' doc lists both wanted
the same shape -- a ruled list of title-then-description rows -- and
got there two different ways. Self-host hand-wrote a .pdpp-features
BEM class in components.css; maintainers reused that same class from
a different file with a comment explaining the reuse, because there
was no component to import instead.

Both call sites now import PdppRuledList / PdppRuledListItem
(apps/site/src/components/pdpp-concept/ruled-list.tsx), a small
component that owns the border-top-then-ruled-until-last-item layout
in Tailwind. The .pdpp-features CSS block is deleted -- nothing
references it anymore -- along with its `strong` selector, which no
call site had emitted since before this change (dead rule). This is
the same move components.css's own header already commits to for
every widget in its REMAINING BEM list: componentize before deleting
the BEM, not after.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The concept surface had two ways to hide text visually while keeping
it for screen readers: Tailwind's built-in sr-only utility (already
used in color-scheme-menu.tsx) and a hand-rolled .pdpp-visually-hidden
BEM class in components.css that did the identical job with a
slightly older technique (clip-path inset instead of the modern
clip-rect-free approach, but same visual/AT result).

Every .pdpp-visually-hidden call site (two form legends and a live
region in command-tabs.tsx, one link label on the self-host
implementations table) now uses sr-only. The BEM rule is deleted --
it has no remaining call sites.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
This reverts commit 680a87b.

The revert was proven necessary, not just cautious: reading the CSS
values in text made pdpp-eyebrow, pdpp-display, and pdpp-body-lg look
like clean 1:1 matches for Text's eyebrow/display/lede sizes, but a
computed-style check in an actual browser (comparing this branch's
build against origin/main's, both served locally) showed otherwise.

Two concrete regressions, both invisible from reading the stylesheet
alone:
- pdpp-display and pdpp-body-lg hardcode font-family: var(--font-sans)
  in packages/pdpp-brand/styles/typography.css. Text's display/lede
  sizes set no family, so on this page they fell back to the concept
  surface's serif default -- the coverage page's <h1> and lede
  paragraph rendered in serif instead of the intended sans, and the
  lede paragraph's line-wrap point shifted because serif and sans
  characters aren't the same width.
- pdpp-eyebrow on the concept surface is shadowed by a second,
  unlayered .pdpp-eyebrow rule in components.css (13px, color:
  --pdpp-concept-ink-faint) that wins the cascade over the brand
  compat class. Text's eyebrow size uses the brand-react CVA voice
  (11px, color from the `color` prop) instead, so the converted
  eyebrows rendered smaller and in the wrong shade of muted --
  color="subtle" (-> text-foreground-faint) would have matched, not
  color="muted", and that wasn't visible from the CSS values alone
  either.

Fixing this properly needs either an explicit family="sans" per call
site plus swapping color="muted" for color="subtle" on the eyebrows,
or adding those as defaults somewhere in the type system -- a design
decision, not a same-PR mechanical fix under this task's time budget.
Reverting rather than shipping a page with a silently swapped
typeface and a mismatched label color.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
There is no browser test harness in this repo and zero tests render a
public page, so a composition refactor that looks behavior-preserving
in JSX can still change what a browser paints. A prior agent reverted
correct work for want of exactly this signal.

scripts/style-differ.mjs points at two already-running servers
(baseline vs candidate), visits a fixed route list at two viewports,
and for a set of stable data-slot/structural selectors captures
bounding boxes plus resolved computed styles (font-family, font-size,
line-height, color, display, margin, padding). It diffs candidate
against baseline and reports every element whose box or style values
changed, so a later composition change can be checked against real
computed truth instead of asserted from the JSX.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
PdppConceptPage took a home?: boolean that switched vertical-centering
and doc padding for the front door (/) and 404 — a direct instance of
architecture-avoid-boolean-props (CRITICAL): the interface couldn't
say what a call renders without reading the boolean's value at the
call site.

Split into an explicit variant per patterns-explicit-variants instead
of a string-union prop: the two treatments differ on more than one
axis (an extra centering rule plus a padding override reaching into
the child), so folding them back into one module's interface would
just rename the boolean, not remove it. PdppConceptPage now always
renders the standard chrome; PdppConceptFrontPage is its own module
for the two short-page callers. Both callers (app/(concept)/page.tsx,
not-found.tsx) never passed a rail, so the variant only needs to
compose PdppConceptDoc, matching how they use it today.

Verified with scripts/style-differ.mjs against the prior commit
(home-boolean HEAD) across all 6 public routes plus /not-found at
1440x900 and 390x844: 0 diffs.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
PdppCommandBuilder took a compact?: boolean that switched a
pdpp-cmd--compact BEM modifier — a second instance of
architecture-avoid-boolean-props (CRITICAL). Unlike the home boolean
on PdppConceptPage (previous commit), this one had no live caller to
turn into a variant: PdppCommandBuilder has exactly one call site
(self-host/page.tsx) and it never passes compact. The CSS rule's own
comment described "the landing-page instance," but the front door
(app/(concept)/page.tsx) never renders PdppCommandBuilder — the
modifier was authored for a composition that was never wired up.

Removed the parameter, the conditional class, and the orphaned
.pdpp-cmd--compact CSS rule together, so the module's interface no
longer offers a mode nothing calls.

Verified with scripts/style-differ.mjs against the pre-S1 commit
across all 6 public routes at 1440x900 and 390x844: 0 diffs (one rerun
showed a single /specification TOC scroll-spy color diff that also
reproduces 1 run in 4 between two builds of the SAME commit — a
fumadocs intersection-observer timing artifact unrelated to this
change, not a regression).

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
pdpp-concept/ held 20 modules flat with no taxonomy — his measured
taxonomy from the odl-website cleanup (layout/, sections/, elements/,
page-<route>/) was entirely absent here.

Classified each module by role and moved it, one taxonomy per commit
so a bisect lands cleanly:
  layout/   page-frame chrome, one instance per document:
            concept-page.tsx (PdppConceptPage/PdppConceptFrontPage/
            PdppConceptDoc), concept-shell.tsx, masthead.tsx,
            footer.tsx, rail.tsx (the rail SLOT/frame).
  sections/ named, composed content shapes that sit inside a page
            body: concept-section.tsx (the SectionHeading analogue —
            already the deep module his taxonomy is built around),
            concept-doc-header.tsx, front-door.tsx, hero-water-still/
            -data (front-door's content), ruled-list.tsx, command-
            tabs.tsx, and the rail's own internal content (rail-toc,
            rail-front-matter, rail-section-label — distinct from the
            rail FRAME in layout/).
  elements/ small reusable leaf pieces used across many contexts:
            button.tsx, terminal.tsx, icons.tsx, theme-switch.tsx,
            color-scheme-menu.tsx.
  (root)    text.tsx/text-variants.ts, site-facts.ts, spec-status.ts,
            generated-color-scheme-names.ts stay at pdpp-concept/ root
            — data/constants and the typography facade, not
            components; each is a single file, so a dedicated folder
            (his typography/ precedent) would be one file deep for
            no navigational win at this scale (20 files here vs 104
            in his source cleanup).

Mechanical move: every absolute (@/components/pdpp-concept/...) and
relative (./...) import updated across the site to the new paths,
plus three scripts/*.test.ts that read source files by hardcoded path
(self-host-browser-capability, site-surface-ownership, pdpp-concept-
facts) rather than importing them.

Verified with scripts/style-differ.mjs against the pre-S1 commit
across all 6 public routes at 1440x900 and 390x844: 0 diffs — a pure
file move, no rendered output changed.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
specification.css carried its own raw-element type rules (h1-h4, p, li,
strong) alongside the marketing routes' Text ladder — two type systems
for one job. mdx.tsx now maps every prose element it renders onto Text's
size rungs (h1->display, h2->title, h3->heading, h4->lede/semi, p/li/
blockquote->lede), so prose gets a different source (MDX) rather than a
parallel type interface.

h1-h4 route through fumadocs' own Heading (via defaultMdxComponents),
not Text directly: Heading owns the #anchor-link + copy-button depth for
any heading with an id, and swapping it for Text silently drops that
affordance behind a flex->block layout change (caught with the
computed-style differ, not by reading the diff). Only Text's className
composes on top.

strong keeps an explicit 700-weight rule in specification.css rather
than falling through to Text's built-in font-medium (500): measured in
a browser, medium reads as barely distinguishable from body weight at
this surface's serif lede rung, a real legibility loss against the
spec's functional use of <strong> as inline mini-headers. No shipped
marketing route currently renders a <strong> to validate the shared
default against, so this stays a disclosed, scoped exception rather
than a change to Text's shared base class.

Deleted the now-dead font-size/line-height/font-family declarations for
h1-h4/p (~19 lines); kept every docs-chrome rule (rail, TOC, tables,
Shiki code blocks) specification.css owns that marketing has no
equivalent for.

Verified with apps/site/scripts/style-differ.mjs against an
origin/main baseline: 0 diffs on every marketing route and /, a
self-control run (baseline against itself) reproduces the known
oklab/oklch TOC flake and nothing else, and the real diff isolates
cleanly to /specification's measured type-rung deltas plus their
downstream layout (row positions below the fold).

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pdpp Ready Ready Preview Aug 18, 2026 9:56pm

Request Review

…xonomy

The public site kept every concept-surface component nested under
components/pdpp-concept/{layout,sections,elements}/, a leftover from
when the site was a single prototype surface. The reference structure
this pass targets keeps layout/, sections/, and elements/ at the top
of components/ with no project-name wrapper.

Move layout/, sections/, and elements/ up to components/ directly.
Move text.tsx and text-variants.ts into a new components/typography/
dir. Move the non-visual data modules (site-facts.ts, spec-status.ts)
to lib/, alongside the sibling spec-front-matter.ts they already
depend on, and the generated color-scheme-names file to generated/,
next to the repo's other "do not edit directly" generated output.
Update every import, including three test scripts outside src/ that
read component source by path.

data-slot values and --pdpp-concept-* CSS custom properties are left
as-is: those are load-bearing selectors used across ~100 CSS rules,
not the stale directory name, and renaming them is a separate,
higher-risk change.

Two references to the old components/pdpp-concept path remain outside
apps/site (docs/design-system/styling-in-apps.md and
scripts/test-accounting/authority-prepare.test.ts at the repo root)
and are out of scope for this change.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
No file declared `@layer name1, name2, ...;` anywhere, so the layer
priority Tailwind's own utilities depend on (properties < theme <
base < components < utilities) was only an accident of which
stylesheet happened to load first. Declare it explicitly in site.css,
the one stylesheet every route loads.

Investigated removing the second, duplicate Tailwind utilities build
that the public concept surface runs alongside the root build (the
actual cause of a real bug: fumadocs' `max-xl:hidden` on #nd-toc lost
to the concept build's own unconditional `.flex`, both same layer,
same specificity, only load order deciding). Two different fixes for
that got as far as a green build each, and both introduced real
regressions verified with the computed-style differ before being
reverted:

- Folding the concept surface's `@theme` additions into site.css's one
  build, so only one utilities layer would ever exist, deleted
  `max-w-page` and every concept spacing utility site-wide and changed
  what other shared utility classes resolved to.
- Giving the concept build's utilities import its own, lower-priority
  layer name fixed the #nd-toc collision specifically, but broke
  `.container`'s breakpoint margins on every route, because the
  concept build also legitimately depends on some of its later-loaded
  rules beating the shared `utilities` layer elsewhere.

Both routes also require rewriting scripts/site-surface-ownership.test.ts's
"concept entrypoint owns Tailwind utility generation" contract, which
exists specifically to keep the two builds architecturally separate.

The #nd-toc override in specification.css therefore stays. It is not
racing the bug it works around: this file is unlayered plain CSS,
which the CSS layers spec always ranks above every named layer
regardless of load order, so it is a legitimate, minimal fix for a
collision between two independently-built Tailwind layers rather than
the workaround-on-a-workaround it looked like. Its comment is expanded
to record what was actually tried, so a future pass does not re-spend
the same investigation.

Verified with apps/site/scripts/style-differ.mjs: 0 unexpected diffs
on every route including /specification (which continues to carry
only the pre-existing, already-disclosed type-rung diff from an
earlier commit on this branch), and #nd-toc measured at both 1279px
(display:none) and 1281px (display:flex, real bounding box) before
and after.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…xt module

/self-host/coverage was the one public concept route with its own
container (max-w-7xl, same numeric width as the shared max-w-page, so
this is not a layout change) and its own type classes (.pdpp-eyebrow,
.pdpp-display, .pdpp-body-lg, .pdpp-caption, .pdpp-title) instead of
PdppConceptPage/PdppConceptDoc and the Text component every other
public route uses.

Two of those classes were quietly broken. .pdpp-caption is defined
twice: once in the shared brand package (small rung, sans) and once,
unlayered, in this surface's own components.css (15px italic serif,
no matching Text rung). Unlayered CSS always outranks a Tailwind
layer regardless of source order, so the concept-local definition won
everywhere on this page — table headers, category pills, status
pills, and evidence links all rendered in italic serif, though nothing
about a table header or a status pill called for italics. .pdpp-title
is `--text-body` (14px) restyled at 600 weight — a legacy compat
class, not a Text size — while Text's own `title` rung is a 23-27px
fluid clamp; same name, unrelated metrics, confirmed by reading both
definitions and by rendering the page.

Replaced every .pdpp-* class with Text and the size rung it actually
maps to (eyebrow, display, lede, small, body), keeping family="sans"
where the dashboard's existing sans-serif voice was intentional and
letting editorial copy (the H1, the intro paragraph) take the
surface's serif default like every other concept page. Did not add a
new rung: .pdpp-caption's italic treatment survives as a caller
className on Text size="small", not as ladder growth, because
"italic caption" is explicitly documented as packaging, not a rung,
in brand-react's own text-variants.ts. Left the ui/button.tsx action
buttons and the table/pill markup as-is — the task was the type module
and page frame, not the button system.

Net effect on this route, measured with style-differ.mjs: eyebrow
13px->11px sans (now matches the shared rung it always should have),
H1/intro copy sans->serif (matches every other concept H1/body copy),
row titles unchanged (15px Inter 600, same metrics, now the body rung
instead of a same-named-but-different local class), and table headers/
notes/pills/evidence links unchanged in size but corrected from
accidental italic serif to their intended sans voice. Every other
public route stays at 0 diffs; only /specification carries its
pre-existing, already-disclosed diff from an earlier commit on this
branch (plus one occurrence of the known #nd-toc oklab/oklch flake).

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The elements/layout/sections/typography split stopped short: mdx.tsx,
hero.tsx, site-header.tsx, pdpp-logo.tsx, longview-wordmark.tsx,
reference-app.tsx, and reference-hero-proof.tsx were still loose at
src/components/ root, and the pdpp/, site/, theme/, and docs/
directories duplicated the taxonomy's job instead of using it.

Sort each file by role: small reusable leaves (pdpp-logo,
longview-wordmark, theme-toggle, source-link, artifact-link) go to
elements/; page-frame chrome (site-header, prose-page) goes to
layout/; named composed content (hero, reference-app,
reference-hero-proof, and the five pdpp/* surface modules) goes to
sections/; mdx.tsx joins the existing mdx/ directory next to
mermaid.tsx. theme-state.ts holds no view logic, so it moves to
src/lib/ with its colocated test; site-providers.tsx gets its own
providers/ directory, matching the reference layout. ui/ is untouched.

Every import is repointed, including three test scripts that read
component source by hardcoded file path rather than by import
(reference-app-motion-reduced.test.ts, site-surface-ownership.test.ts,
pdpp-concept-facts.test.ts) and one regex in theme-runtime.test.ts
that would have silently stopped checking its invariant once the
literal path it matched moved.

biome.jsonc's noBarrelFile waiver is repointed from the old
components/{pdpp,theme}/* globs to the shims' new explicit paths, so
the eight one-line re-export shims keep their waiver without also
exempting the ordinary view modules that now share their directories.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The root stylesheet (site.css) and the concept surface's own stylesheet
(surfaces/concept/index.css) are two independent Tailwind compilations,
kept separate on purpose (surfaces/concept/index.css exists so
concept-exclusive @theme keys like --container-page resolve at all;
scripts/site-surface-ownership.test.ts pins the split). Two earlier
attempts tried to merge the two builds into one and each broke something
real (documented in the removed comment on specification.css). Neither
touched what each build actually SCANS.

Both builds previously auto-detected sources from the whole apps/site
tree, so a component used by only one surface still got scanned by the
other build too, and any ordinary class it used (`flex`, `grid`, ...)
was regenerated a second time for no reason. @source not now excludes
each build from the files only the other surface renders, cutting
duplicate selector overlap between the two builds' output from 680 to 4
(measured on the built CSS reaching /specification). The 4 that remain
are genuinely shared files (components/ui/button.tsx,
elements/theme-toggle.tsx, providers/site-providers.tsx, and
components/ui/tooltip.tsx via the root layout) that legitimately render
on both surfaces and must stay scanned by both.

Scoping concept's scan also removed an ACCIDENTAL source of classes
fumadocs' own compiled UI needs (md:hidden, lg:flex, and friends):
concept's old unscoped build picked those up only because some
unrelated root-only file (design/page.tsx, a kitchen-sink style-guide
page) happened to also contain the same literal class string. Losing
that accidental supply silently broke #nd-subnav's responsive
display:none. Fixed by giving concept's build an explicit @source over
fumadocs-ui's compiled dist, the same way site.css already @sources
operator-ui and brand-react.

That fix also resolves the #nd-toc bug the specification.css workaround
existed for: fumadocs ships #nd-toc with both an unconditional `flex`
and a conditional `max-xl:hidden`. Previously only site.css's build had
`max-xl:hidden` (by the same accident), so concept's later-loading,
unconditional `.flex` always won the cross-stylesheet <link>-order race.
With the fumadocs @source, `max-xl:hidden` lives in the SAME stylesheet
as concept's own `.flex`, in Tailwind's own variant-after-base order, so
it wins on source order within one file rather than racing a second
stylesheet. The plain-CSS override this used to require is removed;
#nd-toc's computed display was measured at 1279px (none) and 1281px
(flex) before and after, on this commit and on main, with matching
results, and scripts/style-differ.mjs (baseline main vs. this commit)
shows zero diffs across every route/viewport pair.

Duplication is not fully eliminated: `.flex{display:flex}` still
appears once per build, because concept-exclusive components (masthead,
rail, footer, ...) and root-exclusive components independently use
`flex` in their own right. That is not scan overlap — it is two
surfaces each needing an ordinary utility on their own account — and no
amount of scan-scoping removes it without re-merging the builds, which
is the thing this change was asked not to do.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
showThemeToggle had zero call sites across the codebase — every caller
of SiteHeader (reference-app, sandbox shell, /design) took the default.
A boolean with no variance isn't a mode switch, it's dead weight, so
the fix is deletion rather than a variant module: render ThemeToggle
unconditionally.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
wide was a live boolean prop selecting between two layout modes on the
reference-app section shell: narrow (text stacked above content, capped
reading width) and two-column (text left, content right, wider cap). It
had four real call sites (inventory, request, revoke, export), so this
is a variant split, not a deletion.

The shared depth (border color from surface, headline/narrative copy,
detail panel, Reveal wiring) now lives in an internal SectionShell. Section
and TwoColumnSection are its two seams: each owns only its layout-specific
classes and exposes no boolean, so a call site's choice of component states
its rendering mode directly instead of through a flag. Also corrected three
stale "wide layout" comments left over from an earlier draft that no longer
matched the code they described.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…modules

trimDocumentTitle negated whether ProsePage stripped a leading `# Title`
line before rendering. Its one live caller (/design, a noindex internal
page) passed trimDocumentTitle={false} — a negated flag naming what it
does NOT want rather than what it does.

The rendering interface is now two seams sharing one depth
(ProsePageShell, unexported): ProsePage strips a redundant leading title
(the shape used when a doc is pulled into a page that already renders its
own heading); VerbatimProsePage renders markdown exactly as authored (the
shape used for a self-contained excerpt). The caller states its markdown's
shape directly. Behavior is unchanged — the /design example never had a
leading `# ` line, so trimDocumentTitle was already a no-op there.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
`overflow-x: auto` on the tab row makes it a scroll container on both axes,
not just the one named. The tab labels measure 47px inside a 46px row, so
that single pixel was enough for the browser to paint a vertical scrollbar
beside two tabs that have nothing to scroll.

Clip the block axis instead. Horizontal scrolling still works if the tabs
ever outgrow the row, which is what the rule was there for.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…itorial-*

pdpp-concept named a prototype route that no longer exists; the component
directory carrying that name was already dissolved earlier on this branch,
but the identifier survived in the design-token layer. "Editorial" is the
term the codebase already uses for this family (brand README, components.css
header, semantic.css comments) and predicts what the tokens style: the
site's marketing/documentation surface, distinct from brand product tokens
and the internal /design, /sandbox, /palette tooling surfaces.

Renames the runtime palette declarations (primitive.css, primitive.legacy.css,
compat-palette.css, semantic.css), the generated scheme file and its
generator (generate-concept-colors.mts, design-book/concept-colors.mts), and
the data-pdpp-concept-scheme attribute those schemes gate on. No value
changes — same colours, same cascade, new name.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…pp-editorial-*

Continues the pdpp-concept -> pdpp-editorial rename onto the second half of
the family: the ten data-slot values the concept-surface components emit
(page, doc, doc-header, masthead, footer, section, text, button, rail,
rail-toc), the Tailwind data-slot selectors and CSS attribute selectors that
target them, the color-scheme menu's dataset write (was writing
pdppConceptScheme while the CSS already expected the renamed attribute after
the previous commit), and the differ script's KEY_SELECTORS list used for
zero-diff verification.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
styling-in-apps.md and the brand README describe the pdpp-concept token
family and BEM class by name; update both to pdpp-editorial to match the
rename. Also fixes five links/paths left behind by the earlier
components/pdpp-concept/ directory dissolution on this branch (files moved
into layout/, sections/, and typography/ but the doc still pointed at the
old directory), and a stale path in a brand-react test comment pointing at
the same pre-move location.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…c-status.ts's real path

ALIAS_CONSUMER hardcoded apps/site/src/components/pdpp-concept/spec-status.ts,
a path that stopped existing when an earlier commit on this branch moved the
file to apps/site/src/lib/spec-status.ts as part of dissolving the
components/pdpp-concept/ directory. The test was failing: the first case's
post-prepare import assertion saw a real Cannot-find-module error from the
missing consumer file itself, not the module-resolution success the test
means to prove. Confirmed 1 of 3 sub-tests failing before this change and
3 of 3 passing after, against the same real prepare command and real
spawned-child import.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.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

Development

Successfully merging this pull request may close these issues.

1 participant