Repository navigation
Agentic UI: Close the CSS design-system gaps with tokens, shared recipes, surface stepping, scoped dark mode and a token lint - #5390
Merged
Conversation
…er tokens, and use them instead of literals Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-css-design-tokens
… global svg rule Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lors to their scene Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pe-css-surfaces-and-schemes
…re-css-recipes # Conflicts: # apps/ui/README.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-css-design-tokens # Conflicts: # apps/ui/src/features/chats/session-view/conversation/style.module.css # apps/ui/src/features/composer/style.module.css # apps/ui/src/features/sidebar/site-list/style.module.css # apps/ui/src/features/sites/site-preview/style.module.css
…re-css-recipes # Conflicts: # apps/ui/src/features/chats/session-view/conversation/style.module.css # apps/ui/src/features/composer/style.module.css # apps/ui/src/features/sites/site-preview/style.module.css
…pe-css-surfaces-and-schemes # Conflicts: # apps/ui/src/features/chats/session-view/conversation/style.module.css
… for colors, type, radii, shadows and layers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/ui/src/features/composer/attachment-chips/style.module.css # apps/ui/src/features/composer/style.module.css # apps/ui/src/features/notifications/notice-history/style.module.css # apps/ui/src/features/sidebar/sidebar-header/style.module.css # apps/ui/src/features/sidebar/site-list/style.module.css
# Conflicts: # apps/ui/src/features/chats/session-view/conversation/agent-question/style.module.css # apps/ui/src/lib/theme/tokens.css # apps/ui/src/lib/ui/dialog/style.module.css # apps/ui/src/lib/ui/input-group/style.module.css # apps/ui/src/lib/ui/popover/style.module.css # apps/ui/src/lib/ui/switch/style.module.css
…ions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
📊 Performance Test ResultsComparing 8119f42 vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
…-design-system
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 task
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.
Related issues
How AI was used in this PR
Claude Code made the changes. It used dev-time postcss codemods (not committed) to swap literals for tokens and repeated declarations for shared recipes without touching the formatting. A computed-style harness then checked every rendered element against trunk across 39 scenes, in Chromium and WebKit, light and dark, plus motion passes. A subagent matched each off-scale radius to the same element in the rebuild. I reviewed the token names, the snapped radii, the recorded exceptions and the lint rule.
Proposed Changes
The agentic UI's stylesheets now draw on a complete token set and share their recurring recipes. A lint rule keeps them that way. The UI looks the same apart from a handful of radii and weights that now match the rebuild (listed below). The CSS shrinks by about 90 lines.
Tokens (
lib/theme/tokens.css), used in place of ~800 literals:--space-*, a 4px grid numbered like Tailwind's scale (--space-1_5is 6px). It covers margins, padding, gaps and insets, including negatives and values insidecalc().--text-*--line-heightfor each text size (line-height: 20pxwas hand-written 41 times), plus--leading-*for the unitless ratios.--text-2xs(11px),--text-md(15px) and--text-2xl(20px).--text-2xssits below the existing--text-xxs(12px). The names follow the brief but read alike, so a rename may be worth it.--radius-2xl,--radius-3xland a pill,--radius-full. The pill replaces 999px, 9999px and 50%.--shadow-raised, plus--z-sticky,--z-overlay,--z-bandand--z-sidebarfor the layers inside the workspace.( width < 640px )(the rebuild'ssm).Shared recipes: declarations repeated across files are now defined once in
lib/theme/recipes.module.cssand pulled in with CSS Modulescomposes. That file sits in theuilayer, so a component's own rules still override them. The recipes are the ellipsis trio (33 places), the band's header hairline, the 1px inset image outline and the notification dot with its icon mask. The duplicatedspin/sitePreviewSpinandpulse/skeletonPulsekeyframes become onestudio-spinand onestudio-pulse.Icons size themselves at 16px. That comes from
LucideProviderand a 16px default on the custom icons. The globalsvg { 16px }rule goes, along with itsdata-keep-sizeescape hatch and the rules that only restated 16px.Nested surfaces step automatically, as in the rebuild. A surface sets
--surfaceto its fill, and--mutedinside it resolves a shade up through@propertyand@container style()queries. That replaces five hand-patched--mutedoverrides (composer, command sheet, composer dialog, settings cards, question card). WebKit supports it, and the harness checks it there.Dark mode follows the nearest scene. Scheme-dependent colors use
light-dark(), and code tokens use Shiki'sdefaultColor: 'light-dark()'. The two non-color cases use the[data-color-scheme='dark'] .xform. No:root[...]orhtml[...]scheme selectors are left, so a scopeddata-color-schemescene renders its own colors.Lint and formatting:
@eslint/csslanguage. The existing Prettier setup formats it, which ends thevar(--x)/var( --x )mix, andnpx eslint --fixcovers stylesheets too.studio/use-theme-tokensrule flags literal colors, px font sizes, radii, elevation shadows and z-index layers outsidelib/theme.eslint-disable-next-linecomment with the reason.Changes you can see, all toward the rebuild:
Alertrounded-lg--radius-lgrounded-xl--radius-xlrounded-lg--radius-lgrounded-xl--radius-xlrounded-xl,rounded-lg--radius-xl,--radius-lg--radius-md--radius-mdrounded-sm--radius-smfont-weight: 500becomes--font-weight-medium(450) on the Blueprint upload link, the create-site form labels, the Add site summary title and the settings dialog title.--focus-width(1.5px), like the primitives.--motion-duration-surface, so with reduced motion they run at 120ms.Recorded and left as they are:
--text-xs, 16/17/20/24px on--text-xxs. Also the 1.2–1.4 ratios.bold.rounded-[4px]there too), the 1px drop line, 2px link focus rings, the 4px site icon and transcript previews, the annotation chip, the settings search highlight and the address-bar focus ring..item > .label,.source spanand similar) keep the ellipsis trio, becausecomposesneeds a single-class selector.Testing Instructions
npm run cli:build:ui && node apps/cli/dist/cli/main.mjs ui --no-open. Browse the sidebar, chats, the composer, settings, Add site, the palette and the guide in light and dark, in Chromium and Safari. Everything should look as on trunk.b32accbf3). The harness reads every rendered element's computed values in 39 scenes: the sidebar, chats, markdown, the composer, settings tabs, Add site steps, the palette, menus, notifications, the design system view, guide steps and narrow windows. The table counts what's left after the intended changes above and after known noise. The noise shows up trunk against trunk too: the Usage tab's loading race, Add site's random site name, WebKit's lack of scroll anchoring, and spinners rotating during motion passes.Each build ran twice. A difference counts only if it's the same in both trunk runs and both branch runs. Add site, Share and the narrow sidebar each had one timing-dependent difference (Blueprint thumbnails or preview list still loading, sidebar mid-slide), so I re-ran those scenes on their own, and they matched trunk exactly.
The intended differences in these scenes are the dragged-row and field-composer radii, the 450 weight on "upload a file" and the settings title, and the avatar ring inheriting a 9999px instead of 50% radius (the same 20px circle). The other radius changes are on elements the scenes don't render (error boxes, previews, the drop pill).
npm run lint,npm run typecheckandnpm test -- apps/ui tools/eslint-plugin-studiopass (869 tests).Pre-merge Checklist
🤖 Generated with Claude Code