feat(storybook): modularize CSS module support as pluggable preset config#36088
Merged
Hotell merged 26 commits intomicrosoft:masterfrom May 5, 2026
Merged
feat(storybook): modularize CSS module support as pluggable preset config#36088Hotell merged 26 commits intomicrosoft:masterfrom
Hotell merged 26 commits intomicrosoft:masterfrom
Conversation
…me, refine sandbox & tabs
Bebop docsite (apps/public-docsite-v9-headless):
- Custom BebopDocsPage replaces autodocs: title/description/primary canvas/
ArgTypes/remaining stories layout matching deployed Fluent docs.
- BebopSource portals a tabbed Story.tsx + .module.css source panel into the
same .sbdocs-preview canvas card the user already sees, listening to
Storybook's native 'Show code' toggle so it sits next to 'Open in Stackblitz'.
- Per-story tab strip is filtered to CSS modules actually referenced in the
displayed TSX (the meta still lists the full set so the sandbox can bundle
what's needed).
- Sidebar/toolbar accent + tab indicator + selected nav now use Bebop
magenta #9b1f5a (Figma --prmt-color-red ramp); replaces the prior near-black
#4a0a2c. Sidebar hover bg switched to neutral grey.
- Drop the rsms.me Inter import; everything inherits Segoe UI.
Stories (react-headless-components-preview/stories):
- New README.md replaces CLAUDE.md, merging the package metadata header with
the full authoring guide (pattern, boilerplate, token tiers, gotchas,
PR checklist, file map).
- withStorySource pins each story's own raw source as
parameters.docs.source.code+originalSource and overrides fullSource via a
non-writable getter so the babel preset's empty post-strip overwrite is
swallowed; rewrites long ../../bebop/components/*.module.css paths to a
colocated ./styles/*.module.css for paste-ready snippets.
- withCssModuleSource bundles tokens.css + only the CSS modules referenced
from src/example.tsx into the Stackblitz sandbox under src/styles/, and
prepends an import of tokens.css to App.tsx.
- Remove the broken Copy Page button.
Babel preset (babel-preset-storybook-full-source):
- modifyImportsPlugin no longer warns for known-safe relative patterns
(*.module.css, ?raw queries, with{Story,CssModule}Source helpers,
*.stories?raw). Strip behaviour and tests are unchanged.
Recovered bebop/ design system source (tokens.css + 27 component CSS modules)
that drives the docsite preview and Stackblitz sandboxes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…imer - Rename bebop/ folder to theme/ (tokens.css + 27 component CSS modules) - Rename BebopDocsPage -> HeadlessDocsPage and BebopSource -> HeadlessSourcePanel - Scrub all bebop identifiers, paths, classes, and comments across stories, helpers, .storybook config, and project.json files - Add a disclaimer banner on the docs page explaining that headless components ship without default styles and that the demo CSS is illustrative only Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move every `theme/components/*.module.css` into the corresponding story folder under `stories/src/<Component>/`. Stories that compose multiple components (Field, Dialog, Select, MessageBar, SpinButton) reference the adjacent folder's module via a sibling import. - Update `withStorySource` and `withCssModuleSource` regexes to match any relative `*.module.css` import (not just the old `theme/components/` path) and rewrite to `./styles/` for paste-ready snippets and Stackblitz sandbox layout. - `theme/` now only contains `tokens.css` (still global). - Update the stories README to document the colocated layout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The global tokens.css now lives under apps/public-docsite-v9-headless/theme/ together with the docsite that consumes it. Update import paths in: - apps/public-docsite-v9-headless/.storybook/preview.js - stories/.storybook/preview.js - stories/src/_helpers/withCssModuleSource.ts (?raw inline) - project.json build inputs (workspaceRoot -> projectRoot for the docsite) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Owning tokens.css inside the stories package (where the headless story authors edit theme variables) is cleaner than reaching from a sibling package into the docsite app. The docsite now imports it from there. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the design-system swap suggestion; just clarify that the CSS in the stories is a demonstration of one possible look. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…review The standalone stories storybook lives at stories/.storybook/preview.js, so tokens.css (now at stories/theme/tokens.css) is one level up via ../theme/tokens.css — not ../../theme. The docsite build still passes because it imports via its own preview.js which uses a different relative path. Also add a beachball change file for the babel-preset-storybook-full-source warning skip (committed earlier). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-check errors - Exclude react-headless-components-preview-stories from auto-inferred test-ssr target via nx.json workspace plugin config (esbuild can't resolve ?raw query imports used by withStorySource). - Narrow language type in HeadlessSourcePanel to satisfy SyntaxHighlighter's SupportedLanguage union (was string). - Type RatingIcon helpers as React.FC instead of () => React.ReactNode so they match RatingDisplay's icon prop under React 17/18 stricter typing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ton underline - Bump heads-up disclaimer font to 16px and beef up the styling so it stands out on every story page. - Add a secondary preview note clarifying the controls are in preview and their APIs are subject to change. - Force the 'Show code' and 'Open in Stackblitz' buttons' hover/focus underline to the magenta accent (Storybook's default and the sandbox addon's hard-coded blue both override here). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .storybook/tsconfig.json runs with checkJs:true so the webpack config in main.js needed JSDoc annotations: `patchRules` parameter is annotated as `any[]` and the `localConfig` cast as `any` to access `module.rules`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Many *.module.css files in the headless stories had selectors written as their bundler-generated names (e.g. .dialog-module__row--0+lMS) or with trailing dashes/escaped pluses (.bar-, .card\+). These selectors never matched at runtime, leaving Dialog, Divider, MessageBar, Rating, SearchBox, Skeleton, Slider and others without their demo styles. - Strip the '<file>-module__' prefix and '--<hash>' suffix - Strip stray trailing '-' / escaped '+' from selectors - Combine .surface with .alertSurface in the alert / non-modal Dialog stories so the surface is positioned and styled correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Skeleton demo had an extra 140px 'thumb' bar and a shimmer animation that didn't match the deployed reference. Rewrite the CSS module to match the deployed look: a clean card with avatar + two short lines, then three horizontal bars with a softer pulse animation. Drop the unused .thumb. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…le.css Removes the `react-headless-components-preview-stories` SSR-test exclusion added when these tests started failing. test-ssr's esbuild pipeline now bundles `?raw` queries (raw text loader with extension resolution) and shims `*.module.css` imports as a Proxy that echoes the property name — sufficient for SSR snapshots without the actual CSS-Modules transform. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The ambient `declare module '*?raw'` lived in the global `typings/static-assets/` allowlist, which is too broad — it pulled the shape into every consumer's compile. Move it to a colocated `raw.d.ts` inside the stories package so the typing is opt-in rather than workspace-wide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the special-case for `*.module.css`, `?raw` queries, and withStorySource/withCssModuleSource imports that suppressed the modifyImports warning. The warning is informational and the helpers do their own thing — silencing them was tightly-coupled wallpaper. Strip behaviour and the existing 15 tests are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Source of truth for the CSS-Modules + ?raw webpack wiring lives at `stories/.storybook/css-modules-webpack.js`. Both the per-package storybook and the docsite app `require()` it — eliminates the duplicated ~80 LoC of `cssModuleRule` + `patchRules` between the two configs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e app HeadlessDocsPage / HeadlessSourcePanel are docsite-app concerns, not stories-package concerns — the per-package storybook should not need a custom docs page. Move both into `apps/public-docsite-v9-headless/.storybook/` and wire the docsite's preview.js to consume them locally. Externalize the previously-inlined `<style>` block into a static `headless-docs-page.css` loaded once from preview.js. Switch HeadlessSourcePanel from inline CSSProperties objects to emotion-via-`storybook/theming` `styled` components so the panel inherits the active SB theme tokens (matches the rest of the docs chrome). Surface the `CssModule` / `HeadlessSourceParameters` types from `withCssModuleSource` (which produces them) instead of from the panel that consumes them — cleaner one-way dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without an explicit webfont, the previous font stack fell through to BlinkMacSystemFont/Roboto on machines without Segoe installed (i.e. anyone not on Windows). Pull the four Segoe UI weights (light/normal/ semibold/bold) from Microsoft's static font CDN in both the manager chrome and the story canvas iframe so the docsite renders Segoe on every OS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends fullSourcePlugin to also write Story.parameters.docs.source.code and originalSource on every story export, using the cleaned raw file contents (with `*.module.css` paths rewritten to the Stackblitz `./styles/<basename>` layout). Removes the need for per-story `withStorySource(...)` calls and `?raw` story-source imports. Sweeps all 42 headless story files to drop the boilerplate, deletes the `withStorySource` helper, and updates the four fixture outputs to match the new injection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Card was the only headless component story using inline Tailwind class strings, which the docsite doesn't load — so /docs/headless-components-card rendered unstyled. Add a `card.module.css` driven by the same token ramp (--bg, --border, --space-*, --radius-*, --shadow-*, --accent…) the rest of the components use, and switch CardDefault / CardSelectable / CardDisabled to consume it. Wire `withCssModuleSource` in the meta so the Stackblitz sandbox bundles the new stylesheet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Stories README: §3 now describes the auto-injected docs source flow and the corrected `...withCssModuleSource(...)` spread (the previous example used a `transform:` shape the helper never matched). §8 paths point at the moved `?raw` typing, the shared webpack module, and the docs page components in the docsite app. - Babel preset README: documents the new `parameters.docs.source.code` / `originalSource` injection alongside the existing `fullSource` feature. - test-ssr README: documents the two custom esbuild plugins (`?raw` query loader, `*.module.css` Proxy shim) so future contributors know they exist before reaching for a testSSR exclusion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Storybook ships a global \`.docs-story + div > div:last-child { background:
rgb(0,0,0) }\` rule meant for the legacy Source block. Our portaled
HeadlessSourcePanel renders into the matched DOM position; the previous
inline \`style=\` on its container beat the rule on specificity, but the
move to \`styled\` from \`storybook/theming\` (commit 7c949d1) emits class
names that don't.
Reset the inherited paint (background / box-shadow / border-radius /
right) on \`.headless-source-portal > div\` so the panel's own light
surface and theme-driven tokens show through. Background uses the
\`--bg-elev\` token from \`theme/tokens.css\`, already loaded in preview.js.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The .preview's negative margins push it to the card's border, but its own square top corners were poking past the card's rounded boundary — clearly visible on Selectable/Disabled where the magenta border made the mismatch obvious. Added overflow: hidden to .card so children clip to the rounded shape; the border and box-shadow render outside the overflow box and stay intact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both packages were merged with Tailwind class strings inline, which the docsite doesn't load — same problem we hit with Card. Following the existing pattern: - New drawer.module.css covers the OverlayDrawer (fixed-position dialog pinned to the right edge) and InlineDrawer (in-flow expanding panel) variants, plus shared DrawerHeader / Body / Footer / nav / button classes. Native `<dialog>` user-agent styles needed `left: auto` to stop centering and let `right: 0` win. - New popover.module.css consolidates the trigger / surface / heading / body / actionButton / menuItem / arrow rules used across all 11 popover stories. Multi-color trigger variants (root/nested/deep) are kept as triggerSecondary + triggerSmall modifiers driven by the monochrome accent ramp instead of arbitrary blue/indigo/purple. - Both meta files now spread `withCssModuleSource(...)` so the docsite's "Show code" tab strip and the Stackblitz sandbox bundle the right styles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📊 Bundle size report✅ No changes found |
9501585 to
684ac28
Compare
3bbea9f to
fe3901b
Compare
fe3901b to
9529896
Compare
|
Pull request demo site: URL |
940253b to
4e8e235
Compare
4e8e235 to
c56b5d1
Compare
tudorpopams
approved these changes
May 5, 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.
Summary
Makes the CSS module "Show code" support from #36073 pluggable and zero-boilerplate — story authors just
importa CSS module and everything works.Before this change, every story file in #36073 required manual
?rawimports and awithCssModuleSource()call to register its CSS into the docs panel. Now the babel plugin has opt-in configuration to enable auto-detection of*.module.cssimports at build time, and all configuration lives in a singlePresetConfigobject passed to the sandbox addon.Architecture
flowchart TB subgraph "Consumer — stories/.storybook/main.js" MC["PresetConfig\n{ importMappings, cssModules: { tokensFilePath } }"] end subgraph "Addon — react-storybook-addon-export-to-sandbox" WP["webpack.ts\ncreates babel-loader rule"] end subgraph "Babel Plugin — babel-preset-storybook-full-source" FP["fullsource.ts\nAST walk → detects *.module.css imports"] MI["modifyImports.ts\nrewrites paths to ./styles/<basename>"] end subgraph "Output — Story Parameters (injected per-story)" P1["parameters.fullSource"] P2["parameters.cssModuleSources\n{ cssModules: [{name, source}], tokensSource }"] end subgraph "Runtime — HeadlessSourcePanel" HP["Reads parameters → renders tabbed code view"] end MC -->|"options"| WP WP -->|"[plugin, { importMappings, cssModules }]"| FP FP --> MI FP -->|"reads .module.css from disk"| P2 FP -->|"injects cleaned source"| P1 P1 --> HP P2 --> HPflowchart LR subgraph "DRY Config (re-export pattern)" SP["stories/.storybook/main.js\n(source of truth)"] DP["apps/docsite/.storybook/main.js\n(extends stories config)"] SPV["stories/.storybook/preview.js\n(tokens, decorators, docs page)"] DPV["apps/docsite/.storybook/preview.js\n(extends stories preview)"] end DP --> SP DPV --> SPVWhat changed (vs #36073)
Pluggable preset config
PresetConfignow accepts acssModulesoption that flows through the addon's webpack preset into the babel plugin:Auto-detection replaces manual wiring (opt in behind config flag)
The babel plugin walks each
*.stories.tsxAST, finds*.module.cssimports, reads them from disk, and injectsparameters.cssModuleSourcesper-story.Before (each story file):
After:
DRY storybook configs
The docsite app (
apps/public-docsite-v9-headless) no longer duplicates webpack/preview setup — it re-exports from the stories package config and only adds deployment-specific overrides (stories paths,staticDirs,build.previewUrl,storySort).Co-located storybook infrastructure
Moved into
stories/.storybook/(source of truth):tokens.css— design tokens (wastheme/tokens.css)HeadlessDocsPage.tsx— custom docs page (was in app)HeadlessSourcePanel.tsx— tabbed code panel (was in app)headless-docs-page.css— docs page chrome (was in app)theme.js— Storybook theme (was in app)The
preview-storiespackage re-exports these forappconsumption.Removed dead code
withCssModuleSource.ts+raw.d.tscleanSource.tsrawQueryPlugin(esbuild)?rawimports in stories?rawresourceQuery webpack guardsRenamed parameter namespace
parameters.theme→parameters.cssModuleSourcesparameters.theme(which carries Fluent theme objects likewebDarkTheme)Test coverage
css-module-auto-detect,css-module-with-tokens)cssModuleSourcesshape)rawQueryPluginremoval)