fix(design-library): fix Storybook docs dark mode + add token import to web app#31089
Conversation
…to web app - Replace static preview.ts with preview.tsx that provides a custom ThemedDocsContainer. The container reads the current theme global via Storybook's DocsContextProps API and maps it to a matching Storybook theme (light/dark/velvet) created with create() from storybook/theming. This ensures all docs elements (wrapper, preview containers, headings, prop tables, code blocks) automatically pick up the correct theme colors through Storybook's ThemeProvider cascade. - Add @import "@vellum/design-library/tokens.css" to apps/web/src/index.css so the web app gets the design system CSS variables that 12+ component files already reference (var(--surface-base), var(--content-default), etc.). This is the documented integration path from tokens.css and was tracked in LUM-1607. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eafb5b8690
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…storybook-dark-mode
…ckfile - Add .storybook/**/*.tsx to packages/design-library/tsconfig.json so the renamed preview.tsx is included in typechecking (was only .storybook/**/*.ts). - Regenerate apps/web/bun.lock to include new Radix dependencies added by #31087 (react-checkbox, react-radio-group, react-slider, react-tabs). The previous lockfile had a stale snapshot of the design-library deps. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
There was a problem hiding this comment.
✦ COMMENT
Value: Fixes invisible buttons/text in Storybook docs dark mode (white-on-white), and wires up the missing tokens.css import so apps/web CSS variables actually resolve — both correctness fixes that unblock real design-library development.
What this does: Replaces the static preview.ts decorator with a ThemedDocsContainer that dynamically maps the Storybook theme global to a matching create()-based theme at the DocsContainer level, cascading correctly to all emotion-styled docs elements. Separately adds @import "@vellum/design-library/tokens.css" to apps/web/src/index.css.
🔴 CI failure — tsconfig doesn't cover .tsx in .storybook/
packages/design-library/tsconfig.json includes .storybook/**/*.ts but the renamed file is .storybook/preview.tsx. The Lint, Type Check & Build check is failing (1 annotation, exit code 2). Codex flagged this as P2 inline — it's the root cause.
Fix: add .storybook/**/*.tsx to the include array in packages/design-library/tsconfig.json:
"include": ["src/**/*.ts", "src/**/*.tsx", ".storybook/**/*.ts", ".storybook/**/*.tsx"]✅ The approach is correct
The DocsContainer override is the right integration point — CSS !important overrides would only fix the wrapper background, not the emotion-cascade on headings, prop tables, code blocks, and controls. Using create() with base inheritance and a try/catch fallback for standalone docs pages is clean.
✅ Token import
@import "@vellum/design-library/tokens.css" as the second import in index.css is the right place and documented path. Additive, no overrides.
Non-blocking: componentStories()[0] for reading the current theme global works but is slightly fragile — if Storybook eventually changes the DocsContextProps API shape, the try/catch fallback to light is the safety net. Fine for now.
One fix needed (tsconfig). Will approve once that lands.
Vellum Constitution — Inviting: consistent, readable tooling (a working Storybook with correct dark mode) makes the design system feel first-class for contributors.
|
Both issues addressed in 7e54286:
CI should be green on this push. |
Test Results: Storybook Theme SwitchingRan Storybook locally on the feature branch, tested theme switching across Light/Dark/Velvet in both docs mode and canvas mode end-to-end. Theme Switching in Docs Mode (Primary Fix)
Dark Theme (was broken — now fixed)Velvet ThemeCanvas Mode & Cross-Component Verification
Notice Docs — Dark ThemeCI: 10/10 checks passing |
Prompt / plan
Fixes Storybook docs-mode dark/light/velvet theme switching. The background was not respecting theme selection — white background persisted in dark mode, making white-text-on-white-background buttons invisible. Also adds the missing
tokens.cssimport to the web app.Root cause analysis
Storybook issue: The theme decorator correctly sets
data-themeon the iframe's<html>and<body>, and thebody { background: var(--background) }rule inpreview.cssresolves to the correct dark color. However, Storybook's docs mode renders all content insideDocsWrapperandPreviewContainer— both are emotion-styled components that read backgrounds from Storybook's ThemeProvider:This pattern is used by every docs element (headings, prop tables, code blocks, controls). A CSS
!importantoverride on just the wrapper backgrounds would leave all text/table elements with light-theme colors — unreadable on a dark background.Web app issue:
apps/web/src/index.csswas missing@import "@vellum/design-library/tokens.css". The web app has 82 CSS variable references across 12 files (var(--surface-base),var(--content-default), etc.) that all resolve to the initial value without this import. This was tracked in LUM-1607.How the code got into this state: PR #31045 set up Storybook with the theme decorator and CSS variables, but the docs-mode ThemeProvider cascade was not accounted for. The decorator correctly switches CSS variables (which works for canvas mode), but docs mode wraps everything in emotion-styled components that read from a separate ThemeProvider — not CSS variables.
Prevention: When setting up Storybook theming, always verify both canvas mode AND docs mode. They use different rendering pipelines — canvas renders stories directly with body as the background layer, while docs mode wraps everything in emotion-styled containers.
What changed
.storybook/preview.ts→.storybook/preview.tsx: CustomThemedDocsContainerthat reads the current theme global via Storybook'sDocsContextPropsAPI (context.getStoryContext()) and maps it to a matching Storybook theme created withcreate()fromstorybook/theming. TheThemeProvidercascade automatically applies correct colors to all docs elements.apps/web/src/index.css: Added@import "@vellum/design-library/tokens.css"— the documented integration path per the tokens.css header and design-librarypackage.jsonexports.packages/design-library/tsconfig.json: Added.storybook/**/*.tsxto include list so the renamed preview file is typechecked.apps/web/bun.lock: Regenerated to include Radix dependencies added by feat(design-library): add form input and navigation components #31087 (react-checkbox,react-radio-group,react-slider,react-tabs).Why this approach (not CSS overrides)
Storybook's docs elements all read from the ThemeProvider via emotion's
styled(({ theme }) => ...)pattern. Overriding individual class backgrounds with!importantwould only fix 2 of dozens of themed elements. The DocsContainer approach changes the ThemeProvider theme, which cascades to everything automatically — backgrounds, text colors, borders, shadows, code blocks, prop tables.Alternatives rejected
!importantoverrides on.sbdocs-wrapper/.sbdocs-previewparameters.docs.theme(static)storybook-dark-modeaddonSafety
create()which fills defaults from the base — only specified colors overrideDocsContainerPropsis a public, typed API from@storybook/addon-docs/blocksReferences
create()Test plan
packages/design-libraryandapps/web— both passapps/web— passesLink to Devin session: https://app.devin.ai/sessions/2bcf952e517f488f9c6fed7f9ec8bac8
Requested by: @ashleeradka