Skip to content

fix(design-library): fix Storybook docs dark mode + add token import to web app#31089

Merged
ashleeradka merged 3 commits into
mainfrom
devin/1779158486-fix-storybook-dark-mode
May 19, 2026
Merged

fix(design-library): fix Storybook docs dark mode + add token import to web app#31089
ashleeradka merged 3 commits into
mainfrom
devin/1779158486-fix-storybook-dark-mode

Conversation

@ashleeradka
Copy link
Copy Markdown
Contributor

@ashleeradka ashleeradka commented May 19, 2026

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.css import to the web app.

Root cause analysis

Storybook issue: The theme decorator correctly sets data-theme on the iframe's <html> and <body>, and the body { background: var(--background) } rule in preview.css resolves to the correct dark color. However, Storybook's docs mode renders all content inside DocsWrapper and PreviewContainer — both are emotion-styled components that read backgrounds from Storybook's ThemeProvider:

DocsWrapper = styled.div(({ theme }) => ({
  background: theme.background.content,  // hardcoded to light
}))

This pattern is used by every docs element (headings, prop tables, code blocks, controls). A CSS !important override 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.css was 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

  1. .storybook/preview.ts.storybook/preview.tsx: Custom ThemedDocsContainer that reads the current theme global via Storybook's DocsContextProps API (context.getStoryContext()) and maps it to a matching Storybook theme created with create() from storybook/theming. The ThemeProvider cascade automatically applies correct colors to all docs elements.

  2. apps/web/src/index.css: Added @import "@vellum/design-library/tokens.css" — the documented integration path per the tokens.css header and design-library package.json exports.

  3. packages/design-library/tsconfig.json: Added .storybook/**/*.tsx to include list so the renamed preview file is typechecked.

  4. 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 !important would 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

Approach Why rejected
CSS !important overrides on .sbdocs-wrapper / .sbdocs-preview Incomplete — only handles 2 elements; all other docs text/table/code elements remain light-themed
parameters.docs.theme (static) Accepts one theme at config time; can't switch dynamically between 3 themes
storybook-dark-mode addon External dependency, only handles light/dark (not 3-way velvet), adds complexity

Safety

  • Custom themes use create() which fills defaults from the base — only specified colors override
  • DocsContainerProps is a public, typed API from @storybook/addon-docs/blocks
  • Try/catch fallback to light theme for standalone docs pages without stories
  • Token import is additive — only defines CSS variables, no overrides

References

Test plan

  • Typechecked both packages/design-library and apps/web — both pass
  • Linted apps/web — passes
  • All CI checks pass (10/10)

Link to Devin session: https://app.devin.ai/sessions/2bcf952e517f488f9c6fed7f9ec8bac8
Requested by: @ashleeradka


Open in Devin Review

…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-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/design-library/.storybook/preview.tsx
devin-ai-integration Bot and others added 2 commits May 19, 2026 02:47
…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>
Copy link
Copy Markdown
Contributor

@vex-assistant-bot vex-assistant-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Both issues addressed in 7e54286:

CI should be green on this push.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Test Results: Storybook Theme Switching

Ran 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)
Test Result
Light theme (default) — light bg, dark text, prop table readable Passed
Dark theme — dark bg (#17191C), light text, button visible Passed
Velvet theme — very dark bg (#121214), light text, red/pink accent Passed
Round-trip (Velvet → Light) — no visual artifacts Passed

Dark Theme (was broken — now fixed)

Dark theme

Velvet Theme

Velvet theme

Canvas Mode & Cross-Component Verification
Test Result
Canvas mode Light/Dark/Velvet switching Passed
Notice docs page — Dark/Velvet theming Passed

Notice Docs — Dark Theme

Notice dark

CI: 10/10 checks passing

Devin session

@ashleeradka ashleeradka merged commit 7dd08d2 into main May 19, 2026
15 checks passed
@ashleeradka ashleeradka deleted the devin/1779158486-fix-storybook-dark-mode branch May 19, 2026 03:07
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