Skip to content

fix(theme): make theme token references resolve (borders, prose colours, focus ring) - #27

Merged
JakeGinnivan merged 1 commit into
mainfrom
jakeginnivan/fix-theme-token-references
Oct 19, 2026
Merged

JakeGinnivan merged 1 commit into
mainfrom
jakeginnivan/fix-theme-token-references

Conversation

@JakeGinnivan

@JakeGinnivan JakeGinnivan commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Upstreamed from dddwa/dddperth#205.

What was broken

The theme builder used flat dotted keys ('border.default'). Panda emits those as --colors-border\.default, but every reference to them ({colors.border.default}, token(colors.border.subtle)) resolves to var(--colors-border-default), which was never defined. Those references silently computed to nothing:

  • border="default" | "subtle" | "emphasis", the sponsor border and the admin borders rendered as no border
  • every prose colour on MDX/blog pages fell back to the inherited colour
  • the bg.*, button.* and link.* aliases were empty
  • token(colors.border.subtle) dividers vanished

Utility props like bg="surface.card" kept working, because the class and the definition used the same escaped name. That's why it went unnoticed.

focus-ring was also broken separately: {colors.interactive.focus}33 becomes var(...)33, so the 33 is a stray number rather than a hex alpha, and the whole box-shadow was invalid.

The fix

  • themes/theme-builder.ts: a small nest() helper turns the dotted keys into nested groups, so Panda defines the hyphenated names. index.css and hero.tsx move off the hard-coded escaped name.
  • Both stub themes: focus-ring alpha via color-mix(... 20%, transparent)
  • _layout.voting.tsx, _layout.agenda.($year).tsx, _layout.agenda.my.tsx, _layout.feedback.tsx: remove colorPalette="brand.primary|secondary". It stops type-checking once brand is nested, and was always a no-op: the button recipe reads colorPalette.solid.*, which brand never had, so the buttons inherited gray and still do.
  • New app/theme/token-references.test.ts fails on any colour/border/shadow var the generated CSS references but never defines, and on any dot-escaped token name.

What was upstreamed vs left behind

  • Copied verbatim from the fork's post-merge state (upstream's copies were byte-identical to the fork's pre-change versions): theme builder, token test, theme-invariant test, index.css, hero.tsx, both stub themes, _layout.agenda.my.tsx, _layout.feedback.tsx
  • Prop lines removed by hand: _layout.agenda.($year).tsx and _layout.voting.tsx, which differ from the fork for unrelated reasons
  • Fork-owned, not copied: conference/theme/perth*.theme.ts (the stub themes are their counterpart) and the fork's Perth-branded baselines

Baselines

Regenerated against the stub on top of #26 (main's tree is identical to it) with pnpm vr --update-snapshots=all (several changes sit under the 2% tolerance). 54 of 63 changed; I reviewed before/after crops of each route:

  • home: the Important Dates row gains its 2px emphasis top border (+2px)
  • content page, blog post: prose links and anchor-linked headings take text.highlight (teal)
  • sponsors, agenda: sponsor tiles get their border.sponsor frame (slate in the stub, which differs from its page background)
  • talk detail: the talk dialog's title divider renders
  • blog index: unchanged

Testing

After this merges, dddperth's next /core-pull should apply cleanly, since its copy is identical.

🤖 Generated with Claude Code

@JakeGinnivan
JakeGinnivan force-pushed the jakeginnivan/fix-theme-token-references branch from 419589d to 6a58c17 Compare October 19, 2026 02:03
@JakeGinnivan
JakeGinnivan changed the base branch from main to jakeginnivan/upstream-fork-prs-158-202 October 19, 2026 02:04
@JakeGinnivan
JakeGinnivan force-pushed the jakeginnivan/upstream-fork-prs-158-202 branch 2 times, most recently from 2f11bf7 to ebe65ca Compare October 19, 2026 02:14
Base automatically changed from jakeginnivan/upstream-fork-prs-158-202 to main October 19, 2026 02:21
Upstreamed from dddwa/dddperth#205.

The theme builder used flat dotted keys ('border.default'). Panda emits
those as `--colors-border\.default`, but every reference to them
(`{colors.border.default}`, `token(colors.border.subtle)`) resolves to
`var(--colors-border-default)`, which was never defined. Those
references silently computed to nothing: the border tokens rendered as
no border, prose colours fell back to the inherited colour, the
`bg.*`/`button.*`/`link.*` aliases were empty, and `token()` dividers
vanished.

- Nest the keys so Panda defines the hyphenated names; move the two
  hard-coded escaped references (body background, hero fade) over.
- `focus-ring` was `{colors.interactive.focus}33`, which becomes
  `var(...)33`: an invalid box-shadow, so no ring showed. Use color-mix
  for the 20% alpha in both stub themes.
- Drop `colorPalette="brand.primary|secondary"` from the voting,
  agenda, my agenda and feedback buttons. It no longer type-checks once `brand` is nested, and it was
  always a no-op: the button recipe reads `colorPalette.solid.*`, which
  brand never had, so the buttons inherited gray and still do.
- New test fails on any referenced-but-undefined colour, border or
  shadow var, and on any dot-escaped token name.

Stub baselines regenerated with `pnpm vr --update-snapshots=all`: home
gains the Important Dates top border, prose links and anchor headings
take `text.highlight`, sponsor tiles get their `border.sponsor`
frame, and the talk dialog's title divider renders.
@JakeGinnivan
JakeGinnivan force-pushed the jakeginnivan/fix-theme-token-references branch from 6a58c17 to 9ef5430 Compare October 19, 2026 02:22
@JakeGinnivan
JakeGinnivan merged commit ae2d81b into main Oct 19, 2026
3 checks passed
@JakeGinnivan
JakeGinnivan deleted the jakeginnivan/fix-theme-token-references branch October 19, 2026 03:10
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