feat(studio): move every design value into one theme file - #3621
Draft
miguel-heygen wants to merge 1 commit into
Draft
feat(studio): move every design value into one theme file#3621miguel-heygen wants to merge 1 commit into
miguel-heygen wants to merge 1 commit into
Conversation
Studio's design values lived in three places: a JS Tailwind preset, an inline @theme block, and raw hex and pixel literals in markup and CSS. theme.css is now the single owner. Tailwind v4 turns it into both the utility classes and the CSS custom properties, so markup, plain CSS and canvas code all read the same number. - semantic colour palette (background levels, surfaces, borders, text levels, accent, selection, playhead, danger, media, container), radii, control heights, the micro type scale, font stacks, shadows, easings and motion durations - the stock Tailwind palette is cleared with --color-*: initial, so a colour that is not a Studio token cannot be reached from markup; the entries Studio's markup still names are re-declared as deprecated aliases and removed once the sweep converts them - motion durations ship as @Utility rules that zero themselves under prefers-reduced-motion, since Tailwind has no duration namespace - Phosphor icons get one default size and weight, read off the theme at the app root; icons that pass their own still win - published as @hyperframes/studio/theme.css; the JS preset export stays as a deprecated shim for v3 consumers, with a test that fails if the two drift apart
This was referenced Sep 4, 2026
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.
Lands unit U1 (theme file and semantic tokens) of the Studio design-system foundation. Stacked on the Tailwind v4 PR (#3618). No ratchet yet; the color literals this adds live in the token file, which the ratchet excludes.
What
Lands U1 of the Studio design-system stack: one theme file that owns every design value.
packages/studio/src/styles/theme.cssis new and holds the semantic colour palette (background levels, surfaces, input, hover, borders, hairline, text levels 0 to 5, accent, selection, playhead, danger, media, container), radii, control heights, the micro type scale, the two font stacks, menu and popover shadows, easings, motion durations, and the icon size and weight.--color-*: initial. The stock colours Studio's markup still names are re-declared in a deprecated block; thestudio.*andpanel.*names stay as aliases that point at the semantic tokens. Both blocks are removed by the sweep unit that converts the last usage.@utilityrules (duration-pressand siblings) because Tailwind has no duration namespace. Each zeroes itself underprefers-reduced-motion, so a caller cannot forget the reduced-motion case.@hyperframes/studio/theme.css. The JS preset export stays as a deprecated shim for Tailwind v3 consumers.Stacked on the Tailwind v4 branch; review that one first.
Why
Studio's design values lived in three places at once: a JS Tailwind preset, an inline
@themeblock, and raw hex and pixel literals in markup and CSS. Two colour scales overlapped, the same grey was spelled four ways, and a value read from CSS could disagree with the same value read from a class. Tailwind v4 emits every theme entry as both a utility and a real CSS custom property, so a single file can serve markup, plain CSS and canvas code without a second copy.How
@theme staticrather than plain@theme: the default behaviour drops theme variables no utility class happens to reference, and several of these tokens are read throughvar()from stylesheets and from JavaScript rather than through a class.--text-step-8through--text-step-18because--text-11would collide with Tailwind's own size scale.text-xs,text-smandtext-lgkeep their Tailwind values, so the sweep can be a 1:1 replacement of thetext-[11px]style literals.rounded-mdis unchanged today and the theme file becomes the place to change it tomorrow.var()cannot be handed to the icon library.readIconTokensresolves them once at the app root, which keeps the token file the only owner instead of pairing a CSS variable with a JavaScript constant.studio.csscollapse to--font-sansand--font-mono.--font-monotakes the code editor's stack, so the timeline timecode and the loader detail line now prefer the same monospace family as the editor.--color-selectionand--color-playheadare reserved and differ from the accent (the playhead and a selected clip both draw in the accent colour today), and--shadow-menuand--shadow-popoverare sized for a near-black canvas rather than Tailwind's light-mode drop shadows. Nothing reads them yet.Test plan
theme.test.tscompiles Studio's real entry stylesheet with Tailwind and asserts: the semantic palette is emitted both as custom properties and as utilities; an alpha modifier on a plain hex resolves throughcolor-mix; a stock colour outside the deprecated block no longer compiles; selection, playhead and accent are three distinct values; every micro type step exists and the Tailwind sizes are untouched; a duration utility carries its reduced-motion variant; every deprecated stock-palette entry still equals its upstream Tailwind value; the JS preset shim still agrees with the CSS; and an icon with no props of its own inherits the token size and weight. Each assertion was checked against a deliberately broken theme file first.typecheck,build,oxlint,oxfmt --checkand the fallow audit are clean.components/ui/Button.tsxthat previously did not resolve and now do (border-border,enabled:hover:border-border-strong,duration-press). That component was written against a token vocabulary that did not exist yet; its remaining unresolved classes (shadow-btn-primary,bg-surface-hover,bg-accent-red,rounded-button,ease-standard) are left for the unit that rebuilds it. Its border therefore moves from the inherited text colour to the border token, which is the one intended rendering change in this PR.Not covered
components/ui/Button.tsxis not fixed here beyond the classes that now resolve on their own.