feat(design-sync): publish rnui to Claude Design - #9
Merged
Conversation
Syncs the design system to claude.ai/design so its design agent builds with the real compiled @e412/rnui-react components. All 72 components verified against this repo's own Storybook render. - config.json: converter config (titleMap, docsMap, cardMode overrides, extraEntries, story skips). Paths are PKG_DIR-relative except readmeHeader, which resolves from the config home. - conventions.md: usage guide prepended to the generated README. Its key point is that Tailwind v4 has no runtime compiler in Claude Design, so only pre-compiled utilities resolve; it enumerates the real vocabulary. - previews/Slider.tsx: owned preview restoring Slider's card (its stories are unrenderable in Storybook — see NOTES.md). - NOTES.md: config rationale, triaged warnings, and re-sync risks. Also documents a repo bug: 5 stories in slider/progress use a percentage width under the global layout:'centered', so they collapse to 0px and are invisible in Storybook itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1
Claude Design reported "Missing brand fonts ... rendering with substitutes": the 8 presets name 10 web fonts, but @e412/rnui-themes deliberately doesn't load them, so every preset fell back to a system stack in rendered designs. Self-hosts them instead: fetch-fonts.mjs pulls the variable woff2 from Google Fonts (all 10 families are SIL OFL 1.1) into .design-sync/fonts/, and cfg.extraFonts makes the build copy them into ds-bundle/fonts/ and @import the sheet from styles.css — the closure designs receive. 20 faces (latin + latin-ext), 0.55 MB. Verified in chromium: 20 faces registered, 0 failed requests, all 10 families pass document.fonts.check(), and every preset resolves to its real body face (oxide/DM Sans ... crimson/Instrument Sans). extraFonts is outside the grade key, so no component grades were invalidated — the driver receipt stayed 72 carried forward, 0 cleared. conventions.md previously told the design agent these fonts were NOT bundled; corrected, with a per-theme typography table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1
Both story files size their root with a percentage width (`w-[60%]`)
while .storybook/preview.tsx applies a global `layout: 'centered'`.
A centered story root is shrink-to-fit, so the percentage resolved
against a 0px parent: the components mounted with full Base UI DOM but
measured 0px wide and rendered nothing. Base UI computed
`--start-position: NaN%` / `Infinity%` for the slider indicator.
Five stories were affected and invisible in Storybook:
slider (Default, Range) and progress (Default, Empty, Full).
Adds `parameters: { layout: 'padded' }` to both metas — the same thing
date-selector.stories.tsx already does, which is why its `w-full
max-w-xl` stories always rendered. A padded root spans the canvas, so
the percentage resolves normally.
Verified: all 6 stories across the two components now capture with 0
factual failures (previously 5 were sb-error) and grade match against
their previews.
Removes the sync workarounds these stories forced:
- overrides.{Slider,Progress}.skip — both sets capture now
- .design-sync/previews/Slider.tsx — the owned stopgap that stood in
for Slider's unphotographable card
- Progress keeps cardMode "single" (its grid-cell overflow is a card
layout concern, unrelated to the story bug)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1
rnui is normally consumed with Tailwind in the consumer's build, so any class compiles on demand. But it is also consumed precompiled — as one stylesheet embedded in a design tool — and there a class Tailwind never emitted silently does nothing. Ordinary layout markup (gap-10, grid-cols-4, max-w-4xl, text-3xl) hit this constantly. Adds: - src/safelist.css — complete scales, not just the values our own components use: spacing 0-24 on every box-model utility, text-xs..9xl, grid-cols-1..12, col-span-*, max-w-xs..7xl, radius/border/opacity, plus sm:/md:/lg:/xl: and hover:/focus:/active:/disabled:/dark: on the interactive subset. - src/utilities.css -> dist/utilities.css (~353 KB, 40 KB gzip), the precompiled layer for consumers who cannot run Tailwind. - dist/utilities.json — 4964 classes, derived from the COMPILED output rather than the safelist source, so tooling can report what actually shipped. - dist/tokens.json — 399 tokens across 9 themes with name/theme/kind/ value/light/dark. Built from hand-authored theme sources, never from compiled CSS, so Tailwind internals are excluded by construction. The build fails if any leak in. Tokens omitted from a dark ramp are flagged darkInherited instead of being presented as declared. apps/storybook imports the safelist because the design-sync bundle is scraped from that build; the safelist has to be in it, not only in the themes package. The safelist lives in themes rather than react because react already depends on themes — the reverse would be a workspace cycle. Themes therefore safelists only what it can compile alone; component classes still come from the Storybook scrape. Also prefixes the private custom properties --cell-size/--cell-radius (Calendar) and --data-grid-fill-size (DataGrid) with --rnui-_ so they are unambiguously not design tokens. --sidebar-width is left alone: it is set via a style prop on SidebarProvider, a supported consumer override. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1
The new exports had no documentation, and one existing doc was actively wrong after the change. - packages/themes/README.md: adds "Precompiled consumers" (when to use utilities vs. safelist vs. neither) and a machine-readable token export section, plus the two new entries in the Imports list. - apps/docs theming.mdx: adds "Reading Tokens Programmatically" — read tokens.json rather than scanning compiled CSS, since a CSS scan reports Tailwind plumbing as part of the palette — and "Using rnui Without a Tailwind Build". - README.md: the themes package summary now mentions both. Also corrects .design-sync/conventions.md, which is inlined into the design agent's prompt. It enumerated the OLD narrow vocabulary (gap stopped at 6, no grid-cols past 3, no responsive or state variants), so after the safelist landed it would have steered the agent away from classes that now work. Rewritten against the shipped bundle; every class named in it is verified present. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the sync state that publishes this design system to claude.ai/design, so its design agent builds UIs with the real compiled
@e412/rnui-reactcomponents instead of generic ones — plus a fix for five Storybook stories that were rendering invisible.Verification
All 72 components were checked against this repo's own Storybook render, screenshot vs screenshot: 246 stories graded, 246 match, 0 close, 0 mismatch. The closing receipt came back
ok: truewith nothing pending,validateexited clean, and the render check was 72/72 with 0 broken, 0 thin, 0 identical-variant cards.No component needed a hand-written preview to match — the generated ones were faithful on first capture across the whole library.
The Storybook fix (
apps/storybook, +8/−0)Both
slider.stories.tsxandprogress.stories.tsxsize their root with a percentage width (w-[60%]) while.storybook/preview.tsxapplies a globallayout: 'centered'. A centered story root is shrink-to-fit, so the percentage resolved against a 0px parent — the components mounted with full Base UI DOM but measured 0px wide and rendered nothing. Base UI computed--start-position: NaN%/Infinity%for the slider indicator.Five stories were invisible in Storybook: slider (Default, Range) and progress (Default, Empty, Full).
The fix is
parameters: { layout: 'padded' }on both metas — the same thingdate-selector.stories.tsxalready does, which is why itsw-full max-w-xlstories always rendered.Verified: all 6 stories across the two components now capture with 0 factual failures (previously 5 were
sb-error) and gradematch. This also let three sync workarounds be deleted — the twoskipentries and the ownedpreviews/Slider.tsxstopgap.What's in
.design-sync/config.jsontitleMap,docsMap, card-mode overrides,extraEntries,extraFontsconventions.mdfonts/+fetch-fonts.mjsNOTES.mdOther notable bits
The conventions file's key content is a constraint, not style advice. Tailwind v4 compiles at build time and there's no compiler in Claude Design, so only utilities already in the shipped CSS resolve — an invented class like
mt-8silently does nothing. The file enumerates the actual available vocabulary and directs the agent tovar(--token)inline styles for anything outside it. Every class, token, preset, and component name in it is validated against the built artifacts.Brand fonts are now self-hosted. The presets name 10 web fonts but
@e412/rnui-themesdeliberately doesn't load them, so every preset rendered with a substitute stack.fetch-fonts.mjspulls the variable woff2 from Google Fonts (all 10 families are SIL OFL 1.1) andcfg.extraFontswires them intostyles.css. Verified in chromium: 20 faces registered, 0 failed requests, every preset resolving to its real face.One echarts fix. EChart's gauge story rendered blank because
import { GaugeChart } from 'echarts/charts'was bundled as a second copy of echarts, whoseuse()overwrote the working registration with a foreign class. Fixed viaextraEntries. This was preview-only — the shipped bundle always had it registered correctly.One thing left for you
Only
oxideandforestdefine--font-heading. The other six presets brand the body font but inherit the system stack forh1–h6. Shipped faithfully rather than "fixed" — if that's unintended, the change belongs inpackages/themes.🤖 Generated with Claude Code
https://claude.ai/code/session_01NAmWWJ2twyebZgPLMc1cg1