From 831d0d4dffcc0b0a884407bc2a4b5062e06c3741 Mon Sep 17 00:00:00 2001 From: Tudor Popa Date: Tue, 28 Apr 2026 19:30:04 +0200 Subject: [PATCH 01/25] feat(headless-docsite): embed code panel in canvas, Bebop magenta theme, 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> --- .../.storybook/main.js | 96 ++++++ .../.storybook/manager-head.html | 102 +++---- .../.storybook/preview-head.html | 103 +------ .../.storybook/preview.js | 15 +- .../.storybook/theme.js | 61 ++-- apps/public-docsite-v9-headless/project.json | 6 +- bebop/components/accordion.module.css | 85 ++++++ bebop/components/avatar.module.css | 126 ++++++++ bebop/components/badge.module.css | 86 ++++++ bebop/components/breadcrumb.module.css | 63 ++++ bebop/components/button.module.css | 149 +++++++++ bebop/components/chat-input.module.css | 130 ++++++++ bebop/components/checkbox.module.css | 77 +++++ bebop/components/dialog.module.css | 121 ++++++++ bebop/components/divider.module.css | 181 +++++++++++ bebop/components/field.module.css | 54 ++++ bebop/components/input.module.css | 85 ++++++ bebop/components/link.module.css | 50 +++ bebop/components/message-bar.module.css | 125 ++++++++ bebop/components/progress-bar.module.css | 82 +++++ bebop/components/radio-group.module.css | 88 ++++++ bebop/components/rating-display.module.css | 56 ++++ bebop/components/rating.module.css | 44 +++ bebop/components/select.module.css | 53 ++++ bebop/components/skeleton.module.css | 64 ++++ bebop/components/slider.module.css | 87 ++++++ bebop/components/spin-button.module.css | 83 +++++ bebop/components/spinner.module.css | 63 ++++ bebop/components/switch.module.css | 87 ++++++ bebop/components/tab-list.module.css | 86 ++++++ bebop/components/textarea.module.css | 54 ++++ bebop/components/toggle-button.module.css | 114 +++++++ bebop/components/toolbar.module.css | 87 ++++++ bebop/tokens.css | 209 +++++++++++++ .../src/modifyImports.ts | 15 +- .../stories/.storybook/main.js | 73 ++++- .../stories/.storybook/preview-head.html | 7 +- .../stories/.storybook/preview.js | 18 +- .../stories/README.md | 200 +++++++++++- .../stories/eslint.config.js | 11 + .../stories/project.json | 7 +- .../AccordionCollapsible.stories.tsx | 29 +- .../Accordion/AccordionDefault.stories.tsx | 41 ++- .../stories/src/Accordion/index.stories.tsx | 4 + .../src/Avatar/AvatarDefault.stories.tsx | 41 ++- .../stories/src/Avatar/index.stories.tsx | 4 + .../src/Badge/BadgeDefault.stories.tsx | 27 +- .../stories/src/Badge/index.stories.tsx | 4 + .../Breadcrumb/BreadcrumbDefault.stories.tsx | 37 +-- .../stories/src/Breadcrumb/index.stories.tsx | 4 + .../src/Button/ButtonDefault.stories.tsx | 59 +++- .../stories/src/Button/index.stories.tsx | 4 + .../src/Checkbox/CheckboxDefault.stories.tsx | 46 ++- .../stories/src/Checkbox/index.stories.tsx | 4 + .../src/Dialog/DialogAlert.stories.tsx | 71 +++-- .../src/Dialog/DialogControlled.stories.tsx | 41 ++- .../src/Dialog/DialogDefault.stories.tsx | 24 +- .../src/Dialog/DialogKeepMounted.stories.tsx | 44 ++- .../src/Dialog/DialogNested.stories.tsx | 45 +-- .../src/Dialog/DialogNoTrigger.stories.tsx | 42 +-- .../src/Dialog/DialogNonModal.stories.tsx | 30 +- .../Dialog/DialogWithCloseButton.stories.tsx | 76 ++--- .../stories/src/Dialog/index.stories.tsx | 12 + .../src/Divider/DividerDefault.stories.tsx | 25 +- .../src/Divider/DividerVertical.stories.tsx | 53 +++- .../stories/src/Divider/index.stories.tsx | 4 + .../src/Field/FieldDefault.stories.tsx | 47 +-- .../stories/src/Field/index.stories.tsx | 8 + .../stories/src/Input/InputBasic.stories.tsx | 32 ++ .../src/Input/InputDefault.stories.tsx | 65 +++- .../stories/src/Input/index.stories.tsx | 9 + .../stories/src/Link/LinkDefault.stories.tsx | 20 +- .../stories/src/Link/index.stories.tsx | 4 + .../MessageBar/MessageBarDefault.stories.tsx | 43 ++- .../MessageBar/MessageBarIntent.stories.tsx | 86 +++--- .../stories/src/MessageBar/index.stories.tsx | 8 + .../ProgressBarDefault.stories.tsx | 40 ++- .../stories/src/ProgressBar/index.stories.tsx | 4 + .../RadioGroup/RadioGroupDefault.stories.tsx | 32 +- .../stories/src/RadioGroup/index.stories.tsx | 4 + .../src/Rating/RatingDefault.stories.tsx | 30 +- .../stories/src/Rating/index.stories.tsx | 4 + .../RatingDisplayCompact.stories.tsx | 33 +- .../RatingDisplayDefault.stories.tsx | 36 ++- .../src/RatingDisplay/index.stories.tsx | 4 + .../SearchBox/SearchBoxDefault.stories.tsx | 25 +- .../stories/src/SearchBox/index.stories.tsx | 4 + .../src/Select/SelectDefault.stories.tsx | 46 +-- .../stories/src/Select/index.stories.tsx | 8 + .../src/Skeleton/SkeletonDefault.stories.tsx | 24 +- .../stories/src/Skeleton/index.stories.tsx | 4 + .../src/Slider/SliderDefault.stories.tsx | 36 ++- .../stories/src/Slider/index.stories.tsx | 4 + .../SpinButton/SpinButtonDefault.stories.tsx | 30 +- .../stories/src/SpinButton/index.stories.tsx | 8 + .../src/Spinner/SpinnerDefault.stories.tsx | 34 ++- .../src/Spinner/SpinnerLabels.stories.tsx | 32 +- .../stories/src/Spinner/index.stories.tsx | 4 + .../src/Switch/SwitchDefault.stories.tsx | 55 +++- .../stories/src/Switch/index.stories.tsx | 4 + .../src/TabList/TabListDefault.stories.tsx | 27 +- .../stories/src/TabList/index.stories.tsx | 4 + .../src/Textarea/TextareaDefault.stories.tsx | 24 +- .../stories/src/Textarea/index.stories.tsx | 4 + .../ToggleButtonDefault.stories.tsx | 51 +++- .../src/ToggleButton/index.stories.tsx | 4 + .../src/Toolbar/ToolbarDefault.stories.tsx | 84 ++--- .../Toolbar/ToolbarToggleButton.stories.tsx | 72 ++--- .../src/Toolbar/ToolbarVertical.stories.tsx | 39 +-- .../stories/src/Toolbar/index.stories.tsx | 4 + .../stories/src/_helpers/BebopDocsPage.tsx | 145 +++++++++ .../stories/src/_helpers/BebopSource.tsx | 287 ++++++++++++++++++ .../src/_helpers/withCssModuleSource.ts | 128 ++++++++ .../stories/src/_helpers/withStorySource.ts | 104 +++++++ typings/static-assets/index.d.ts | 14 + 115 files changed, 4979 insertions(+), 913 deletions(-) create mode 100644 bebop/components/accordion.module.css create mode 100644 bebop/components/avatar.module.css create mode 100644 bebop/components/badge.module.css create mode 100644 bebop/components/breadcrumb.module.css create mode 100644 bebop/components/button.module.css create mode 100644 bebop/components/chat-input.module.css create mode 100644 bebop/components/checkbox.module.css create mode 100644 bebop/components/dialog.module.css create mode 100644 bebop/components/divider.module.css create mode 100644 bebop/components/field.module.css create mode 100644 bebop/components/input.module.css create mode 100644 bebop/components/link.module.css create mode 100644 bebop/components/message-bar.module.css create mode 100644 bebop/components/progress-bar.module.css create mode 100644 bebop/components/radio-group.module.css create mode 100644 bebop/components/rating-display.module.css create mode 100644 bebop/components/rating.module.css create mode 100644 bebop/components/select.module.css create mode 100644 bebop/components/skeleton.module.css create mode 100644 bebop/components/slider.module.css create mode 100644 bebop/components/spin-button.module.css create mode 100644 bebop/components/spinner.module.css create mode 100644 bebop/components/switch.module.css create mode 100644 bebop/components/tab-list.module.css create mode 100644 bebop/components/textarea.module.css create mode 100644 bebop/components/toggle-button.module.css create mode 100644 bebop/components/toolbar.module.css create mode 100644 bebop/tokens.css create mode 100644 packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx create mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage.tsx create mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx create mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts create mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts diff --git a/apps/public-docsite-v9-headless/.storybook/main.js b/apps/public-docsite-v9-headless/.storybook/main.js index f1f764f65e3c0..dd66e928f0f67 100644 --- a/apps/public-docsite-v9-headless/.storybook/main.js +++ b/apps/public-docsite-v9-headless/.storybook/main.js @@ -1,5 +1,97 @@ +const path = require('path'); + const rootMain = require('../../../.storybook/main'); +const repoRoot = path.resolve(__dirname, '../../..'); +const tokensDir = path.resolve(repoRoot, 'bebop'); +const headlessStoriesDir = path.resolve( + repoRoot, + 'packages/react-components/react-headless-components-preview/stories', +); + +/** + * CSS Modules webpack rule for the headless docsite. + * + * Storybook's `@storybook/builder-webpack5` ships a default `\.css$` rule that + * pipes any CSS through `style-loader` + plain `css-loader`. That handles + * `bebop/tokens.css` correctly. For `*.module.css` files (the per-component + * design-system styles) we want CSS Modules, so we narrow the default rule to + * skip `.module.css` and add a dedicated rule that turns on `modules: true`. + * + * Scoped via `include` so we only handle CSS coming from the design-system + * tree and the headless stories tree. + */ +const cssIncludes = [tokensDir, headlessStoriesDir]; + +const cssModuleRule = { + test: /\.module\.css$/, + include: cssIncludes, + // Skip `?raw` imports โ€” those go through Storybook's `resourceQuery:/raw/` + // asset/source rule and yield the file's text content. Without this filter, + // webpack chains style-loader + css-loader on top of asset/source and the + // story ends up importing the style-loader JS wrapper instead of the CSS. + resourceQuery: { not: [/raw/] }, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]__[local]--[hash:base64:5]', + }, + importLoaders: 0, + }, + }, + ], +}; + +/** + * Patches existing webpack rules in two ways: + * 1. Storybook's default `\.css$` rule gets a `\.module\.css$` exclude, so + * our CSS Modules rule is the only one that handles `*.module.css`. + * 2. Any rule whose test matches `.css` or `.tsx?` and that doesn't already + * filter on `resourceQuery` is told to skip `?raw` queries. That keeps + * Storybook's built-in `resourceQuery:/raw/` asset/source rule as the + * sole handler for `import x from 'path?raw'`, which we use to surface + * story file source and CSS Module source in the Show-code panel. + * + * Returns the input array for chaining. + */ +function patchRules(rules) { + for (const rule of rules) { + if (!rule || typeof rule !== 'object') continue; + const test = rule.test; + const isRegExp = test instanceof RegExp; + const matchesPlainCss = isRegExp && test.source === /\.css$/.source; + if (matchesPlainCss) { + const existing = rule.exclude; + const moduleRegex = /\.module\.css$/; + if (Array.isArray(existing)) { + rule.exclude = [...existing, moduleRegex]; + } else if (existing) { + rule.exclude = [existing, moduleRegex]; + } else { + rule.exclude = moduleRegex; + } + } + // Probe with synthetic file paths so we don't have to parse the regex source. + // Includes `.stories.tsx`/`.stories.ts` shapes because Storybook's + // export-order-loader and the @fluentui export-to-sandbox addon both add + // rules that match those and would otherwise re-process `?raw` imports. + const matchesCssOrTs = + isRegExp && + (test.test('a.css') || + test.test('a.tsx') || + test.test('a.ts') || + test.test('a.stories.tsx') || + test.test('a.stories.ts')); + if (matchesCssOrTs && rule.resourceQuery == null) { + rule.resourceQuery = { not: [/raw/] }; + } + } + return rules; +} + module.exports = /** @type {Omit} */ ({ ...rootMain, stories: [ @@ -18,6 +110,10 @@ module.exports = /** @type {Omit { const localConfig = /** @type config */ ({ ...rootMain.webpackFinal(config, options) }); + localConfig.module = localConfig.module || { rules: [] }; + const rules = patchRules([...(localConfig.module.rules || [])]); + localConfig.module.rules = [cssModuleRule, ...rules]; + return localConfig; }, }); diff --git a/apps/public-docsite-v9-headless/.storybook/manager-head.html b/apps/public-docsite-v9-headless/.storybook/manager-head.html index cca30d3b13bd0..90513a751a048 100644 --- a/apps/public-docsite-v9-headless/.storybook/manager-head.html +++ b/apps/public-docsite-v9-headless/.storybook/manager-head.html @@ -5,61 +5,22 @@ - diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 920889c0e46fb..8a69b4aa46a32 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -1,7 +1,15 @@ import { polyfillBodyAndObserve } from '@microsoft/focusgroup-polyfill/shadowless'; import * as rootPreview from '../../../.storybook/preview'; -import { tailwindSandboxTemplate } from './tailwind-sandbox-template'; + +// Design tokens (light + dark CSS custom properties on :root and +// [data-theme="dark"]), plus a few base resets for body/html. Loaded once +// for every story rendered in this Storybook. +import '../../../bebop/tokens.css'; + +// Custom docs page that renders the "Show code" panel with TSX | CSS tabs. +// See `packages/.../stories/src/_helpers/BebopDocsPage.tsx` for rationale. +import { BebopDocsPage } from '../../../packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage'; polyfillBodyAndObserve(); @@ -13,6 +21,7 @@ export const parameters = { ...rootPreview.parameters, docs: { ...rootPreview.parameters.docs, + page: BebopDocsPage, }, options: { storySort: { @@ -20,10 +29,6 @@ export const parameters = { order: ['Introduction', 'Headless Components'], }, }, - exportToSandbox: { - ...rootPreview.parameters.exportToSandbox, - ...tailwindSandboxTemplate, - }, reactStorybookAddon: { docs: { argTable: { diff --git a/apps/public-docsite-v9-headless/.storybook/theme.js b/apps/public-docsite-v9-headless/.storybook/theme.js index 4a00dce65f788..9dd53beb75e85 100644 --- a/apps/public-docsite-v9-headless/.storybook/theme.js +++ b/apps/public-docsite-v9-headless/.storybook/theme.js @@ -1,37 +1,52 @@ import { create } from 'storybook/theming'; /** - * Theming and branding the storybook to fluent. Taken from https://storybook.js.org/docs/react/configure/theming + * Custom Storybook chrome for the headless components docsite. + * + * Values mirror the light-mode tokens in `bebop/tokens.css`. The Storybook + * theme builds at compile time and cannot read CSS custom properties, so the + * palette is inlined here. Update this file alongside `bebop/tokens.css` if + * the design tokens shift. */ const theme = create({ base: 'light', - // Storybook-specific color palette - colorPrimary: 'rgba(255, 255, 255, .4)', - colorSecondary: '#0078d4', + // Storybook color palette + colorPrimary: '#9b1f5a', // matches --accent + colorSecondary: '#9b1f5a', - // UI - appBg: '#ffffff', - appContentBg: '#ffffff', - appBorderColor: '#e0e0e0', // use msft gray - appBorderRadius: 4, + // UI surfaces + appBg: '#f7f7f8', // --bg-soft + appContentBg: '#ffffff', // --bg + appPreviewBg: '#ffffff', + appBorderColor: '#e4e4e7', // --border + appBorderRadius: 12, // --radius-lg // Fonts - fontBase: - '"Segoe UI", "Segoe UI Web (West European)", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;', - fontCode: 'monospace', + fontBase: '"Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif', + fontCode: '"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', + + // Text + textColor: '#0a0a0a', // --text + textInverseColor: '#ffffff', // --text-on-accent + textMutedColor: '#52525b', // --text-muted + + // Toolbar + barTextColor: '#52525b', + barHoverColor: '#9b1f5a', + barSelectedColor: '#9b1f5a', + barBg: '#ffffff', + + // Form controls + buttonBg: '#ffffff', + buttonBorder: '#e4e4e7', + booleanBg: '#f2f2f4', // --surface-muted + booleanSelectedBg: '#9b1f5a', + inputBg: '#ffffff', + inputBorder: '#e4e4e7', + inputTextColor: '#0a0a0a', + inputBorderRadius: 8, // --radius-md - // Text colors - textColor: '#11100f', - textInverseColor: '#0078d4', // use msft primary blue default - - // Toolbar default and active colors - barSelectedColor: '#0078d4', // use msft primary blue default - - // Form colors - inputBorderRadius: 4, - - // Use the fluent branding for the upper left image brandTitle: 'Fluent UI Headless Components', brandUrl: 'https://github.com/microsoft/fluentui/tree/master/packages/react-components/react-headless-components-preview', diff --git a/apps/public-docsite-v9-headless/project.json b/apps/public-docsite-v9-headless/project.json index f231a9a1891f6..9f0b35dec4c2f 100644 --- a/apps/public-docsite-v9-headless/project.json +++ b/apps/public-docsite-v9-headless/project.json @@ -11,7 +11,8 @@ "projects": ["react-storybook-addon", "react-storybook-addon-export-to-sandbox", "storybook-llms-extractor"], "target": "build" } - ] + ], + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/bebop/**"] }, "build-storybook:docsite": { "dependsOn": [ @@ -19,7 +20,8 @@ "projects": ["react-storybook-addon", "react-storybook-addon-export-to-sandbox", "storybook-llms-extractor"], "target": "build" } - ] + ], + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/bebop/**"] } } } diff --git a/bebop/components/accordion.module.css b/bebop/components/accordion.module.css new file mode 100644 index 0000000000000..0231ec8bc2b03 --- /dev/null +++ b/bebop/components/accordion.module.css @@ -0,0 +1,85 @@ +.accordion { + display: flex; + flex-direction: column; + width: 100%; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--bg-elev); + overflow: hidden; +} + +.item { + border-bottom: 1px solid var(--border); +} + +.item:last-child { + border-bottom: none; +} + +.header { + margin: 0; +} + +.headerBtn { + width: 100%; + display: flex; + align-items: center; + gap: 12px; + padding: 14px 18px; + background: transparent; + border: none; + font-size: 13.5px; + font-weight: 500; + color: var(--text); + cursor: pointer; + text-align: left; + transition: background var(--duration-fast) var(--ease-standard); +} + +.headerBtn:hover { + background: var(--surface-muted); +} + +.headerBtn:focus-visible { + outline: none; + box-shadow: inset 0 0 0 2px var(--accent); +} + +.expandIcon { + width: 14px; + height: 14px; + color: var(--text-muted); + transition: transform var(--duration-medium) var(--ease-emphasized); + flex-shrink: 0; +} + +.item[data-open] .expandIcon { + transform: rotate(90deg); + color: var(--text); +} + +.panel { + padding: 0 18px; + font-size: 13px; + color: var(--text-muted); + line-height: 1.65; + max-height: 0; + overflow: hidden; + transition: max-height var(--duration-medium) var(--ease-emphasized), + padding var(--duration-medium) var(--ease-emphasized); +} + +.item[data-open] .panel { + max-height: 320px; + padding: 0 18px 16px; +} + +.label { + flex: 1; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 480px; +} diff --git a/bebop/components/avatar.module.css b/bebop/components/avatar.module.css new file mode 100644 index 0000000000000..79ef61fcd5f4f --- /dev/null +++ b/bebop/components/avatar.module.css @@ -0,0 +1,126 @@ +.avatar { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: var(--accent); + color: var(--accent-contrast); + font-weight: 600; + position: relative; + overflow: hidden; + flex-shrink: 0; + user-select: none; + letter-spacing: 0; +} + +.size32 { + width: 32px; + height: 32px; + font-size: 12px; +} + +.size40 { + width: 40px; + height: 40px; + font-size: 14px; +} + +.size56 { + width: 56px; + height: 56px; + font-size: 19px; +} + +.size72 { + width: 72px; + height: 72px; + font-size: 24px; +} + +.image { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.initials { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; +} + +/* Tones โ€” neutral grays + a single brand-pink accent so the group isn't monotonous */ +.tone1 { + background: #44403c; +} + +.tone2 { + background: #525252; +} + +.tone3 { + background: #57534e; +} + +.tone4 { + background: var(--brand); +} + +.stack { + display: inline-flex; +} + +.stack > * { + margin-left: -8px; + border: 2px solid var(--bg-elev); +} + +.stack > *:first-child { + margin-left: 0; +} + +.row { + display: flex; + align-items: center; + gap: 12px; +} + +.avatar-module__meta--0\+MMt { + display: flex; + flex-direction: column; +} + +.metaName { + font-weight: 600; + color: var(--text); + font-size: 13.5px; +} + +.metaSub { + color: var(--text-muted); + font-size: 12.5px; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 24px; +} + +.demoRow { + display: flex; + + align-items: center; + + gap: 12px; + + flex-wrap: wrap; +} diff --git a/bebop/components/badge.module.css b/bebop/components/badge.module.css new file mode 100644 index 0000000000000..bc754d065bace --- /dev/null +++ b/bebop/components/badge.module.css @@ -0,0 +1,86 @@ +.badge { + display: inline-flex; + align-items: center; + gap: 4px; + height: 22px; + padding: 0 8px; + border-radius: var(--radius-pill); + background: var(--surface-muted); + color: var(--text); + border: 1px solid var(--border); + font-size: 11.5px; + font-weight: 500; + letter-spacing: 0; +} + +.solid { + background: var(--accent); + color: var(--accent-contrast); + border-color: transparent; +} + +.success { + background: var(--success-soft); + color: var(--success); + border-color: transparent; +} + +.warning { + background: var(--warning-soft); + color: var(--warning); + border-color: transparent; +} + +.danger { + background: var(--brand-soft); + color: var(--brand); + border-color: transparent; +} + +.info { + background: var(--info-soft); + color: var(--info); + border-color: transparent; +} + +.accent { + background: var(--surface-muted); + color: var(--text); + border-color: var(--border); +} + +.dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; + display: inline-block; +} + +.icon { + width: 12px; + height: 12px; +} + +.counter { + height: 18px; + min-width: 18px; + padding: 0 6px; + font-size: 10.5px; + font-weight: 600; + background: var(--brand); + color: white; + border-color: transparent; +} + +/* Demo helpers (used by Storybook examples) */ + +.badge-module__demo--uj\+MF { + display: flex; + + align-items: center; + + gap: 12px; + + flex-wrap: wrap; +} diff --git a/bebop/components/breadcrumb.module.css b/bebop/components/breadcrumb.module.css new file mode 100644 index 0000000000000..aea22063ad818 --- /dev/null +++ b/bebop/components/breadcrumb.module.css @@ -0,0 +1,63 @@ +.crumb { + display: flex; + align-items: center; +} + +.list { + display: flex; + align-items: center; + gap: 2px; + list-style: none; + margin: 0; + padding: 0; + font-size: 13px; +} + +.item { + display: flex; + align-items: center; +} + +.btn { + background: none; + border: none; + padding: 4px 8px; + border-radius: var(--radius-md); + color: var(--text-muted); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.btn:hover { + background: var(--surface-muted); + color: var(--text); +} + +.btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.btn[data-current] { + color: var(--text); + font-weight: 600; + cursor: default; +} + +.btn[data-current]:hover { + background: transparent; +} + +.divider { + display: inline-flex; + align-items: center; + color: var(--text-faint); + list-style: none; +} + +.divider svg { + width: 12px; + height: 12px; +} diff --git a/bebop/components/button.module.css b/bebop/components/button.module.css new file mode 100644 index 0000000000000..351fc1750adaa --- /dev/null +++ b/bebop/components/button.module.css @@ -0,0 +1,149 @@ +/* Bebop button โ€” pill-shaped, monochrome */ +.button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + height: 32px; + padding: 0 14px; + border-radius: var(--radius-pill); + border: 1px solid transparent; + background: var(--accent); + color: var(--accent-contrast); + font-size: 13px; + font-weight: 500; + letter-spacing: 0; + cursor: pointer; + user-select: none; + text-decoration: none; + transition: background-color var(--duration-fast) var(--ease-standard), + color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard), + transform 80ms var(--ease-standard); +} + +.button:hover { + background: var(--accent-strong); +} + +.button:active { + transform: scale(0.98); +} + +.button:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.button[data-disabled], +.button[data-disabled-focusable] { + opacity: 0.4; + cursor: not-allowed; +} + +.button[data-disabled]:hover, +.button[data-disabled-focusable]:hover { + background: var(--accent); +} + +/* Secondary โ€” light gray pill */ +.secondary { + background: var(--surface-muted); + color: var(--text); +} + +.secondary:hover { + background: var(--surface-sunken); +} + +.secondary[data-disabled]:hover, +.secondary[data-disabled-focusable]:hover { + background: var(--surface-muted); +} + +/* Subtle โ€” text-only, no chrome until hover */ +.subtle { + background: transparent; + color: var(--text); +} + +.subtle:hover { + background: var(--surface-muted); +} + +.subtle[data-disabled]:hover, +.subtle[data-disabled-focusable]:hover { + background: transparent; +} + +/* Outline โ€” bordered transparent */ +.button-module__outline--gaC\+9 { + background: transparent; + color: var(--text); + border-color: var(--border-strong); +} + +.button-module__outline--gaC\+9:hover { + background: var(--surface-muted); + border-color: var(--text); +} + +/* Sizes */ +.small { + height: 26px; + padding: 0 10px; + font-size: 12px; +} + +.large { + height: 40px; + padding: 0 18px; + font-size: 14px; +} + +/* Icon-only โ€” perfectly circular */ +.button[data-icon-only] { + width: 32px; + padding: 0; +} + +.iconOnlySmall[data-icon-only] { + width: 26px; + height: 26px; +} + +.iconOnlyLarge[data-icon-only] { + width: 40px; + height: 40px; +} + +.icon { + width: 14px; + height: 14px; + flex-shrink: 0; +} + +.large .icon, +.iconOnlyLarge .icon { + width: 16px; + height: 16px; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 16px; +} + +.demoRow { + display: flex; + + gap: 12px; + + align-items: center; + + flex-wrap: wrap; +} diff --git a/bebop/components/chat-input.module.css b/bebop/components/chat-input.module.css new file mode 100644 index 0000000000000..3d45d2c979925 --- /dev/null +++ b/bebop/components/chat-input.module.css @@ -0,0 +1,130 @@ +/* Bebop chat-input โ€” borderless field on a soft surface, + with a hairline bottom rule and inline icon buttons. + + Layout: [+] [text ยท placeholder text] [mic] [waveform | send] +*/ + +.chat { + display: flex; + align-items: center; + gap: 4px; + width: 100%; + padding: 8px 8px; + background: var(--bg-soft); + border-radius: var(--radius-pill); + border: 1px solid transparent; + transition: background var(--duration-fast) var(--ease-standard), + border-color var(--duration-fast) var(--ease-standard); +} + +.chat:has(:focus-visible) { + background: var(--bg-elev); + border-color: var(--text); +} + +.chatLeading, +.chatTrailing { + display: inline-flex; + align-items: center; + gap: 2px; + flex-shrink: 0; +} + +.chatField { + flex: 1; + border: none; + outline: none; + background: transparent; + padding: 8px 8px; + font-size: 14px; + color: var(--text); + min-width: 0; +} + +.chatField::placeholder { + color: var(--text-soft); +} + +.iconBtn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--text-muted); + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.iconBtn:hover { + background: var(--surface-muted); + color: var(--text); +} + +.iconBtn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.iconBtn svg { + width: 18px; + height: 18px; +} + +.iconBtn[disabled] { + opacity: 0.35; + cursor: not-allowed; +} + +.send { + background: var(--accent); + color: var(--accent-contrast); +} + +.send:hover { + background: var(--accent-strong); + color: var(--accent-contrast); +} + +.send svg { + width: 16px; + height: 16px; + stroke-width: 2.25; +} + +.send[disabled] { + background: var(--surface-muted); + color: var(--text-faint); +} + +/* Underline variant โ€” borderless, thin bottom rule (matches the larger + Bebop chat input shown in the canvas screenshot). */ + +.chatUnderline { + background: transparent; + border-radius: 0; + border-bottom: 1px solid var(--border); + padding: 6px 4px; +} + +.chatUnderline:has(:focus-visible) { + background: transparent; + border-bottom-color: var(--text); + border-color: transparent; + border-bottom: 1px solid var(--text); +} + +.chatUnderline .chatField { + font-size: 16px; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + width: 100%; + + max-width: 560px; +} diff --git a/bebop/components/checkbox.module.css b/bebop/components/checkbox.module.css new file mode 100644 index 0000000000000..78aeb19019d33 --- /dev/null +++ b/bebop/components/checkbox.module.css @@ -0,0 +1,77 @@ +.row { + position: relative; + display: inline-flex; + align-items: center; + gap: 10px; + cursor: pointer; + user-select: none; + font-size: 13.5px; + color: var(--text); + padding: 4px 0; +} + +.input { + position: absolute; + inset: 0; + width: 18px; + height: 18px; + opacity: 0; + cursor: pointer; + z-index: 1; +} + +.indicator { + width: 18px; + height: 18px; + border-radius: var(--radius-xs); + border: 1.5px solid var(--border-stronger); + background: var(--bg-elev); + display: inline-flex; + align-items: center; + justify-content: center; + color: transparent; + flex-shrink: 0; + transition: background var(--duration-fast) var(--ease-standard), + border-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.row:hover .indicator { + border-color: var(--text); +} + +.input:checked + .indicator, +.input[data-state='mixed'] + .indicator { + background: var(--accent); + border-color: var(--accent); + color: var(--accent-contrast); +} + +.input:focus-visible + .indicator { + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.input:disabled + .indicator { + background: var(--surface-muted); + border-color: var(--border); +} + +.label { + color: var(--text); +} + +.row[data-disabled] { + opacity: 0.4; + cursor: not-allowed; +} + +.iconCheck { + width: 12px; + height: 12px; + stroke-width: 2.5; +} + +.list { + display: flex; + flex-direction: column; + gap: 6px; +} diff --git a/bebop/components/dialog.module.css b/bebop/components/dialog.module.css new file mode 100644 index 0000000000000..a02d7e07604cf --- /dev/null +++ b/bebop/components/dialog.module.css @@ -0,0 +1,121 @@ +.surface { + position: fixed; + inset: 0; + margin: auto; + width: min(96vw, 480px); + max-height: 90vh; + border: 1px solid var(--border); + border-radius: var(--radius-2xl); + background: var(--bg-elev); + color: var(--text); + padding: 0; + overflow: hidden; + box-shadow: var(--shadow-5); +} + +.surface::backdrop { + background: rgba(10, 10, 10, 0.4); + backdrop-filter: blur(2px); +} + +.body { + padding: 24px 24px 8px; + overflow-y: auto; +} + +.title { + margin: 0 0 8px; + font-family: var(--font-display); + font-size: 18px; + font-weight: 700; + letter-spacing: var(--tracking-heading); +} + +.copy { + margin: 0; + color: var(--text-muted); + font-size: 13.5px; + line-height: 1.6; +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 16px 24px 20px; +} + +.alertSurface { + width: min(94vw, 400px); +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + height: 32px; + padding: 0 14px; + border-radius: var(--radius-pill); + border: none; + background: var(--surface-muted); + color: var(--text); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard); +} + +.btn:hover { + background: var(--surface-sunken); +} + +.primary { + background: var(--accent); + color: var(--accent-contrast); +} + +.primary:hover { + background: var(--accent-strong); +} + +.danger { + background: var(--brand); + color: white; +} + +.danger:hover { + background: var(--brand-strong); +} + +.dialog-module__row--0\+lMS { + display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap; +} + +/* Demo helpers (used by Storybook examples) */ + +.demoSpacer { + margin-bottom: 12px; +} + +.demoSpacerLg { + margin-bottom: 16px; +} + +.demoCol- { + display: flex; + + flex-direction: column; + + gap: 12px; +} + +.demoNote { + align-self: center; + + color: var(--text-muted); + + font-size: 13px; +} diff --git a/bebop/components/divider.module.css b/bebop/components/divider.module.css new file mode 100644 index 0000000000000..6a268f205bedf --- /dev/null +++ b/bebop/components/divider.module.css @@ -0,0 +1,181 @@ +/* Headless Divider renders {children}. + The line itself comes from ::before / ::after on the root. */ + +.divider-module__divider--Qmh\+f { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + color: var(--text-faint); +} + +.divider-module__divider--Qmh\+f::before, +.divider-module__divider--Qmh\+f::after { + content: ''; + flex: 1; + height: 1px; + background: var(--border); +} + +/* Start: short 8 px stub before content, full line after. */ +.divider-module__divider--Qmh\+f.start::before { + flex: 0 0 8px; +} + +/* End: full line before content, short 8 px stub after. */ +.divider-module__divider--Qmh\+f.end::after { + flex: 0 0 8px; +} + +.label { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 11px; + font-weight: 500; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-faint); + white-space: nowrap; +} + +.label::before { + content: ''; + width: 8px; + height: 8px; + border-radius: 50%; + border: 1.5px solid var(--border-stronger); +} + +/* Sentence-case content (icon + text) used inside dividers. */ +.content- { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: var(--text-muted); + white-space: nowrap; +} + +.content- svg { + width: 14px; + height: 14px; +} + +/* Vertical: stack lines top/bottom of label, swap orientation. */ +.dividerVertical { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + height: 100%; + width: auto; + min-width: 80px; +} + +.dividerVertical::before, +.dividerVertical::after { + content: ''; + flex: 1; + width: 1px; + height: auto; + min-height: 8px; + background: var(--border); +} + +/* Start: 8 px stub above content, full line below. */ +.dividerVertical.start::before { + flex: 0 0 8px; +} + +/* End: full line above content, 8 px stub below. */ +.dividerVertical.end::after { + flex: 0 0 8px; +} + +/* Plain hairline (no label) */ +.horizontal { + width: 100%; + height: 1px; + background: var(--border); +} + +.vertical { + width: 1px; + background: var(--border); +} + +.row { + display: flex; + align-items: center; + gap: 16px; + height: 24px; + font-size: 13px; + color: var(--text-muted); +} + +.column { + display: flex; + flex-direction: column; + gap: 14px; + width: 100%; +} + +.section { + font-size: 13px; + color: var(--text); +} + +.verticalGroup- { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + height: 180px; + width: 100%; +} + +.verticalCol { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; + gap: 6px; +} + +.verticalCaption { + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-faint); +} + +.verticalLineWrap { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + width: 100%; +} + +.labelled { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + text-align: center; +} + +.labelledLine { + flex: 1; + height: 1px; + background: var(--border); +} + +.divider-module__labelledText--L\+wxQ { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-faint); +} diff --git a/bebop/components/field.module.css b/bebop/components/field.module.css new file mode 100644 index 0000000000000..438712e3c2500 --- /dev/null +++ b/bebop/components/field.module.css @@ -0,0 +1,54 @@ +.field { + display: flex; + flex-direction: column; + gap: 6px; + width: 100%; +} + +.label { + font-size: 13px; + font-weight: 500; + color: var(--text); +} + +.field-module__hint--jh\+Qf { + font-size: 12px; + color: var(--text-muted); +} + +.message { + font-size: 12px; + display: inline-flex; + align-items: center; + gap: 4px; +} + +.messageError { + color: var(--brand); +} + +.messageWarning { + color: var(--warning); +} + +.field-module__messageSuccess--H\+7ls { + color: var(--success); +} + +.messageInfo { + color: var(--info); +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 20px; + + width: 100%; + + max-width: 360px; +} diff --git a/bebop/components/input.module.css b/bebop/components/input.module.css new file mode 100644 index 0000000000000..9f4474fc2b133 --- /dev/null +++ b/bebop/components/input.module.css @@ -0,0 +1,85 @@ +/* Bebop input wrapper โ€” clean rounded, light border */ +.wrap { + display: flex; + align-items: center; + width: 100%; + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius-md); + transition: border-color var(--duration-fast) var(--ease-standard), + box-shadow var(--duration-fast) var(--ease-standard); +} + +.wrap:hover { + border-color: var(--border-strong); +} + +.wrap:has(:focus-visible), +.wrap:focus-within { + border-color: var(--text); + box-shadow: 0 0 0 3px var(--surface-muted); +} + +.wrap[data-disabled], +.wrapDisabled { + background: var(--surface-muted); + cursor: not-allowed; + opacity: 0.7; +} + +.wrapError { + border-color: var(--brand); +} + +.wrapError:has(:focus-visible) { + box-shadow: 0 0 0 3px var(--brand-soft); +} + +.input { + flex: 1; + border: none; + outline: none; + background: transparent; + padding: 8px 12px; + font-size: 13.5px; + color: var(--text); + min-width: 0; +} + +.input::placeholder { + color: var(--text-faint); +} + +.input:disabled { + cursor: not-allowed; +} + +.input-module__affix--y\+IXS { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 10px; + color: var(--text-soft); + flex-shrink: 0; + font-size: 13px; +} + +.affixIcon { + width: 16px; + height: 16px; +} + +.column { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + width: 100%; + + max-width: 360px; +} diff --git a/bebop/components/link.module.css b/bebop/components/link.module.css new file mode 100644 index 0000000000000..4789eea5bd0e0 --- /dev/null +++ b/bebop/components/link.module.css @@ -0,0 +1,50 @@ +.link { + color: var(--text); + text-decoration: underline; + text-decoration-color: var(--border-strong); + text-underline-offset: 3px; + font-weight: 500; + border-radius: var(--radius-xs); + cursor: pointer; + transition: color var(--duration-fast) var(--ease-standard), + text-decoration-color var(--duration-fast) var(--ease-standard); +} + +.link:hover { + color: var(--text); + text-decoration-color: var(--text); +} + +.link:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.link[data-disabled] { + color: var(--text-faint); + cursor: not-allowed; + text-decoration-color: var(--text-faint); +} + +.inline { + text-decoration: underline; + text-underline-offset: 3px; +} + +.paragraph { + margin: 0; + font-size: 14.5px; + line-height: 1.65; + color: var(--text-muted); + max-width: 56ch; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 16px; +} diff --git a/bebop/components/message-bar.module.css b/bebop/components/message-bar.module.css new file mode 100644 index 0000000000000..8061f33256bc9 --- /dev/null +++ b/bebop/components/message-bar.module.css @@ -0,0 +1,125 @@ +.bar- { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 12px; + width: 100%; + padding: 8px 12px; + border-radius: var(--radius-pill); + background: var(--bg-elev); + border: 1px solid var(--border); + font-size: 13px; + line-height: 1.45; +} + +.bar-[data-layout='multiline'] { + grid-template-columns: auto 1fr; + border-radius: var(--radius-lg); + padding: 12px 16px; +} + +.bar-[data-layout='multiline'] .actions { + grid-column: 2; + justify-self: end; +} + +.message-bar-module__icon--B5\+ob { + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + flex-shrink: 0; +} + +.message-bar-module__icon--B5\+ob svg { + width: 16px; + height: 16px; +} + +.body { + color: var(--text); + min-width: 0; +} + +.title { + font-weight: 600; + margin-right: 4px; +} + +.actions { + display: inline-flex; + align-items: center; + gap: 4px; +} + +.actionBtn { + height: 24px; + padding: 0 10px; + border-radius: var(--radius-pill); + font-size: 12px; + font-weight: 500; + background: transparent; + color: var(--text); + border: none; + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard); +} + +.actionBtn:hover { + background: color-mix(in srgb, var(--text) 8%, transparent); +} + +.iconBtn { + width: 24px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.iconBtn svg { + width: 12px; + height: 12px; +} + +/* Intent variants โ€” subtle pastel backgrounds */ +.info { + background: var(--info-soft); + border-color: transparent; +} +.info .message-bar-module__icon--B5\+ob { + color: var(--info); +} + +.success { + background: var(--success-soft); + border-color: transparent; +} +.success .message-bar-module__icon--B5\+ob { + color: var(--success); +} + +.message-bar-module__warning--8iN\+a { + background: var(--warning-soft); + border-color: transparent; +} +.message-bar-module__warning--8iN\+a .message-bar-module__icon--B5\+ob { + color: var(--warning); +} + +.danger { + background: var(--brand-soft); + border-color: transparent; +} +.danger .message-bar-module__icon--B5\+ob { + color: var(--brand); +} + +.list { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; +} diff --git a/bebop/components/progress-bar.module.css b/bebop/components/progress-bar.module.css new file mode 100644 index 0000000000000..6ccb1cb34ac73 --- /dev/null +++ b/bebop/components/progress-bar.module.css @@ -0,0 +1,82 @@ +.bar { + position: relative; + width: 100%; + height: 4px; + border-radius: var(--radius-pill); + background: var(--surface-sunken); + overflow: hidden; +} + +.fill { + height: 100%; + border-radius: inherit; + background: var(--accent); + transition: width var(--duration-medium) var(--ease-standard); +} + +.progress-bar-module__success--yG\+Ae .fill { + background: var(--success); +} + +.warning .fill { + background: var(--warning); +} + +.danger .fill { + background: var(--brand); +} + +.indeterminate { + position: relative; + overflow: hidden; +} + +.indeterminate .fill { + position: absolute; + inset: 0; + width: 35%; + animation: slide 1.4s ease-in-out infinite; +} + +.row { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.label { + display: flex; + justify-content: space-between; + font-size: 12.5px; + color: var(--text-muted); +} + +.label strong { + color: var(--text); + font-weight: 500; +} + +@keyframes slide { + 0% { + transform: translateX(-100%); + } + 50% { + transform: translateX(180%); + } + 100% { + transform: translateX(280%); + } +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 24px; + + max-width: 360px; +} diff --git a/bebop/components/radio-group.module.css b/bebop/components/radio-group.module.css new file mode 100644 index 0000000000000..b8914ef026bd0 --- /dev/null +++ b/bebop/components/radio-group.module.css @@ -0,0 +1,88 @@ +.group { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.row { + position: relative; + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 14px; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--bg-elev); + cursor: pointer; + transition: border-color var(--duration-fast) var(--ease-standard), + background var(--duration-fast) var(--ease-standard); +} + +.row:hover { + border-color: var(--border-strong); +} + +.row[data-disabled] { + cursor: not-allowed; + opacity: 0.4; +} + +.input { + position: absolute; + inset: 0; + opacity: 0; + cursor: pointer; + margin: 0; +} + +.indicator { + width: 18px; + height: 18px; + border-radius: 50%; + border: 1.5px solid var(--border-stronger); + background: var(--bg-elev); + position: relative; + flex-shrink: 0; + margin-top: 1px; + transition: border-color var(--duration-fast) var(--ease-standard); +} + +.input:checked + .indicator { + border-color: var(--accent); + border-width: 5px; + background: var(--bg-elev); +} + +.input:focus-visible + .indicator { + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.row:has(.input:checked) { + border-color: var(--accent); + background: var(--bg-elev); +} + +.text { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; +} + +.title { + font-weight: 500; + color: var(--text); + font-size: 13.5px; +} + +.subtitle { + font-size: 12px; + color: var(--text-muted); +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 360px; +} diff --git a/bebop/components/rating-display.module.css b/bebop/components/rating-display.module.css new file mode 100644 index 0000000000000..5c515ebcfac95 --- /dev/null +++ b/bebop/components/rating-display.module.css @@ -0,0 +1,56 @@ +.display { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--text); + font-size: 13.5px; +} + +.display [data-appearance] { + width: 18px; + height: 18px; + position: relative; + display: inline-flex; +} + +.display [data-appearance] svg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.icon { + width: 18px; + height: 18px; +} + +.display [data-appearance='filled'] .rating-display-module__iconHalf--\+w-kN, +.display [data-appearance='filled'] .iconOutline { + visibility: hidden; +} + +.display [data-appearance='filled-half'] .iconFilled, +.display [data-appearance='filled-half'] .iconOutline { + visibility: hidden; +} + +.display [data-appearance='outline'] .iconFilled, +.display [data-appearance='outline'] .rating-display-module__iconHalf--\+w-kN { + visibility: hidden; +} + +.display [data-appearance='outline'] .iconOutline { + color: var(--border-strong); +} + +.value { + color: var(--text); + font-weight: 500; + font-variant-numeric: tabular-nums; +} + +.count { + color: var(--text-muted); + font-size: 12.5px; +} diff --git a/bebop/components/rating.module.css b/bebop/components/rating.module.css new file mode 100644 index 0000000000000..35a97e4ea4b2e --- /dev/null +++ b/bebop/components/rating.module.css @@ -0,0 +1,44 @@ +.rating { + display: inline-flex; + align-items: center; + gap: 2px; + color: var(--text); +} + +.item { + position: relative; + display: inline-flex; +} + +.input { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; + margin: 0; +} + +.input:focus-visible ~ svg { + filter: drop-shadow(0 0 0 var(--accent)) drop-shadow(0 0 3px var(--brand)); +} + +.icon { + width: 22px; + height: 22px; + color: inherit; +} + +.rating-module__row--J7o\+R { + display: flex; + align-items: center; + gap: 12px; +} + +.value { + font-weight: 500; + color: var(--text); + font-size: 13px; + font-variant-numeric: tabular-nums; +} diff --git a/bebop/components/select.module.css b/bebop/components/select.module.css new file mode 100644 index 0000000000000..1aab37e6bbc28 --- /dev/null +++ b/bebop/components/select.module.css @@ -0,0 +1,53 @@ +.wrap { + position: relative; + display: inline-block; + width: 100%; +} + +.select { + width: 100%; + appearance: none; + -webkit-appearance: none; + border: 1px solid var(--border); + background: var(--bg-elev); + color: var(--text); + border-radius: var(--radius-md); + padding: 8px 36px 8px 12px; + font-size: 13.5px; + cursor: pointer; + transition: border-color var(--duration-fast) var(--ease-standard), + box-shadow var(--duration-fast) var(--ease-standard); +} + +.select:hover { + border-color: var(--border-strong); +} + +.select:focus-visible { + outline: none; + border-color: var(--text); + box-shadow: 0 0 0 3px var(--surface-muted); +} + +.select:disabled { + background: var(--surface-muted); + cursor: not-allowed; + opacity: 0.6; +} + +.icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 14px; + height: 14px; + pointer-events: none; + color: var(--text-soft); +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 360px; +} diff --git a/bebop/components/skeleton.module.css b/bebop/components/skeleton.module.css new file mode 100644 index 0000000000000..091a7aa54e3ea --- /dev/null +++ b/bebop/components/skeleton.module.css @@ -0,0 +1,64 @@ +.card\+ { + display: flex; + flex-direction: column; + gap: 14px; + width: 100%; +} + +.row { + display: flex; + gap: 14px; + align-items: center; +} + +.bar { + height: 12px; + border-radius: var(--radius-xs); + background: linear-gradient(90deg, var(--surface-muted) 0%, var(--surface-sunken) 50%, var(--surface-muted) 100%); + background-size: 200% 100%; + animation: shimmer 1.6s linear infinite; +} + +.circle { + width: 44px; + height: 44px; + border-radius: 50%; +} + +.line40 { + width: 40%; +} + +.line60 { + width: 60%; +} + +.line100 { + width: 100%; +} + +.thumb { + height: 140px; + border-radius: var(--radius-lg); +} + +@keyframes shimmer { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 400px; +} + +.demoFlex { + flex: 1; + + gap: 8px; +} diff --git a/bebop/components/slider.module.css b/bebop/components/slider.module.css new file mode 100644 index 0000000000000..d3d5bf3e826d1 --- /dev/null +++ b/bebop/components/slider.module.css @@ -0,0 +1,87 @@ +.slider { + position: relative; + width: 100%; + max-width: 320px; + height: 28px; + display: flex; + align-items: center; +} + +.input { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + cursor: pointer; + z-index: 2; + margin: 0; +} + +.input:disabled { + cursor: not-allowed; +} + +.slider-module__rail--Y\+av4 { + position: relative; + width: 100%; + height: 4px; + border-radius: var(--radius-pill); + background: var(--surface-sunken); + overflow: hidden; +} + +.slider-module__rail--Y\+av4::after { + content: ''; + position: absolute; + inset: 0; + width: var(--fui-Slider--progress, 0%); + background: var(--accent); + border-radius: inherit; + transition: width var(--duration-fast) var(--ease-standard); +} + +.thumb { + position: absolute; + top: 50%; + left: var(--fui-Slider--progress, 0%); + transform: translate(-50%, -50%); + width: 18px; + height: 18px; + border-radius: 50%; + background: var(--bg-elev); + border: 2px solid var(--accent); + box-shadow: var(--shadow-2); + transition: transform 80ms var(--ease-standard), border-color var(--duration-fast) var(--ease-standard); +} + +.input:focus-visible ~ .thumb, +.input:focus-visible + .thumb { + box-shadow: 0 0 0 4px var(--surface-muted), var(--shadow-2); +} + +.input:active ~ .thumb { + transform: translate(-50%, -50%) scale(1.12); +} + +.row { + display: flex; + align-items: center; + gap: 16px; + width: 100%; +} + +.value { + font-variant-numeric: tabular-nums; + font-weight: 500; + color: var(--text); + min-width: 38px; + text-align: right; + font-size: 13px; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 360px; +} diff --git a/bebop/components/spin-button.module.css b/bebop/components/spin-button.module.css new file mode 100644 index 0000000000000..9508b4f67911d --- /dev/null +++ b/bebop/components/spin-button.module.css @@ -0,0 +1,83 @@ +.wrap { + position: relative; + display: inline-flex; + align-items: center; + width: 160px; + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius-md); + overflow: hidden; + transition: border-color var(--duration-fast) var(--ease-standard), + box-shadow var(--duration-fast) var(--ease-standard); +} + +.wrap:hover { + border-color: var(--border-strong); +} + +.wrap:has(:focus-visible) { + border-color: var(--text); + box-shadow: 0 0 0 3px var(--surface-muted); +} + +.input { + flex: 1; + border: none; + outline: none; + background: transparent; + font-size: 13.5px; + font-variant-numeric: tabular-nums; + font-weight: 500; + text-align: center; + padding: 8px 36px 8px 12px; + min-width: 0; + color: var(--text); +} + +.btn { + position: absolute; + right: 0; + width: 28px; + height: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + border-left: 1px solid var(--border); + background: var(--bg-elev); + color: var(--text-muted); + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.btn:hover { + background: var(--surface-muted); + color: var(--text); +} + +.btn:focus-visible { + outline: none; + z-index: 1; + box-shadow: inset 0 0 0 2px var(--accent); +} + +.btnUp { + top: 0; + border-bottom: 1px solid var(--border); +} + +.btnDown { + bottom: 0; +} + +.icon { + width: 11px; + height: 11px; + stroke-width: 2; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 240px; +} diff --git a/bebop/components/spinner.module.css b/bebop/components/spinner.module.css new file mode 100644 index 0000000000000..7775ac2ed5c03 --- /dev/null +++ b/bebop/components/spinner.module.css @@ -0,0 +1,63 @@ +.spinner { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: var(--text-muted); +} + +.tail { + display: inline-flex; + width: 20px; + height: 20px; + color: var(--accent); + animation: spin 800ms linear infinite; +} + +.tail svg { + width: 100%; + height: 100%; +} + +.large .tail { + width: 32px; + height: 32px; +} + +.muted .tail { + color: var(--text-muted); +} + +.column { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/* Demo helpers (used by Storybook examples) */ + +.demoRow { + display: flex; + + align-items: center; + + gap: 32px; +} + +.demoCol { + display: flex; + + flex-direction: column; + + gap: 16px; +} diff --git a/bebop/components/switch.module.css b/bebop/components/switch.module.css new file mode 100644 index 0000000000000..3bc3c402bbe33 --- /dev/null +++ b/bebop/components/switch.module.css @@ -0,0 +1,87 @@ +.row { + position: relative; + display: inline-flex; + align-items: center; + gap: 12px; + cursor: pointer; + font-size: 13.5px; + user-select: none; + color: var(--text); +} + +.row[data-disabled] { + cursor: not-allowed; + opacity: 0.4; +} + +.input { + position: absolute; + inset: 0; + width: 38px; + height: 22px; + opacity: 0; + cursor: pointer; + z-index: 1; +} + +.indicator { + position: relative; + width: 38px; + height: 22px; + border-radius: var(--radius-pill); + background: var(--surface-sunken); + border: 1px solid var(--border); + flex-shrink: 0; + transition: background var(--duration-medium) var(--ease-standard), + border-color var(--duration-medium) var(--ease-standard); +} + +.indicator::after { + content: ''; + position: absolute; + top: 1px; + left: 1px; + width: 18px; + height: 18px; + border-radius: 50%; + background: var(--bg-elev); + box-shadow: var(--shadow-2); + transition: transform var(--duration-medium) var(--ease-emphasized); +} + +.input:checked + .indicator { + background: var(--accent); + border-color: var(--accent); +} + +.input:checked + .indicator::after { + transform: translateX(16px); + background: var(--accent-contrast); +} + +.input:focus-visible + .indicator { + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.label { + display: flex; + flex-direction: column; + gap: 2px; +} + +.title { + font-weight: 500; + color: var(--text); +} + +.subtitle { + font-size: 12px; + color: var(--text-muted); +} + +.list { + display: flex; + flex-direction: column; + gap: 14px; + align-items: flex-start; +} diff --git a/bebop/components/tab-list.module.css b/bebop/components/tab-list.module.css new file mode 100644 index 0000000000000..a29d31454ae28 --- /dev/null +++ b/bebop/components/tab-list.module.css @@ -0,0 +1,86 @@ +.tabs- { + display: flex; + gap: 4px; + padding: 4px; + background: var(--surface-muted); + border-radius: var(--radius-pill); +} + +.tab-list-module__tabsVertical--HTX\+H { + flex-direction: column; + border-radius: var(--radius-lg); + width: 200px; + align-self: flex-start; +} + +.tab { + position: relative; + padding: 7px 14px; + background: transparent; + border: none; + color: var(--text-muted); + font-size: 13px; + font-weight: 500; + cursor: pointer; + border-radius: var(--radius-pill); + text-align: left; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.tab-list-module__tabsVertical--HTX\+H .tab { + border-radius: var(--radius-md); +} + +.tab:hover { + color: var(--text); +} + +.tab:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.tab[data-selected] { + background: var(--bg-elev); + color: var(--text); + box-shadow: var(--shadow-1); +} + +.layout { + display: flex; + flex-direction: column; + gap: 16px; + width: 100%; +} + +.layoutVertical { + flex-direction: row; + align-items: stretch; + gap: 24px; +} + +.panel { + padding: 16px 4px 4px; + font-size: 13px; + color: var(--text-muted); + line-height: 1.6; + flex: 1; +} + +.layoutVertical .panel { + padding: 4px 0 0; +} + +.panelTitle { + margin: 0 0 6px; + color: var(--text); + font-size: 14px; + font-weight: 600; + letter-spacing: var(--tracking-tight); +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + max-width: 520px; +} diff --git a/bebop/components/textarea.module.css b/bebop/components/textarea.module.css new file mode 100644 index 0000000000000..34f27c0f2d656 --- /dev/null +++ b/bebop/components/textarea.module.css @@ -0,0 +1,54 @@ +.wrap { + display: flex; + width: 100%; + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 8px 12px; + transition: border-color var(--duration-fast) var(--ease-standard), + box-shadow var(--duration-fast) var(--ease-standard); +} + +.wrap:hover { + border-color: var(--border-strong); +} + +.wrap:has(:focus-visible) { + border-color: var(--text); + box-shadow: 0 0 0 3px var(--surface-muted); +} + +.textarea { + width: 100%; + min-height: 96px; + border: none; + outline: none; + background: transparent; + resize: vertical; + font-size: 13.5px; + line-height: 1.55; + color: var(--text); + font-family: inherit; +} + +.textarea::placeholder { + color: var(--text-faint); +} + +.noResize { + resize: none; +} + +/* Demo helpers (used by Storybook examples) */ + +.demo\+ { + display: flex; + + flex-direction: column; + + gap: 16px; + + width: 100%; + + max-width: 360px; +} diff --git a/bebop/components/toggle-button.module.css b/bebop/components/toggle-button.module.css new file mode 100644 index 0000000000000..0eeb49f7dea6e --- /dev/null +++ b/bebop/components/toggle-button.module.css @@ -0,0 +1,114 @@ +.toggle-button-module__toggle--ob\+SX { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + height: 32px; + padding: 0 14px; + border-radius: var(--radius-pill); + border: 1px solid var(--border); + background: var(--bg-elev); + color: var(--text); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard), + border-color var(--duration-fast) var(--ease-standard); +} + +.toggle-button-module__toggle--ob\+SX:hover { + background: var(--surface-muted); + border-color: var(--border-strong); +} + +.toggle-button-module__toggle--ob\+SX:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); +} + +.toggle-button-module__toggle--ob\+SX[data-checked] { + background: var(--accent); + border-color: var(--accent); + color: var(--accent-contrast); +} + +.toggle-button-module__toggle--ob\+SX[data-checked]:hover { + background: var(--accent-strong); + border-color: var(--accent-strong); +} + +.toggle-button-module__toggle--ob\+SX[data-disabled] { + opacity: 0.4; + cursor: not-allowed; +} + +.icon { + width: 14px; + height: 14px; +} + +.iconOnly { + width: 32px; + padding: 0; +} + +/* Segmented group โ€” single bordered shell, dividers between cells */ +.group { + display: inline-flex; + border: 1px solid var(--border); + border-radius: var(--radius-pill); + background: var(--bg-elev); + padding: 2px; + gap: 2px; +} + +.groupItem { + height: 28px; + width: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + color: var(--text-muted); + cursor: pointer; + border-radius: var(--radius-pill); + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.groupItem:hover { + background: var(--surface-muted); + color: var(--text); +} + +.groupItem[data-checked] { + background: var(--accent); + color: var(--accent-contrast); +} + +.groupItem[data-checked]:hover { + background: var(--accent-strong); +} + +.groupItem:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 3px var(--accent); +} + +/* Demo helpers (used by Storybook examples) */ + +.demo { + display: flex; + + flex-direction: column; + + gap: 16px; +} + +.demoRow { + display: flex; + + gap: 12px; + + align-items: center; +} diff --git a/bebop/components/toolbar.module.css b/bebop/components/toolbar.module.css new file mode 100644 index 0000000000000..4ec7108b4d1b4 --- /dev/null +++ b/bebop/components/toolbar.module.css @@ -0,0 +1,87 @@ +.toolbar { + display: inline-flex; + align-items: center; + gap: 2px; + padding: 4px; + border-radius: var(--radius-pill); + background: var(--bg-elev); + border: 1px solid var(--border); + box-shadow: var(--shadow-1); +} + +.toolbar[data-vertical] { + flex-direction: column; + border-radius: var(--radius-lg); +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: var(--radius-pill); + background: transparent; + color: var(--text-muted); + border: none; + cursor: pointer; + transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); +} + +.btn:hover { + background: var(--surface-muted); + color: var(--text); +} + +.btn:focus-visible { + outline: none; + box-shadow: inset 0 0 0 2px var(--accent); +} + +.btn[data-checked] { + background: var(--accent); + color: var(--accent-contrast); +} + +.btn[data-checked]:hover { + background: var(--accent-strong); + color: var(--accent-contrast); +} + +.btn[data-disabled] { + opacity: 0.4; + cursor: not-allowed; +} + +.btnActive { + background: var(--accent); + color: var(--accent-contrast); +} + +.toolbar-module__divider--lj\+su { + width: 1px; + align-self: stretch; + margin: 4px 4px; + background: var(--border); +} + +.toolbar[data-vertical] .toolbar-module__divider--lj\+su { + width: auto; + height: 1px; + margin: 4px 4px; +} + +.group { + display: inline-flex; + align-items: center; + gap: 2px; +} + +.toolbar[data-vertical] .group { + flex-direction: column; +} + +.icon { + width: 16px; + height: 16px; +} diff --git a/bebop/tokens.css b/bebop/tokens.css new file mode 100644 index 0000000000000..17368ac44c6ed --- /dev/null +++ b/bebop/tokens.css @@ -0,0 +1,209 @@ +/* ------------------------------------------------------------------ + * Bebop design tokens + * + * Mapped from FOUNDATIONS pages of the Bebop web components Figma file: + * - color algorithm/primitives/generics + * - elevation + * - gap & padding generics + * - stroke primitives/generics + * - border radius generic + * ----------------------------------------------------------------*/ +:root { + /* surface */ + --bg: #ffffff; + --bg-soft: #f7f7f8; + --bg-elev: #ffffff; + --bg-elev-2: #fafafa; + --surface-muted: #f2f2f4; + --surface-sunken: #ededf0; + + /* line */ + --border: #e4e4e7; + --border-strong: #d4d4d8; + --border-stronger: #a1a1aa; + + /* ink */ + --text: #0a0a0a; + --text-muted: #52525b; + --text-soft: #71717a; + --text-faint: #a1a1aa; + --text-on-accent: #ffffff; + + /* accent โ€” primary action is rich Bebop magenta on white (Figma --prmt-color-red-45) */ + --accent: #9b1f5a; + --accent-strong: #7a1a4a; + --accent-soft: #fff1f3; + --accent-contrast: #ffffff; + + /* brand โ€” signature magenta, used sparingly for hot states */ + --brand: #a81f6a; + --brand-strong: #7a1a4a; + --brand-soft: #fff1f3; + + /* status (subtle pastels per Message Bar spec) */ + --success: #2e7d32; + --success-soft: #e8f5e9; + --warning: #b56e00; + --warning-soft: #fff4dc; + --danger: #c62828; + --danger-soft: #fdecea; + --info: #0d47a1; + --info-soft: #eaf2fb; + + /* elevation (light) โ€” formula N=2E, R=elevation index */ + --shadow-1: 0 0 0 1px rgba(0, 0, 0, 0.02), 0 2px 2px rgba(0, 0, 0, 0.03); + --shadow-2: 0 0 0 1px rgba(0, 0, 0, 0.02), 0 4px 6px rgba(0, 0, 0, 0.04); + --shadow-3: 0 1px 0 rgba(0, 0, 0, 0.02), 0 8px 12px rgba(0, 0, 0, 0.06); + --shadow-4: 0 1px 0 rgba(0, 0, 0, 0.02), 0 16px 24px rgba(0, 0, 0, 0.08); + --shadow-5: 0 1px 0 rgba(0, 0, 0, 0.03), 0 20px 40px rgba(0, 0, 0, 0.1); + --shadow-6: 0 1px 0 rgba(0, 0, 0, 0.04), 0 32px 64px rgba(0, 0, 0, 0.16); + + /* legacy aliases */ + --shadow-sm: var(--shadow-1); + --shadow-md: var(--shadow-3); + --shadow-lg: var(--shadow-5); + + /* radius โ€” atomic / composite / layout */ + --radius-xs: 4px; /* badges, small chips */ + --radius-sm: 6px; + --radius-md: 8px; /* buttons (when not pill), small inputs */ + --radius-lg: 12px; /* composite */ + --radius-xl: 16px; + --radius-2xl: 20px; /* cards, dialogs */ + --radius-3xl: 24px; + --radius-pill: 999px; + + /* stroke widths */ + --stroke-thin: 1px; + --stroke-thick: 2px; + --stroke-thicker: 3px; + + /* spacing โ€” atomic, composite, layout */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + + /* type ramp (web) โ€” derived from "Typography primitives - web" */ + --font-sans: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --font-display: 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif; + + --tracking-display: -0.03em; + --tracking-heading: -0.02em; + --tracking-tight: -0.01em; + + /* motion */ + --ease-standard: cubic-bezier(0.2, 0.7, 0.3, 1); + --ease-emphasized: cubic-bezier(0.32, 0.72, 0, 1); + --duration-fast: 120ms; + --duration-medium: 200ms; + --duration-slow: 320ms; +} + +[data-theme='dark'] { + --bg: #09090b; + --bg-soft: #0e0e10; + --bg-elev: #131316; + --bg-elev-2: #18181b; + --surface-muted: #1f1f23; + --surface-sunken: #0e0e10; + + --border: #26262a; + --border-strong: #3a3a40; + --border-stronger: #52525b; + + --text: #fafafa; + --text-muted: #a1a1aa; + --text-soft: #71717a; + --text-faint: #52525b; + --text-on-accent: #ffffff; + + --accent: #ec4899; + --accent-strong: #db2777; + --accent-soft: #3b1525; + --accent-contrast: #ffffff; + + --brand: #f472b6; + --brand-strong: #ec4899; + --brand-soft: #3b1525; + + --success: #4ade80; + --success-soft: #14361f; + --warning: #fbbf24; + --warning-soft: #3a2a08; + --danger: #f87171; + --danger-soft: #3a1414; + --info: #60a5fa; + --info-soft: #11243f; + + /* dark elevation: opacity values double per spec */ + --shadow-1: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 2px 2px rgba(0, 0, 0, 0.06); + --shadow-2: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 4px 6px rgba(0, 0, 0, 0.08); + --shadow-3: 0 1px 0 rgba(255, 255, 255, 0.04), 0 8px 12px rgba(0, 0, 0, 0.45); + --shadow-4: 0 1px 0 rgba(255, 255, 255, 0.04), 0 16px 24px rgba(0, 0, 0, 0.5); + --shadow-5: 0 1px 0 rgba(255, 255, 255, 0.06), 0 20px 40px rgba(0, 0, 0, 0.55); + --shadow-6: 0 1px 0 rgba(255, 255, 255, 0.08), 0 32px 64px rgba(0, 0, 0, 0.72); +} + +* { + box-sizing: border-box; +} + +html, +body, +#root { + margin: 0; + padding: 0; + height: 100%; +} + +body { + font-family: var(--font-sans); + background: var(--bg); + color: var(--text); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: 'cv11', 'ss01', 'ss02'; + font-size: 14px; + line-height: 1.45; + letter-spacing: var(--tracking-tight); + transition: background-color var(--duration-medium) var(--ease-standard), + color var(--duration-medium) var(--ease-standard); +} + +a { + color: inherit; +} + +code { + font-family: var(--font-mono); + font-size: 0.86em; + background: var(--surface-muted); + padding: 0.1em 0.4em; + border-radius: var(--radius-xs); + letter-spacing: 0; +} + +button, +input, +textarea, +select { + font: inherit; + color: inherit; +} + +button { + cursor: pointer; +} + +::selection { + background: var(--accent); + color: var(--accent-contrast); +} diff --git a/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts b/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts index e6d370c3c07a3..32d765bd7d412 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts +++ b/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts @@ -53,7 +53,20 @@ export function modifyImportsPlugin(babel: typeof Babel, options: BabelPluginOpt const isRelativeImportToIndexBarrel = importSource.value.endsWith('./index'); if (isRelativeImport && !isRelativeImportToIndexBarrel) { - if (process.env.NODE_ENV !== 'production') { + // CSS Modules, raw-loader queries, and stories' co-located + // source-extraction helpers (`withStorySource`, + // `withCssModuleSource`) are stripped from the sandbox bundle on + // purpose โ€” sister utilities recover them through other channels + // (e.g. `parameters.exportToSandbox.transformFiles`). Skipping the + // warning for these well-known patterns keeps dev console output + // clean without changing extraction behavior. + const isKnownSafeRelative = + /\.module\.css(\?|$)/.test(importSource.value) || + /\?raw$/.test(importSource.value) || + /\/with(Story|CssModule)Source$/.test(importSource.value) || + /\.stories(\?raw)?$/.test(importSource.value); + + if (process.env.NODE_ENV !== 'production' && !isKnownSafeRelative) { console.warn( [ `๐Ÿšจ Relative import '${importSource.value}' found in ${pluginState.filename} - removing from output - this might create invalid code.`, diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js index 67905c6bfe15f..c19811c405d5b 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js @@ -1,5 +1,74 @@ +const path = require('path'); + const rootMain = require('../../../../../.storybook/main'); +const repoRoot = path.resolve(__dirname, '../../../../..'); +const tokensDir = path.resolve(repoRoot, 'bebop'); +const headlessStoriesDir = path.resolve(__dirname, '..'); + +/** + * CSS Modules webpack rule for the per-package storybook. Mirrors the docsite + * at apps/public-docsite-v9-headless/.storybook/main.js โ€” see that file for + * the design rationale (default Storybook rule handles plain CSS; we narrow + * it to skip `.module.css` and add a CSS-Modules rule). + */ +const cssIncludes = [tokensDir, headlessStoriesDir]; + +const cssModuleRule = { + test: /\.module\.css$/, + include: cssIncludes, + // Skip `?raw` imports โ€” see the docsite at + // apps/public-docsite-v9-headless/.storybook/main.js for the rationale. + resourceQuery: { not: [/raw/] }, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]__[local]--[hash:base64:5]', + }, + importLoaders: 0, + }, + }, + ], +}; + +/** + * Mirrors `patchRules` in the docsite config โ€” see that file's comment for + * the full rationale (CSS Modules carve-out + `?raw` resourceQuery skip). + */ +function patchRules(rules) { + for (const rule of rules) { + if (!rule || typeof rule !== 'object') continue; + const test = rule.test; + const isRegExp = test instanceof RegExp; + const matchesPlainCss = isRegExp && test.source === /\.css$/.source; + if (matchesPlainCss) { + const existing = rule.exclude; + const moduleRegex = /\.module\.css$/; + if (Array.isArray(existing)) { + rule.exclude = [...existing, moduleRegex]; + } else if (existing) { + rule.exclude = [existing, moduleRegex]; + } else { + rule.exclude = moduleRegex; + } + } + const matchesCssOrTs = + isRegExp && + (test.test('a.css') || + test.test('a.tsx') || + test.test('a.ts') || + test.test('a.stories.tsx') || + test.test('a.stories.ts')); + if (matchesCssOrTs && rule.resourceQuery == null) { + rule.resourceQuery = { not: [/raw/] }; + } + } + return rules; +} + module.exports = /** @type {Omit} */ ({ ...rootMain, stories: [...rootMain.stories, '../src/**/*.mdx', '../src/**/index.stories.@(ts|tsx)'], @@ -7,7 +76,9 @@ module.exports = /** @type {Omit { const localConfig = { ...rootMain.webpackFinal(config, options) }; - // add your own webpack tweaks if needed + localConfig.module = localConfig.module || { rules: [] }; + const rules = patchRules([...(localConfig.module.rules || [])]); + localConfig.module.rules = [cssModuleRule, ...rules]; return localConfig; }, diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview-head.html b/packages/react-components/react-headless-components-preview/stories/.storybook/preview-head.html index 670a7917313c6..8505581d0e0d3 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview-head.html +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview-head.html @@ -1,5 +1,8 @@ - - + + <Subtitle /> + <Description /> + + {primaryStory && ( + <> + <hr style={dividerStyle} /> + <HeaderMdx as="h3" id={nameToHash(primaryStory.name)}> + {primaryStory.name} + </HeaderMdx> + <Anchor storyId={primaryStory.id}> + <Canvas of={primaryStory.moduleExport} /> + <BebopSource of={primaryStory.moduleExport} /> + </Anchor> + </> + )} + + {/* Component-level props table (mirrors what FluentDocsPage renders). */} + <ArgTypes /> + + {remainingStories.length > 0 && ( + <> + <h2 style={storiesHeadingStyle}>Stories</h2> + {remainingStories.map(story => ( + <Anchor key={story.id} storyId={story.id}> + <HeaderMdx as="h3" id={nameToHash(story.name)}> + {story.name} + </HeaderMdx> + <Description of={story.moduleExport} /> + <Canvas of={story.moduleExport} /> + <BebopSource of={story.moduleExport} /> + </Anchor> + ))} + </> + )} + </div> + ); +}; + +// We let Storybook's native "Show code" toggle render inside the Canvas +// footer (alongside the "Open in Stackblitz" button injected by +// `@fluentui/react-storybook-addon-export-to-sandbox`). We hide only the +// expanded source pane it would normally reveal โ€” `<BebopSource>` listens to +// the native toggle's clicks and renders our tabbed panel **into the same +// canvas card** via a portal target (`.bebop-source-portal`). +// +// Storybook 9 sets a fixed `height` and `overflow: hidden` on `.sbdocs-preview` +// so its border tightly hugs the rendered story; when our panel is expanded +// inside the same card we need to release both so the code panel can flow. +const bebopDocsPageCss = ` +.bebop-docs-page .sbdocs-preview > *:not(.docs-story):not(.bebop-source-portal) { + display: none !important; +} +.bebop-docs-page .sbdocs-preview:has(> .bebop-source-portal:not(:empty)) { + height: auto !important; +} +`; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx new file mode 100644 index 0000000000000..3fc9d5d25a784 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx @@ -0,0 +1,287 @@ +/** + * `BebopSource` โ€” a docs block that renders the "Show code" panel for a + * headless story with **tabs**: one for the story TSX, one per CSS Module + * referenced by the story's meta. Replaces Storybook's built-in single-blob + * Source block (which can't show two languages side-by-side). + * + * The tabbed panel is driven by Storybook's native "Show code" toggle that + * Canvas renders inside its footer (alongside the "Open in Stackblitz" button + * injected by `@fluentui/react-storybook-addon-export-to-sandbox`). We listen + * to that toggle's clicks via a click handler on its DOM node and mirror its + * open/closed state into local React state โ€” keeping the UX of two buttons + * sitting together in the canvas footer (matching the deployed Fluent docs) + * while still showing the multi-language tabbed panel below the canvas card. + * + * Wired up by `BebopDocsPage`. The story's TSX comes from + * `parameters.docs.source.originalSource` (set via `withStorySource`); the CSS + * comes from `parameters.bebop.cssModules` (set via `withCssModuleSource`). + */ +/* eslint-disable @nx/workspace-no-restricted-globals -- Storybook docs block running in the manager iframe; uses DOM APIs to bridge to the native Canvas toggle that lives outside React. */ +import * as React from 'react'; +import { createPortal } from 'react-dom'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyProps = Record<string, any>; + +// Storybook's docs blocks live behind a deep import. The `useSourceProps` hook +// resolves the Source block's effective `code`/`language` for a story (honoring +// `parameters.docs.source.transform`, `originalSource`, etc). +import { DocsContext, SourceContext, useOf, useSourceProps } from '@storybook/addon-docs/blocks'; +// `SyntaxHighlighter` is part of Storybook's internal UI kit and already +// matches the rest of the docs chrome โ€” reusing it keeps the panel visually +// consistent with everything else Storybook renders. +import { SyntaxHighlighter } from 'storybook/internal/components'; + +/** A CSS Module file surfaced as a tab in the code panel. */ +export interface BebopCssModule { + /** Display name shown on the tab (e.g. `button.module.css`). */ + name: string; + /** Raw CSS source for the module (typically imported via `?raw`). */ + source: string; +} + +export interface BebopParameters { + /** Extra CSS Module sources to surface as tabs after the story TSX tab. */ + cssModules?: BebopCssModule[]; +} + +interface BebopSourceProps { + /** Reference to the story being rendered (`story.moduleExport`). */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + of: any; +} + +const SURFACE_BG = '#ffffff'; +const SURFACE_BORDER = 'rgba(38, 85, 115, 0.15)'; +const TAB_BAR_BG = '#f6f9fc'; +const ACTIVE_TAB_FG = '#9b1f5a'; +const TAB_FG = '#666666'; + +const containerStyle: React.CSSProperties = { + // Blend into the canvas card: no own border/radius, just a top divider and + // breathing room below the action bar (Show code / Open in Stackblitz). + marginTop: 16, + borderTop: `1px solid ${SURFACE_BORDER}`, + background: SURFACE_BG, +}; + +const tabBarStyle: React.CSSProperties = { + display: 'flex', + alignItems: 'stretch', + background: TAB_BAR_BG, + borderBottom: `1px solid ${SURFACE_BORDER}`, +}; + +const tabButtonStyle = (active: boolean): React.CSSProperties => ({ + appearance: 'none', + border: 0, + background: 'transparent', + padding: '10px 14px', + font: 'inherit', + fontSize: 12, + fontWeight: active ? 700 : 500, + color: active ? ACTIVE_TAB_FG : TAB_FG, + cursor: 'pointer', + borderBottom: `2px solid ${active ? ACTIVE_TAB_FG : 'transparent'}`, + marginBottom: -1, + whiteSpace: 'nowrap', +}); + +/** + * Subscribe to the native "Show code" toggle that Canvas renders inside the + * `.docs-story` element for `storyId`. Returns the current open/closed state. + * The selectors mirror those used by `react-storybook-addon-export-to-sandbox` + * to find the same button (supports both Storybook < 10 and >= 10 anchor IDs). + */ +function useNativeToggleState(storyId: string): boolean { + const [expanded, setExpanded] = React.useState(false); + + React.useEffect(() => { + const selector = [ + `#anchor--${storyId} .docs-story .docblock-code-toggle:not(.with-code-sandbox-button)`, + `#anchor--primary--${storyId} .docs-story .docblock-code-toggle:not(.with-code-sandbox-button)`, + ].join(', '); + + let cleanups: Array<() => void> = []; + let cancelled = false; + + const attach = () => { + if (cancelled) { + return true; + } + const button = document.querySelector<HTMLButtonElement>(selector); + if (!button) { + return false; + } + const onClick = () => { + // Native toggle has no aria-expanded โ€” flip our mirror on every click. + setExpanded(prev => !prev); + }; + button.addEventListener('click', onClick); + cleanups.push(() => button.removeEventListener('click', onClick)); + return true; + }; + + if (!attach()) { + // Canvas mounts asynchronously; poll briefly for the toggle to appear. + const interval = window.setInterval(() => { + if (attach()) { + window.clearInterval(interval); + } + }, 100); + cleanups.push(() => window.clearInterval(interval)); + } + + return () => { + cancelled = true; + cleanups.forEach(fn => fn()); + cleanups = []; + }; + }, [storyId]); + + return expanded; +} + +/** + * Find the canvas card (`.sbdocs-preview`) for `storyId` and append (once) a + * portal target div as its last child. Returns the element when ready so + * `BebopSource` can render its tabbed panel **inside** the same bordered card + * as the story preview, rather than as a detached block below it. + */ +function useCanvasPortalTarget(storyId: string): HTMLElement | null { + const [target, setTarget] = React.useState<HTMLElement | null>(null); + + React.useEffect(() => { + const anchorSelector = [`#anchor--${storyId}`, `#anchor--primary--${storyId}`].join(', '); + let cancelled = false; + let interval: number | undefined; + let portalEl: HTMLDivElement | null = null; + + const attach = () => { + if (cancelled) { + return true; + } + const anchor = document.querySelector<HTMLElement>(anchorSelector); + const card = anchor?.querySelector<HTMLElement>('.sbdocs-preview'); + if (!card) { + return false; + } + // Look for an existing target so multiple mounts of `BebopSource` (in + // dev / fast-refresh) reuse the same node. + let existing = card.querySelector<HTMLDivElement>(':scope > .bebop-source-portal'); + if (!existing) { + existing = document.createElement('div'); + existing.className = 'bebop-source-portal'; + // Storybook's `.sbdocs-preview > div` global rules paint a near-black + // background and drop shadow on direct children โ€” explicitly reset + // both so the canvas card colour shows through behind our inset, + // rounded panel. + existing.style.background = 'transparent'; + existing.style.boxShadow = 'none'; + card.appendChild(existing); + } + portalEl = existing; + setTarget(existing); + return true; + }; + + if (!attach()) { + interval = window.setInterval(() => { + if (attach()) { + window.clearInterval(interval!); + interval = undefined; + } + }, 100); + } + + return () => { + cancelled = true; + if (interval !== undefined) { + window.clearInterval(interval); + } + if (portalEl && portalEl.parentElement) { + portalEl.parentElement.removeChild(portalEl); + } + }; + }, [storyId]); + + return target; +} + +export const BebopSource: React.FC<BebopSourceProps> = ({ of }) => { + const { story } = useOf(of || 'story', ['story']) as { story: AnyProps }; + const docsContext = React.useContext(DocsContext); + const sourceContext = React.useContext(SourceContext); + // `useSourceProps` returns the code that Storybook's built-in Source block + // would have rendered. Pulling from it (rather than reading raw `?raw` + // imports ourselves) keeps `withStorySource` / `originalSource` semantics + // intact and follows whatever transform a story sets. + const sourceProps = useSourceProps({ of }, docsContext, sourceContext) as AnyProps; + const expanded = useNativeToggleState(story.id); + const portalTarget = useCanvasPortalTarget(story.id); + const [activeTabId, setActiveTabId] = React.useState<string>('story-tsx'); + + const tsxCode: string = typeof sourceProps.code === 'string' ? sourceProps.code : ''; + const tsxLanguage: string = typeof sourceProps.language === 'string' ? sourceProps.language : 'tsx'; + const allCssModules: BebopCssModule[] = (story.parameters?.bebop as BebopParameters | undefined)?.cssModules ?? []; + + // The meta typically registers every CSS module a component touches across + // all stories so the Stackblitz sandbox can bundle them. For the per-story + // tab strip we only want the modules actually referenced in the displayed + // TSX โ€” match by basename in import strings (e.g. `./styles/dialog.module.css` + // after `cleanStorySource`, or `bebop/components/dialog.module.css?raw`). + const referencedBasenames = new Set(Array.from(tsxCode.matchAll(/([a-z][a-z0-9-]*\.module\.css)/gi), m => m[1])); + const cssModules = referencedBasenames.size + ? allCssModules.filter(m => referencedBasenames.has(m.name)) + : allCssModules; + + if (!expanded || !portalTarget) { + return null; + } + if (!tsxCode && cssModules.length === 0) { + return null; + } + + type Tab = { id: string; label: string; code: string; language: string }; + const tabs: Tab[] = [ + { id: 'story-tsx', label: 'Story.tsx', code: tsxCode, language: tsxLanguage }, + ...cssModules.map((m, i) => ({ id: `css-${i}`, label: m.name, code: m.source.trim(), language: 'css' })), + ]; + const activeTab = tabs.find(t => t.id === activeTabId) ?? tabs[0]; + + return createPortal( + <div className="sb-unstyled" style={containerStyle}> + {tabs.length > 1 && ( + <div style={tabBarStyle} role="tablist" aria-label="Source code"> + {tabs.map(tab => ( + <button + key={tab.id} + type="button" + role="tab" + aria-selected={tab.id === activeTab.id} + style={tabButtonStyle(tab.id === activeTab.id)} + onClick={() => setActiveTabId(tab.id)} + > + {tab.label} + </button> + ))} + </div> + )} + <div role="tabpanel"> + <SyntaxHighlighter + // `key` forces a fresh mount per tab so the highlighter resets its + // scroll position and copy button state between languages. + key={activeTab.id} + language={activeTab.language} + copyable + bordered={false} + padded + format={false} + showLineNumbers={false} + > + {activeTab.code} + </SyntaxHighlighter> + </div> + </div>, + portalTarget, + ); +}; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts new file mode 100644 index 0000000000000..3a0eb0a3288de --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -0,0 +1,128 @@ +/** + * Story meta helper โ€” registers the CSS Module source(s) a story relies on so: + * + * 1. The custom docs page (`BebopDocsPage` โ†’ `BebopSource`) can surface them + * as tabs in the "Show code" panel. + * 2. The "Open in Stackblitz" button (provided by + * `@fluentui/react-storybook-addon-export-to-sandbox`) can bundle them โ€” + * together with `bebop/tokens.css` โ€” into the generated sandbox so the + * example renders with the correct theme out of the box. + * + * Spread the result into a story's `parameters` object: + * + * ```tsx + * import buttonCss from '../../../../../../bebop/components/button.module.css?raw'; + * import { withCssModuleSource } from '../_helpers/withCssModuleSource'; + * + * export default { + * title: 'Headless Components/Button', + * component: Button, + * parameters: { + * docs: { description: { component: descriptionMd } }, + * ...withCssModuleSource({ name: 'button.module.css', source: buttonCss }), + * }, + * }; + * ``` + * + * Pass multiple modules when a single component pulls from several CSS files + * (e.g., Input + chat-input variant): each becomes a tab. + */ + +// Loaded via the `?raw` resourceQuery rule configured in `.storybook/main.js`. +// Bundling tokens.css inline lets the Stackblitz scaffold include them without +// requiring story authors to wire imports manually. +import tokensCss from '../../../../../../bebop/tokens.css?raw'; + +import type { BebopCssModule, BebopParameters } from './BebopSource'; + +export type { BebopCssModule } from './BebopSource'; + +/** + * Minimal local mirror of the `SandboxContext` shape from + * `@fluentui/react-storybook-addon-export-to-sandbox`. We don't import the + * type from the addon itself to keep the stories package free of a direct + * dependency on the addon's source โ€” the addon is consumed at runtime via + * Storybook's addon registry, not statically. + */ +interface SandboxContext { + provider: string; + bundler: 'vite' | 'cra'; + storyExportToken: string; + storyFile: string; + dependencies: Record<string, string>; +} + +interface ExportToSandboxFragment { + exportToSandbox: { + transformFiles: (files: Record<string, string>, ctx: SandboxContext) => Record<string, string>; + }; +} + +export function withCssModuleSource( + ...modules: BebopCssModule[] +): { bebop: BebopParameters } & ExportToSandboxFragment { + return { + bebop: { cssModules: modules }, + exportToSandbox: { + transformFiles: (files, ctx) => buildSandboxFiles(files, ctx, modules), + }, + }; +} + +/** + * The story file imports each CSS Module via a long relative path that points + * back to `bebop/components/<name>.module.css`. In the sandbox, that path + * doesn't exist โ€” so we: + * + * 1. Drop a flat copy of `tokens.css` and each module under `src/styles/`. + * 2. Rewrite every `bebop/components/<โ€ฆ>.module.css` import in the story + * file to `./styles/<basename>` (or `../styles/<basename>` from `App`). + * 3. Inject `import './styles/tokens.css'` at the top of `src/App.tsx` + * so the design tokens cascade onto the rendered example. + */ +function buildSandboxFiles( + files: Record<string, string>, + _ctx: SandboxContext, + modules: BebopCssModule[], +): Record<string, string> { + const next = { ...files }; + + next['src/styles/tokens.css'] = tokensCss; + + // The meta typically lists every CSS Module a component uses across all + // stories so each individual story can pull what it needs without having + // to redeclare the imports. For the generated sandbox we only want the + // files actually referenced from `src/example.tsx`, otherwise unused + // modules (e.g. `checkbox.module.css` in a Dialog/Alert story that only + // uses `dialog.module.css`) clutter the file tree. + const example = next['src/example.tsx']; + const referenced = new Set<string>(); + if (typeof example === 'string') { + for (const match of example.matchAll(/([a-z][a-z0-9-]*\.module\.css)/gi)) { + referenced.add(match[1]); + } + } + const usedModules = referenced.size ? modules.filter(m => referenced.has(m.name)) : modules; + for (const m of usedModules) { + next[`src/styles/${m.name}`] = m.source; + } + + // Story file lives at `src/example.tsx`; rewrite the deeply-relative + // `bebop/components/<file>.module.css` import to a sibling path. + if (typeof example === 'string') { + next['src/example.tsx'] = example.replace( + /(['"])(?:\.\.\/)+bebop\/components\/([^'"]+\.module\.css)\1/g, + (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, + ); + } + + // Prepend the tokens import to App so `:root` custom properties apply + // everywhere (Storybook injects them via `preview.js`; in the sandbox we + // need the equivalent global import). + const app = next['src/App.tsx']; + if (typeof app === 'string' && !app.includes('./styles/tokens.css')) { + next['src/App.tsx'] = `import './styles/tokens.css';\n${app}`; + } + + return next; +} diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts new file mode 100644 index 0000000000000..7e0fb5e90b4d3 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts @@ -0,0 +1,104 @@ +/** + * Storybook docs helper: attaches a story file's own source as + * `parameters.docs.source.originalSource` so the Show-code panel renders the + * actual TSX (not just the meta-level CSS appended by `withCssModuleSource`). + * + * Storybook's CSF source loader (`enrichCsf`) doesn't follow re-exports โ€” when + * `index.stories.tsx` only does `export { Default } from './XDefault.stories'`, + * the loader emits no `originalSource` for `Default`, and the meta-level + * `parameters.docs.source.transform` ends up running on an empty string. This + * helper closes that gap by importing the file's own contents via webpack's + * `?raw` query and pinning them to the story. + * + * Usage in an `<Component>Default.stories.tsx`: + * + * import storySource from './ButtonDefault.stories?raw'; + * import { withStorySource } from '../_helpers/withStorySource'; + * + * export const Default = (): React.ReactNode => โ€ฆ; + * Default.parameters = withStorySource(storySource); + * + * Pass `extra` to merge additional `parameters` (e.g. existing + * `parameters.docs.description`): + * + * Default.parameters = withStorySource(storySource, { + * docs: { description: { story: 'โ€ฆ' } }, + * }); + */ + +type Parameters = Record<string, unknown> & { + docs?: Record<string, unknown> & { + source?: Record<string, unknown>; + }; +}; + +/** + * Strip the internal `withStorySource` plumbing so the displayed code is + * exactly what a consumer would paste into their own project. We remove: + * - the `import storySource from './*.stories?raw';` line + * - the `import { withStorySource } from '...';` line + * - the trailing `<StoryName>.parameters = withStorySource(storySourceโ€ฆ);` + * - the now-blank line(s) those leave behind + */ +function cleanStorySource(source: string): string { + return ( + source + .replace(/^import\s+\w+\s+from\s+['"]\..*?\.stories\?raw['"];\s*\r?\n/m, '') + .replace(/^import\s*\{\s*withStorySource\s*\}\s*from\s+['"][^'"]+['"];\s*\r?\n/m, '') + .replace(/^\w+\.parameters\s*=\s*withStorySource\([\s\S]*?\);\s*\r?\n?/m, '') + // Rewrite the deeply-relative `bebop/components/<file>.module.css` paths + // to a colocated `./styles/<file>.module.css` so the snippet matches what + // the user actually gets in the Stackblitz sandbox (and is paste-ready + // into a project that follows the same colocation convention). + .replace( + /(['"])(?:\.\.\/)+bebop\/components\/([^'"]+\.module\.css)\1/g, + (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, + ) + .replace(/\n{3,}/g, '\n\n') + .trimEnd() + .concat('\n') + ); +} + +export function withStorySource(storySource: string, extra: Parameters = {}): Parameters { + const cleaned = cleanStorySource(storySource); + const extraDocs = (extra.docs ?? {}) as Record<string, unknown> & { + source?: Record<string, unknown>; + }; + const params: Parameters = { + ...extra, + docs: { + ...extraDocs, + source: { + ...(extraDocs.source ?? {}), + // `code` takes precedence in Storybook's Source block โ€” we set it + // explicitly so the panel shows the full file (imports included) + // rather than the bare JSX body that the CSF source loader extracts + // from the right-hand side of the `export const Default = ...`. + code: cleaned, + originalSource: cleaned, + }, + }, + }; + + // The `@fluentui/babel-preset-storybook-full-source` plugin appends a + // `Default.parameters.fullSource = '<post-babel source>';` statement at the + // end of the compiled story file. That post-babel source has had every + // relative import stripped โ€” including our `import styles from + // '../../../../../../bebop/components/<file>.module.css';` line โ€” which + // breaks the "Open in Stackblitz" sandbox (the example references `styles` + // but never imports it). + // + // Defining `fullSource` as a non-writable getter swallows the babel + // assignment so our cleaned source (with imports preserved) wins. + Object.defineProperty(params, 'fullSource', { + get: () => cleaned, + set: () => { + /* swallow the babel preset's overwrite */ + }, + configurable: true, + enumerable: true, + }); + + return params; +} diff --git a/typings/static-assets/index.d.ts b/typings/static-assets/index.d.ts index af7269dabed90..d4bb050f933c5 100644 --- a/typings/static-assets/index.d.ts +++ b/typings/static-assets/index.d.ts @@ -31,3 +31,17 @@ declare module '*.md' { const src: string; export default src; } + +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +/** + * Webpack `?raw` query โ€” imports a file's source as a string. + * Used by stories to display CSS module source in Storybook's "Show code" panel. + */ +declare module '*?raw' { + const content: string; + export default content; +} From 12ace0412446bd667a66bddf94131bf5788fa39a Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 21:19:00 +0200 Subject: [PATCH 02/25] chore(headless-docsite): rename bebop -> theme and add styling disclaimer - 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> --- .../.storybook/main.js | 4 +- .../.storybook/manager-head.html | 4 +- .../.storybook/preview-head.html | 2 +- .../.storybook/preview.js | 8 ++-- .../.storybook/theme.js | 4 +- apps/public-docsite-v9-headless/project.json | 4 +- .../stories/.storybook/main.js | 2 +- .../stories/.storybook/preview.js | 8 ++-- .../stories/README.md | 24 +++++----- .../stories/project.json | 2 +- .../AccordionCollapsible.stories.tsx | 2 +- .../Accordion/AccordionDefault.stories.tsx | 2 +- .../stories/src/Accordion/index.stories.tsx | 2 +- .../src/Avatar/AvatarDefault.stories.tsx | 2 +- .../stories/src/Avatar/index.stories.tsx | 2 +- .../src/Badge/BadgeDefault.stories.tsx | 2 +- .../stories/src/Badge/index.stories.tsx | 2 +- .../Breadcrumb/BreadcrumbDefault.stories.tsx | 2 +- .../stories/src/Breadcrumb/index.stories.tsx | 2 +- .../src/Button/ButtonDefault.stories.tsx | 2 +- .../stories/src/Button/index.stories.tsx | 2 +- .../src/Checkbox/CheckboxDefault.stories.tsx | 2 +- .../stories/src/Checkbox/index.stories.tsx | 2 +- .../src/Dialog/DialogAlert.stories.tsx | 2 +- .../src/Dialog/DialogControlled.stories.tsx | 2 +- .../src/Dialog/DialogDefault.stories.tsx | 2 +- .../src/Dialog/DialogKeepMounted.stories.tsx | 4 +- .../src/Dialog/DialogNested.stories.tsx | 2 +- .../src/Dialog/DialogNoTrigger.stories.tsx | 2 +- .../src/Dialog/DialogNonModal.stories.tsx | 4 +- .../Dialog/DialogWithCloseButton.stories.tsx | 4 +- .../stories/src/Dialog/index.stories.tsx | 8 ++-- .../src/Divider/DividerDefault.stories.tsx | 2 +- .../src/Divider/DividerVertical.stories.tsx | 2 +- .../stories/src/Divider/index.stories.tsx | 2 +- .../src/Field/FieldDefault.stories.tsx | 4 +- .../stories/src/Field/index.stories.tsx | 4 +- .../stories/src/Input/InputBasic.stories.tsx | 2 +- .../src/Input/InputDefault.stories.tsx | 2 +- .../stories/src/Input/index.stories.tsx | 4 +- .../stories/src/Link/LinkDefault.stories.tsx | 2 +- .../stories/src/Link/index.stories.tsx | 2 +- .../MessageBar/MessageBarDefault.stories.tsx | 4 +- .../MessageBar/MessageBarIntent.stories.tsx | 4 +- .../stories/src/MessageBar/index.stories.tsx | 4 +- .../ProgressBarDefault.stories.tsx | 2 +- .../stories/src/ProgressBar/index.stories.tsx | 2 +- .../RadioGroup/RadioGroupDefault.stories.tsx | 2 +- .../stories/src/RadioGroup/index.stories.tsx | 2 +- .../src/Rating/RatingDefault.stories.tsx | 2 +- .../stories/src/Rating/index.stories.tsx | 2 +- .../RatingDisplayCompact.stories.tsx | 2 +- .../RatingDisplayDefault.stories.tsx | 2 +- .../src/RatingDisplay/index.stories.tsx | 2 +- .../SearchBox/SearchBoxDefault.stories.tsx | 2 +- .../stories/src/SearchBox/index.stories.tsx | 2 +- .../src/Select/SelectDefault.stories.tsx | 4 +- .../stories/src/Select/index.stories.tsx | 4 +- .../src/Skeleton/SkeletonDefault.stories.tsx | 2 +- .../stories/src/Skeleton/index.stories.tsx | 2 +- .../src/Slider/SliderDefault.stories.tsx | 2 +- .../stories/src/Slider/index.stories.tsx | 2 +- .../SpinButton/SpinButtonDefault.stories.tsx | 4 +- .../stories/src/SpinButton/index.stories.tsx | 4 +- .../src/Spinner/SpinnerDefault.stories.tsx | 2 +- .../src/Spinner/SpinnerLabels.stories.tsx | 2 +- .../stories/src/Spinner/index.stories.tsx | 2 +- .../src/Switch/SwitchDefault.stories.tsx | 2 +- .../stories/src/Switch/index.stories.tsx | 2 +- .../src/TabList/TabListDefault.stories.tsx | 2 +- .../stories/src/TabList/index.stories.tsx | 2 +- .../src/Textarea/TextareaDefault.stories.tsx | 2 +- .../stories/src/Textarea/index.stories.tsx | 2 +- .../ToggleButtonDefault.stories.tsx | 2 +- .../src/ToggleButton/index.stories.tsx | 2 +- .../src/Toolbar/ToolbarDefault.stories.tsx | 2 +- .../Toolbar/ToolbarToggleButton.stories.tsx | 2 +- .../src/Toolbar/ToolbarVertical.stories.tsx | 2 +- .../stories/src/Toolbar/index.stories.tsx | 2 +- ...BebopDocsPage.tsx => HeadlessDocsPage.tsx} | 44 +++++++++++++------ ...ebopSource.tsx => HeadlessSourcePanel.tsx} | 29 ++++++------ .../src/_helpers/withCssModuleSource.ts | 28 ++++++------ .../stories/src/_helpers/withStorySource.ts | 6 +-- .../components/accordion.module.css | 0 {bebop => theme}/components/avatar.module.css | 0 {bebop => theme}/components/badge.module.css | 0 .../components/breadcrumb.module.css | 0 {bebop => theme}/components/button.module.css | 2 +- .../components/chat-input.module.css | 4 +- .../components/checkbox.module.css | 0 {bebop => theme}/components/dialog.module.css | 0 .../components/divider.module.css | 0 {bebop => theme}/components/field.module.css | 0 {bebop => theme}/components/input.module.css | 2 +- {bebop => theme}/components/link.module.css | 0 .../components/message-bar.module.css | 0 .../components/progress-bar.module.css | 0 .../components/radio-group.module.css | 0 .../components/rating-display.module.css | 0 {bebop => theme}/components/rating.module.css | 0 {bebop => theme}/components/select.module.css | 0 .../components/skeleton.module.css | 0 {bebop => theme}/components/slider.module.css | 0 .../components/spin-button.module.css | 0 .../components/spinner.module.css | 0 {bebop => theme}/components/switch.module.css | 0 .../components/tab-list.module.css | 0 .../components/textarea.module.css | 0 .../components/toggle-button.module.css | 0 .../components/toolbar.module.css | 0 {bebop => theme}/tokens.css | 6 +-- 111 files changed, 185 insertions(+), 168 deletions(-) rename packages/react-components/react-headless-components-preview/stories/src/_helpers/{BebopDocsPage.tsx => HeadlessDocsPage.tsx} (74%) rename packages/react-components/react-headless-components-preview/stories/src/_helpers/{BebopSource.tsx => HeadlessSourcePanel.tsx} (92%) rename {bebop => theme}/components/accordion.module.css (100%) rename {bebop => theme}/components/avatar.module.css (100%) rename {bebop => theme}/components/badge.module.css (100%) rename {bebop => theme}/components/breadcrumb.module.css (100%) rename {bebop => theme}/components/button.module.css (98%) rename {bebop => theme}/components/chat-input.module.css (95%) rename {bebop => theme}/components/checkbox.module.css (100%) rename {bebop => theme}/components/dialog.module.css (100%) rename {bebop => theme}/components/divider.module.css (100%) rename {bebop => theme}/components/field.module.css (100%) rename {bebop => theme}/components/input.module.css (96%) rename {bebop => theme}/components/link.module.css (100%) rename {bebop => theme}/components/message-bar.module.css (100%) rename {bebop => theme}/components/progress-bar.module.css (100%) rename {bebop => theme}/components/radio-group.module.css (100%) rename {bebop => theme}/components/rating-display.module.css (100%) rename {bebop => theme}/components/rating.module.css (100%) rename {bebop => theme}/components/select.module.css (100%) rename {bebop => theme}/components/skeleton.module.css (100%) rename {bebop => theme}/components/slider.module.css (100%) rename {bebop => theme}/components/spin-button.module.css (100%) rename {bebop => theme}/components/spinner.module.css (100%) rename {bebop => theme}/components/switch.module.css (100%) rename {bebop => theme}/components/tab-list.module.css (100%) rename {bebop => theme}/components/textarea.module.css (100%) rename {bebop => theme}/components/toggle-button.module.css (100%) rename {bebop => theme}/components/toolbar.module.css (100%) rename {bebop => theme}/tokens.css (96%) diff --git a/apps/public-docsite-v9-headless/.storybook/main.js b/apps/public-docsite-v9-headless/.storybook/main.js index dd66e928f0f67..d01c99f35740b 100644 --- a/apps/public-docsite-v9-headless/.storybook/main.js +++ b/apps/public-docsite-v9-headless/.storybook/main.js @@ -3,7 +3,7 @@ const path = require('path'); const rootMain = require('../../../.storybook/main'); const repoRoot = path.resolve(__dirname, '../../..'); -const tokensDir = path.resolve(repoRoot, 'bebop'); +const tokensDir = path.resolve(repoRoot, 'theme'); const headlessStoriesDir = path.resolve( repoRoot, 'packages/react-components/react-headless-components-preview/stories', @@ -14,7 +14,7 @@ const headlessStoriesDir = path.resolve( * * Storybook's `@storybook/builder-webpack5` ships a default `\.css$` rule that * pipes any CSS through `style-loader` + plain `css-loader`. That handles - * `bebop/tokens.css` correctly. For `*.module.css` files (the per-component + * `theme/tokens.css` correctly. For `*.module.css` files (the per-component * design-system styles) we want CSS Modules, so we narrow the default rule to * skip `.module.css` and add a dedicated rule that turns on `modules: true`. * diff --git a/apps/public-docsite-v9-headless/.storybook/manager-head.html b/apps/public-docsite-v9-headless/.storybook/manager-head.html index 90513a751a048..e8c4d5e419905 100644 --- a/apps/public-docsite-v9-headless/.storybook/manager-head.html +++ b/apps/public-docsite-v9-headless/.storybook/manager-head.html @@ -9,7 +9,7 @@ The sidebar selectors are brittle โ€” they depend on Storybook's manager DOM structure and may need updating on Storybook version bumps. Color values - mirror `bebop/tokens.css` (light mode). + mirror `theme/tokens.css` (light mode). @see https://storybook.js.org/docs/react/configure/theming#css-escape-hatches --> @@ -78,7 +78,7 @@ /* Override Storybook's default hover (a saturated pink derived from `colorSecondary`) with a subtle neutral surface tone. Matches the - `--surface-muted` token from `bebop/tokens.css`. + `--surface-muted` token from `theme/tokens.css`. */ .sidebar-item:hover, .sidebar-item:hover svg, diff --git a/apps/public-docsite-v9-headless/.storybook/preview-head.html b/apps/public-docsite-v9-headless/.storybook/preview-head.html index 7582cad9f6004..d1ae5416412f3 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview-head.html +++ b/apps/public-docsite-v9-headless/.storybook/preview-head.html @@ -1,7 +1,7 @@ <!-- Story canvas head. - Design tokens (`bebop/tokens.css`) are imported from `preview.js` so they + Design tokens (`theme/tokens.css`) are imported from `preview.js` so they reach the iframe via webpack. This file holds raw <link> / <style> tags that must be present in the iframe document head before any story renders. --> diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 8a69b4aa46a32..48a9233dec33f 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -5,11 +5,11 @@ import * as rootPreview from '../../../.storybook/preview'; // Design tokens (light + dark CSS custom properties on :root and // [data-theme="dark"]), plus a few base resets for body/html. Loaded once // for every story rendered in this Storybook. -import '../../../bebop/tokens.css'; +import '../../../theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. -// See `packages/.../stories/src/_helpers/BebopDocsPage.tsx` for rationale. -import { BebopDocsPage } from '../../../packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage'; +// See `packages/.../stories/src/_helpers/HeadlessDocsPage.tsx` for rationale. +import { HeadlessDocsPage } from '../../../packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage'; polyfillBodyAndObserve(); @@ -21,7 +21,7 @@ export const parameters = { ...rootPreview.parameters, docs: { ...rootPreview.parameters.docs, - page: BebopDocsPage, + page: HeadlessDocsPage, }, options: { storySort: { diff --git a/apps/public-docsite-v9-headless/.storybook/theme.js b/apps/public-docsite-v9-headless/.storybook/theme.js index 9dd53beb75e85..8ff4a86849504 100644 --- a/apps/public-docsite-v9-headless/.storybook/theme.js +++ b/apps/public-docsite-v9-headless/.storybook/theme.js @@ -3,9 +3,9 @@ import { create } from 'storybook/theming'; /** * Custom Storybook chrome for the headless components docsite. * - * Values mirror the light-mode tokens in `bebop/tokens.css`. The Storybook + * Values mirror the light-mode tokens in `theme/tokens.css`. The Storybook * theme builds at compile time and cannot read CSS custom properties, so the - * palette is inlined here. Update this file alongside `bebop/tokens.css` if + * palette is inlined here. Update this file alongside `theme/tokens.css` if * the design tokens shift. */ const theme = create({ diff --git a/apps/public-docsite-v9-headless/project.json b/apps/public-docsite-v9-headless/project.json index 9f0b35dec4c2f..31a47992caa6e 100644 --- a/apps/public-docsite-v9-headless/project.json +++ b/apps/public-docsite-v9-headless/project.json @@ -12,7 +12,7 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/bebop/**"] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] }, "build-storybook:docsite": { "dependsOn": [ @@ -21,7 +21,7 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/bebop/**"] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] } } } diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js index c19811c405d5b..833640b6588cc 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js @@ -3,7 +3,7 @@ const path = require('path'); const rootMain = require('../../../../../.storybook/main'); const repoRoot = path.resolve(__dirname, '../../../../..'); -const tokensDir = path.resolve(repoRoot, 'bebop'); +const tokensDir = path.resolve(repoRoot, 'theme'); const headlessStoriesDir = path.resolve(__dirname, '..'); /** diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js index 03f1296d70b31..48bfb3c978d07 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js @@ -5,12 +5,12 @@ import * as rootPreview from '../../../../../.storybook/preview'; // Design tokens โ€” mirrors the import in // apps/public-docsite-v9-headless/.storybook/preview.js so the per-package // storybook (built by `pr-website-deploy.yml`) renders identical stories. -import '../../../../../bebop/tokens.css'; +import '../../../../../theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. -// See `stories/src/_helpers/BebopDocsPage.tsx` for the rationale (Storybook's +// See `stories/src/_helpers/HeadlessDocsPage.tsx` for the rationale (Storybook's // built-in Source block can't be made multi-language via MDX overrides). -import { BebopDocsPage } from '../src/_helpers/BebopDocsPage'; +import { HeadlessDocsPage } from '../src/_helpers/HeadlessDocsPage'; polyfillBodyAndObserve(); @@ -22,7 +22,7 @@ export const parameters = { ...rootPreview.parameters, docs: { ...(rootPreview.parameters && rootPreview.parameters.docs), - page: BebopDocsPage, + page: HeadlessDocsPage, }, }; diff --git a/packages/react-components/react-headless-components-preview/stories/README.md b/packages/react-components/react-headless-components-preview/stories/README.md index 53bbdbedeb287..c3df7a57b4cf5 100644 --- a/packages/react-components/react-headless-components-preview/stories/README.md +++ b/packages/react-components/react-headless-components-preview/stories/README.md @@ -2,10 +2,10 @@ Storybook stories for [`@fluentui/react-headless-components-preview`](../library). -These stories double as the visual reference for the "Bebop" design language: the +These stories double as the visual reference for the "Design system" design language: the headless components stay unstyled in `library/`, all visual concerns live in CSS -Modules under `bebop/` at the repo root, and the stories pull both together. -`bebop/tokens.css` is imported once in `.storybook/preview.js` and defines +Modules under `theme/` at the repo root, and the stories pull both together. +`theme/tokens.css` is imported once in `.storybook/preview.js` and defines `:root` (light) and `[data-theme="dark"]` (dark) CSS variables for every story. ## Usage @@ -35,8 +35,8 @@ For each new component: 1. Create the headless component under `library/src/components/<Name>/` (out of scope for this guide). -2. Add a CSS Module at `bebop/components/<name>.module.css` driven entirely by - `var(--โ€ฆ)` from `bebop/tokens.css`. **Do not hardcode colors, sizes, or +2. Add a CSS Module at `theme/components/<name>.module.css` driven entirely by + `var(--โ€ฆ)` from `theme/tokens.css`. **Do not hardcode colors, sizes, or typography.** 3. Add a stories folder at `stories/src/<Name>/` containing: - `<Name>Description.md` โ€” short MDX-friendly markdown component description. @@ -52,7 +52,7 @@ the CSS Module, and stories pull both together. ```tsx import * as React from 'react'; import { MyComponent } from '@fluentui/react-headless-components-preview/my-component'; -import styles from '../../../../../../bebop/components/my-component.module.css'; +import styles from '../../../../../../theme/components/my-component.module.css'; export const Default = (): React.ReactNode => <MyComponent className={styles.root} />; ``` @@ -63,9 +63,9 @@ Notes: `stories/src/<Name>/<File>.tsx`. The webpack rule that handles `*.module.css` is registered both in the docsite (`apps/public-docsite-v9-headless/.storybook/main.js`) and in this package's per-package storybook - (`stories/.storybook/main.js`). If you add the file outside `bebop/` make sure + (`stories/.storybook/main.js`). If you add the file outside `theme/` make sure the rule's `include` list covers it. -- No inline styles, no Tailwind, no Griffel. Tokens come from `bebop/tokens.css`. +- No inline styles, no Tailwind, no Griffel. Tokens come from `theme/tokens.css`. - Every CSS value must resolve through a `var(--โ€ฆ)` token โ€” search the diff for raw `#` and `rgb(` to confirm. @@ -79,7 +79,7 @@ helper at `stories/src/_helpers/withCssModuleSource.ts` does the stitching: import { MyComponent } from '@fluentui/react-headless-components-preview/my-component'; import descriptionMd from './MyComponentDescription.md'; -import myComponentCss from '../../../../../../bebop/components/my-component.module.css?raw'; +import myComponentCss from '../../../../../../theme/components/my-component.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './MyComponentDefault.stories'; @@ -129,7 +129,7 @@ declarations live at `typings/static-assets/index.d.ts`. | Type | `--font-sans` (Segoe UI), `--font-mono`, `--font-display` | | Motion | `--ease-standard`, `--ease-emphasized`, `--duration-fast/medium/slow` | -Read the file directly when in doubt: `bebop/tokens.css`. +Read the file directly when in doubt: `theme/tokens.css`. ### 5 ยท Visual language conventions @@ -192,8 +192,8 @@ These are the things that took time to discover. Keep them in mind: | Path | Purpose | | ---------------------------------------------------- | ------------------------------------------------------------------- | -| `bebop/tokens.css` | CSS custom properties, light + dark. Imported once in `preview.js`. | -| `bebop/components/<name>.module.css` | Per-component scoped styles. | +| `theme/tokens.css` | CSS custom properties, light + dark. Imported once in `preview.js`. | +| `theme/components/<name>.module.css` | Per-component scoped styles. | | `stories/src/<Name>/<Name>Default.stories.tsx` | Default story body using CSS Module classes. | | `stories/src/<Name>/<Name>Description.md` | Component description shown in the Docs panel. | | `stories/src/<Name>/index.stories.tsx` | Meta + `parameters.docs.source.transform` wiring. | diff --git a/packages/react-components/react-headless-components-preview/stories/project.json b/packages/react-components/react-headless-components-preview/stories/project.json index 4b374727ebfe8..2f4b38232fe78 100644 --- a/packages/react-components/react-headless-components-preview/stories/project.json +++ b/packages/react-components/react-headless-components-preview/stories/project.json @@ -7,7 +7,7 @@ "implicitDependencies": [], "targets": { "build-storybook": { - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/bebop/**"] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] } } } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx index e698c0eab7ebf..00fbce7e41e07 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/accordion.module.css'; +import styles from '../../../../../../theme/components/accordion.module.css'; import storySource from './AccordionCollapsible.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx index c6fe01f30cb26..8edc91202b490 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/accordion.module.css'; +import styles from '../../../../../../theme/components/accordion.module.css'; import storySource from './AccordionDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx index dde7f5851d795..e09510f79461b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx @@ -6,7 +6,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import descriptionMd from './AccordionDescription.md'; -import accordionCss from '../../../../../../bebop/components/accordion.module.css?raw'; +import accordionCss from '../../../../../../theme/components/accordion.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './AccordionDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx index 77238b6a426b7..7c80678336f7f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Avatar } from '@fluentui/react-headless-components-preview/avatar'; -import styles from '../../../../../../bebop/components/avatar.module.css'; +import styles from '../../../../../../theme/components/avatar.module.css'; import storySource from './AvatarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx index 8f4a84c5299cf..99703ff29afe7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx @@ -1,7 +1,7 @@ import { Avatar } from '@fluentui/react-headless-components-preview/avatar'; import descriptionMd from './AvatarDescription.md'; -import avatarCss from '../../../../../../bebop/components/avatar.module.css?raw'; +import avatarCss from '../../../../../../theme/components/avatar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './AvatarDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx index 8b3298c09c2ed..d02cf5b4be9b0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Badge } from '@fluentui/react-headless-components-preview/badge'; -import styles from '../../../../../../bebop/components/badge.module.css'; +import styles from '../../../../../../theme/components/badge.module.css'; import storySource from './BadgeDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx index 2d71d37bdccba..3e44d71d5acea 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx @@ -1,7 +1,7 @@ import { Badge } from '@fluentui/react-headless-components-preview/badge'; import descriptionMd from './BadgeDescription.md'; -import badgeCss from '../../../../../../bebop/components/badge.module.css?raw'; +import badgeCss from '../../../../../../theme/components/badge.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './BadgeDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx index 865550e5198a2..6390876628736 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/breadcrumb'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/breadcrumb.module.css'; +import styles from '../../../../../../theme/components/breadcrumb.module.css'; import storySource from './BreadcrumbDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx index d1ffaa74f227c..34e7739a3cfa5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx @@ -6,7 +6,7 @@ import { } from '@fluentui/react-headless-components-preview/breadcrumb'; import descriptionMd from './BreadcrumbDescription.md'; -import breadcrumbCss from '../../../../../../bebop/components/breadcrumb.module.css?raw'; +import breadcrumbCss from '../../../../../../theme/components/breadcrumb.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './BreadcrumbDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx index ef041788b52cc..c05dd08fd19f2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Button } from '@fluentui/react-headless-components-preview/button'; import { AddRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/button.module.css'; +import styles from '../../../../../../theme/components/button.module.css'; import storySource from './ButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx index e4a371585f457..19327c424d8a0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx @@ -1,7 +1,7 @@ import { Button } from '@fluentui/react-headless-components-preview/button'; import descriptionMd from './ButtonDescription.md'; -import buttonCss from '../../../../../../bebop/components/button.module.css?raw'; +import buttonCss from '../../../../../../theme/components/button.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ButtonDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx index c490700be42ad..4590ec27afb40 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import { CheckmarkRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/checkbox.module.css'; +import styles from '../../../../../../theme/components/checkbox.module.css'; import storySource from './CheckboxDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx index e91ca38160675..596fcb5772a2c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx @@ -1,7 +1,7 @@ import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import descriptionMd from './CheckboxDescription.md'; -import checkboxCss from '../../../../../../bebop/components/checkbox.module.css?raw'; +import checkboxCss from '../../../../../../theme/components/checkbox.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './CheckboxDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx index a94e0868f03bf..b5854109699fb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../bebop/components/dialog.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; import storySource from './DialogAlert.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx index 802a01b93461b..df499cea19cd1 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../bebop/components/dialog.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; import storySource from './DialogControlled.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx index 1b25414d25733..2ba6ece33cb13 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../bebop/components/dialog.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; import storySource from './DialogDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx index d8678bde53bc5..8b621de928023 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx @@ -9,8 +9,8 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; -import styles from '../../../../../../bebop/components/dialog.module.css'; -import textareaStyles from '../../../../../../bebop/components/textarea.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; +import textareaStyles from '../../../../../../theme/components/textarea.module.css'; import storySource from './DialogKeepMounted.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx index 249073ecf4c10..4648917a0c4f5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../bebop/components/dialog.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; import storySource from './DialogNested.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx index 659f56292f565..278a14ca0d487 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx @@ -8,7 +8,7 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import type { DialogOpenChangeData } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../bebop/components/dialog.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; import storySource from './DialogNoTrigger.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx index 30f25409e3b9c..454775c0ee5b7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx @@ -9,8 +9,8 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import { Input } from '@fluentui/react-headless-components-preview/input'; -import styles from '../../../../../../bebop/components/dialog.module.css'; -import inputStyles from '../../../../../../bebop/components/input.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; +import inputStyles from '../../../../../../theme/components/input.module.css'; import storySource from './DialogNonModal.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx index 7b6c5acf4c109..cb233cd5fd67e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx @@ -10,8 +10,8 @@ import { import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import { CheckmarkRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/dialog.module.css'; -import checkboxStyles from '../../../../../../bebop/components/checkbox.module.css'; +import styles from '../../../../../../theme/components/dialog.module.css'; +import checkboxStyles from '../../../../../../theme/components/checkbox.module.css'; import storySource from './DialogWithCloseButton.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx index 09d415c17ea2b..5cc47a10bfb1f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx @@ -8,10 +8,10 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import descriptionMd from './DialogDescription.md'; -import dialogCss from '../../../../../../bebop/components/dialog.module.css?raw'; -import checkboxCss from '../../../../../../bebop/components/checkbox.module.css?raw'; -import textareaCss from '../../../../../../bebop/components/textarea.module.css?raw'; -import inputCss from '../../../../../../bebop/components/input.module.css?raw'; +import dialogCss from '../../../../../../theme/components/dialog.module.css?raw'; +import checkboxCss from '../../../../../../theme/components/checkbox.module.css?raw'; +import textareaCss from '../../../../../../theme/components/textarea.module.css?raw'; +import inputCss from '../../../../../../theme/components/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './DialogDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx index 6d40f7380ba79..b08bfafd3c2cf 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Divider } from '@fluentui/react-headless-components-preview/divider'; -import styles from '../../../../../../bebop/components/divider.module.css'; +import styles from '../../../../../../theme/components/divider.module.css'; import storySource from './DividerDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx index 04f26da71c590..da9e8af685307 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Divider } from '@fluentui/react-headless-components-preview/divider'; import { CircleRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/divider.module.css'; +import styles from '../../../../../../theme/components/divider.module.css'; import storySource from './DividerVertical.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx index ed0488ad91e91..ca0de2d53c1f3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx @@ -1,7 +1,7 @@ import { Divider } from '@fluentui/react-headless-components-preview/divider'; import descriptionMd from './DividerDescription.md'; -import dividerCss from '../../../../../../bebop/components/divider.module.css?raw'; +import dividerCss from '../../../../../../theme/components/divider.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './DividerDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx index ea90c3fe6b2a7..be66a09e6703b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx @@ -3,8 +3,8 @@ import { Field } from '@fluentui/react-headless-components-preview/field'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { ErrorCircleRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../bebop/components/field.module.css'; -import inputStyles from '../../../../../../bebop/components/input.module.css'; +import fieldStyles from '../../../../../../theme/components/field.module.css'; +import inputStyles from '../../../../../../theme/components/input.module.css'; import storySource from './FieldDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx index e85212a5bd618..762133a2ad580 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx @@ -1,8 +1,8 @@ import { Field } from '@fluentui/react-headless-components-preview/field'; import descriptionMd from './FieldDescription.md'; -import fieldCss from '../../../../../../bebop/components/field.module.css?raw'; -import inputCss from '../../../../../../bebop/components/input.module.css?raw'; +import fieldCss from '../../../../../../theme/components/field.module.css?raw'; +import inputCss from '../../../../../../theme/components/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './FieldDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx index d14b380c28718..f6f84a8325b21 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { SearchRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/input.module.css'; +import styles from '../../../../../../theme/components/input.module.css'; import storySource from './InputBasic.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Basic = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx index 78b83f3e5ee27..834cb5fe580a4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { AddRegular, MicRegular, MicPulseRegular, SendRegular } from '@fluentui/react-icons'; -import chatStyles from '../../../../../../bebop/components/chat-input.module.css'; +import chatStyles from '../../../../../../theme/components/chat-input.module.css'; import storySource from './InputDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx index 50665d235db36..6e919eb258d0c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx @@ -1,8 +1,8 @@ import { Input } from '@fluentui/react-headless-components-preview/input'; import descriptionMd from './InputDescription.md'; -import inputCss from '../../../../../../bebop/components/input.module.css?raw'; -import chatInputCss from '../../../../../../bebop/components/chat-input.module.css?raw'; +import inputCss from '../../../../../../theme/components/input.module.css?raw'; +import chatInputCss from '../../../../../../theme/components/chat-input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './InputDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx index a94fc22cb35bb..504a77d8b87f3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Link } from '@fluentui/react-headless-components-preview/link'; -import styles from '../../../../../../bebop/components/link.module.css'; +import styles from '../../../../../../theme/components/link.module.css'; import storySource from './LinkDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx index e58010a58a9c6..a49ffa40c9f6c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx @@ -1,7 +1,7 @@ import { Link } from '@fluentui/react-headless-components-preview/link'; import descriptionMd from './LinkDescription.md'; -import linkCss from '../../../../../../bebop/components/link.module.css?raw'; +import linkCss from '../../../../../../theme/components/link.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './LinkDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx index 655a71cfe2d42..37f3817080564 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx @@ -8,8 +8,8 @@ import { } from '@fluentui/react-headless-components-preview/message-bar'; import { DismissRegular, InfoRegular } from '@fluentui/react-icons'; -import linkStyles from '../../../../../../bebop/components/link.module.css'; -import styles from '../../../../../../bebop/components/message-bar.module.css'; +import linkStyles from '../../../../../../theme/components/link.module.css'; +import styles from '../../../../../../theme/components/message-bar.module.css'; import storySource from './MessageBarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx index 1de99f383fafa..04e05d5e66ab8 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx @@ -3,8 +3,8 @@ import { Link } from '@fluentui/react-headless-components-preview/link'; import { MessageBar, MessageBarBody, MessageBarTitle } from '@fluentui/react-headless-components-preview/message-bar'; import { CheckmarkCircleRegular, ErrorCircleRegular, InfoRegular, WarningRegular } from '@fluentui/react-icons'; -import linkStyles from '../../../../../../bebop/components/link.module.css'; -import styles from '../../../../../../bebop/components/message-bar.module.css'; +import linkStyles from '../../../../../../theme/components/link.module.css'; +import styles from '../../../../../../theme/components/message-bar.module.css'; import storySource from './MessageBarIntent.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx index c192c24ef7dfc..99ef2eab81d51 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx @@ -6,8 +6,8 @@ import { } from '@fluentui/react-headless-components-preview/message-bar'; import descriptionMd from './MessageBarDescription.md'; -import messageBarCss from '../../../../../../bebop/components/message-bar.module.css?raw'; -import linkCss from '../../../../../../bebop/components/link.module.css?raw'; +import messageBarCss from '../../../../../../theme/components/message-bar.module.css?raw'; +import linkCss from '../../../../../../theme/components/link.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './MessageBarDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx index f9bab509fcc25..b2f90241aa3ab 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ProgressBar } from '@fluentui/react-headless-components-preview/progress-bar'; -import styles from '../../../../../../bebop/components/progress-bar.module.css'; +import styles from '../../../../../../theme/components/progress-bar.module.css'; import storySource from './ProgressBarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx index a7ed8877bfc04..c9f1fb122abb7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx @@ -1,7 +1,7 @@ import { ProgressBar } from '@fluentui/react-headless-components-preview/progress-bar'; import descriptionMd from './ProgressBarDescription.md'; -import progressBarCss from '../../../../../../bebop/components/progress-bar.module.css?raw'; +import progressBarCss from '../../../../../../theme/components/progress-bar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ProgressBarDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx index f34b0f2c09f3b..e4947c4c22cd3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { RadioGroup, Radio } from '@fluentui/react-headless-components-preview/radio-group'; -import styles from '../../../../../../bebop/components/radio-group.module.css'; +import styles from '../../../../../../theme/components/radio-group.module.css'; import storySource from './RadioGroupDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const plans = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx index 3f2c9df36faba..b643f86fc37f3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx @@ -1,7 +1,7 @@ import { RadioGroup, Radio } from '@fluentui/react-headless-components-preview/radio-group'; import descriptionMd from './RadioGroupDescription.md'; -import radioGroupCss from '../../../../../../bebop/components/radio-group.module.css?raw'; +import radioGroupCss from '../../../../../../theme/components/radio-group.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RadioGroupDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx index 987ca27f6de85..78d1b7b923012 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Rating, RatingItem } from '@fluentui/react-headless-components-preview/rating'; import { StarFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/rating.module.css'; +import styles from '../../../../../../theme/components/rating.module.css'; import storySource from './RatingDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx index dc59629cbf551..81df0057870fa 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx @@ -1,7 +1,7 @@ import { Rating, RatingItem } from '@fluentui/react-headless-components-preview/rating'; import descriptionMd from './RatingDescription.md'; -import ratingCss from '../../../../../../bebop/components/rating.module.css?raw'; +import ratingCss from '../../../../../../theme/components/rating.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RatingDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx index d514ec03ac9f4..cb7fe7308dd20 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/rating-display.module.css'; +import styles from '../../../../../../theme/components/rating-display.module.css'; import storySource from './RatingDisplayCompact.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx index 5ef3363bf7e89..78204555679a4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/rating-display.module.css'; +import styles from '../../../../../../theme/components/rating-display.module.css'; import storySource from './RatingDisplayDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx index b829fc58abeab..13280dcd0938f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx @@ -1,7 +1,7 @@ import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import descriptionMd from './RatingDisplayDescription.md'; -import ratingDisplayCss from '../../../../../../bebop/components/rating-display.module.css?raw'; +import ratingDisplayCss from '../../../../../../theme/components/rating-display.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RatingDisplayDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx index e308263500500..679c30dcf018f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx @@ -3,7 +3,7 @@ import { SearchBox } from '@fluentui/react-headless-components-preview/search-bo import { SearchRegular } from '@fluentui/react-icons'; // SearchBox reuses the input CSS module per the story authoring guide. -import styles from '../../../../../../bebop/components/input.module.css'; +import styles from '../../../../../../theme/components/input.module.css'; import storySource from './SearchBoxDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx index 08ba3ad187f42..23b01c4f2ae31 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx @@ -1,7 +1,7 @@ import { SearchBox } from '@fluentui/react-headless-components-preview/search-box'; import descriptionMd from './SearchBoxDescription.md'; -import inputCss from '../../../../../../bebop/components/input.module.css?raw'; +import inputCss from '../../../../../../theme/components/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SearchBoxDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx index 13d76c8474768..3a045e55e4def 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import { Select } from '@fluentui/react-headless-components-preview/select'; import { ChevronDownRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../bebop/components/field.module.css'; -import styles from '../../../../../../bebop/components/select.module.css'; +import fieldStyles from '../../../../../../theme/components/field.module.css'; +import styles from '../../../../../../theme/components/select.module.css'; import storySource from './SelectDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx index 36f2a749dbeb6..0ec78ea7e2093 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx @@ -1,8 +1,8 @@ import { Select } from '@fluentui/react-headless-components-preview/select'; import descriptionMd from './SelectDescription.md'; -import selectCss from '../../../../../../bebop/components/select.module.css?raw'; -import fieldCss from '../../../../../../bebop/components/field.module.css?raw'; +import selectCss from '../../../../../../theme/components/select.module.css?raw'; +import fieldCss from '../../../../../../theme/components/field.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SelectDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx index 2829ba69b400a..875188a4601a4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Skeleton, SkeletonItem } from '@fluentui/react-headless-components-preview/skeleton'; -import styles from '../../../../../../bebop/components/skeleton.module.css'; +import styles from '../../../../../../theme/components/skeleton.module.css'; import storySource from './SkeletonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx index aec338921069d..f3abaa9d91b36 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx @@ -1,7 +1,7 @@ import { Skeleton, SkeletonItem } from '@fluentui/react-headless-components-preview/skeleton'; import descriptionMd from './SkeletonDescription.md'; -import skeletonCss from '../../../../../../bebop/components/skeleton.module.css?raw'; +import skeletonCss from '../../../../../../theme/components/skeleton.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SkeletonDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx index 413a9b324d073..67b2544c02157 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Slider } from '@fluentui/react-headless-components-preview/slider'; -import styles from '../../../../../../bebop/components/slider.module.css'; +import styles from '../../../../../../theme/components/slider.module.css'; import storySource from './SliderDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx index 054bf1da8bacd..d5b633f0157f2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx @@ -1,7 +1,7 @@ import { Slider } from '@fluentui/react-headless-components-preview/slider'; import descriptionMd from './SliderDescription.md'; -import sliderCss from '../../../../../../bebop/components/slider.module.css?raw'; +import sliderCss from '../../../../../../theme/components/slider.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SliderDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx index 9b898a8039fdb..80bf9bc7869bc 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import { SpinButton } from '@fluentui/react-headless-components-preview/spin-button'; import { ChevronDownRegular, ChevronUpRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../bebop/components/field.module.css'; -import styles from '../../../../../../bebop/components/spin-button.module.css'; +import fieldStyles from '../../../../../../theme/components/field.module.css'; +import styles from '../../../../../../theme/components/spin-button.module.css'; import storySource from './SpinButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx index 7f2329a9843a1..b275a83b32287 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx @@ -1,8 +1,8 @@ import { SpinButton } from '@fluentui/react-headless-components-preview/spin-button'; import descriptionMd from './SpinButtonDescription.md'; -import spinButtonCss from '../../../../../../bebop/components/spin-button.module.css?raw'; -import fieldCss from '../../../../../../bebop/components/field.module.css?raw'; +import spinButtonCss from '../../../../../../theme/components/spin-button.module.css?raw'; +import fieldCss from '../../../../../../theme/components/field.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SpinButtonDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx index 4968a7900c2b3..5bc124ca88bf0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/spinner.module.css'; +import styles from '../../../../../../theme/components/spinner.module.css'; import storySource from './SpinnerDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx index a02958ec018fd..a7fdaef32be58 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/spinner.module.css'; +import styles from '../../../../../../theme/components/spinner.module.css'; import storySource from './SpinnerLabels.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Labels = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx index e204a2782387d..12220a79642db 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx @@ -1,7 +1,7 @@ import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import descriptionMd from './SpinnerDescription.md'; -import spinnerCss from '../../../../../../bebop/components/spinner.module.css?raw'; +import spinnerCss from '../../../../../../theme/components/spinner.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SpinnerDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx index ac3122faf5cab..0fed055a1f912 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Switch } from '@fluentui/react-headless-components-preview/switch'; -import styles from '../../../../../../bebop/components/switch.module.css'; +import styles from '../../../../../../theme/components/switch.module.css'; import storySource from './SwitchDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx index 3e6a5921304f7..c387424c513aa 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx @@ -1,7 +1,7 @@ import { Switch } from '@fluentui/react-headless-components-preview/switch'; import descriptionMd from './SwitchDescription.md'; -import switchCss from '../../../../../../bebop/components/switch.module.css?raw'; +import switchCss from '../../../../../../theme/components/switch.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SwitchDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx index 5d8b88ac17891..10461104ef390 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { TabList, Tab } from '@fluentui/react-headless-components-preview/tab-list'; -import styles from '../../../../../../bebop/components/tab-list.module.css'; +import styles from '../../../../../../theme/components/tab-list.module.css'; import storySource from './TabListDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const tabs = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx index 41543d108e819..26810d2e6cb8e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx @@ -1,7 +1,7 @@ import { TabList } from '@fluentui/react-headless-components-preview/tab-list'; import descriptionMd from './TabListDescription.md'; -import tabListCss from '../../../../../../bebop/components/tab-list.module.css?raw'; +import tabListCss from '../../../../../../theme/components/tab-list.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './TabListDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx index 1ec70691f7dfa..d5b7322639de2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; -import styles from '../../../../../../bebop/components/textarea.module.css'; +import styles from '../../../../../../theme/components/textarea.module.css'; import storySource from './TextareaDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx index 614c4fd836181..1301794bff9fd 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx @@ -1,7 +1,7 @@ import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; import descriptionMd from './TextareaDescription.md'; -import textareaCss from '../../../../../../bebop/components/textarea.module.css?raw'; +import textareaCss from '../../../../../../theme/components/textarea.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './TextareaDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx index fa8d3205fad3f..4901e0907a4df 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ToggleButton } from '@fluentui/react-headless-components-preview/toggle-button'; import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/toggle-button.module.css'; +import styles from '../../../../../../theme/components/toggle-button.module.css'; import storySource from './ToggleButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx index ace4a47b91019..44ba1ff23087e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx @@ -1,7 +1,7 @@ import { ToggleButton } from '@fluentui/react-headless-components-preview/toggle-button'; import descriptionMd from './ToggleButtonDescription.md'; -import toggleButtonCss from '../../../../../../bebop/components/toggle-button.module.css?raw'; +import toggleButtonCss from '../../../../../../theme/components/toggle-button.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ToggleButtonDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx index 6c1289119a153..20b5a5bdf9587 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx @@ -19,7 +19,7 @@ import { TextUnderlineRegular, } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/toolbar.module.css'; +import styles from '../../../../../../theme/components/toolbar.module.css'; import storySource from './ToolbarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const alignIcons = { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx index 698775cfc48c3..28be5e56d3e63 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Toolbar, ToolbarGroup, ToolbarToggleButton } from '@fluentui/react-headless-components-preview/toolbar'; import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/toolbar.module.css'; +import styles from '../../../../../../theme/components/toolbar.module.css'; import storySource from './ToolbarToggleButton.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Toggle = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx index a5ca041c9436e..15b574d1dc8ee 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx @@ -14,7 +14,7 @@ import { TextUnderlineRegular, } from '@fluentui/react-icons'; -import styles from '../../../../../../bebop/components/toolbar.module.css'; +import styles from '../../../../../../theme/components/toolbar.module.css'; import storySource from './ToolbarVertical.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx index a2a3577b43eb2..ffb1759e4fdc2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx @@ -8,7 +8,7 @@ import { } from '@fluentui/react-headless-components-preview/toolbar'; import descriptionMd from './ToolbarDescription.md'; -import toolbarCss from '../../../../../../bebop/components/toolbar.module.css?raw'; +import toolbarCss from '../../../../../../theme/components/toolbar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ToolbarDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx similarity index 74% rename from packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage.tsx rename to packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx index 0a3ec92faea21..7047ce554a4d2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopDocsPage.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx @@ -1,11 +1,11 @@ /** - * `BebopDocsPage` โ€” replaces Storybook's autodocs page so we can render a + * `HeadlessDocsPage` โ€” replaces Storybook's autodocs page so we can render a * **tabbed** "Show code" panel under each story (TSX + each CSS Module the * story uses). The deployed Fluent docs page (`FluentDocsPage`) hard-wires * `<Primary>` / `<Stories>` blocks whose Source can't be made multi-language, * so we re-implement the same layout (Title / Subtitle / Description / * primary canvas + source / ArgTypes / Stories heading / each story canvas + - * source) and swap the source block for our own `<BebopSource>`. The order + * source) and swap the source block for our own `<HeadlessSourcePanel>`. The order * mirrors `packages/react-components/react-storybook-addon/src/docs/FluentDocsPage.tsx` * so the page matches what's deployed at storybooks.fluentui.dev/headless. * @@ -24,7 +24,7 @@ import { Title, } from '@storybook/addon-docs/blocks'; -import { BebopSource } from './BebopSource'; +import { HeadlessSourcePanel } from './HeadlessSourcePanel'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type AnyStory = Record<string, any>; @@ -53,7 +53,19 @@ const nameToHash = (name: string) => .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, ''); -export const BebopDocsPage: React.FC = () => { +const disclaimerStyle: React.CSSProperties = { + margin: '20px 0 0', + padding: '12px 16px', + border: '1px solid #e1dfdd', + borderLeft: '3px solid #9b1f5a', + borderRadius: 6, + background: '#fdf6f9', + color: '#3c3c3c', + fontSize: 13, + lineHeight: 1.5, +}; + +export const HeadlessDocsPage: React.FC = () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const docsContext = React.useContext(DocsContext) as any; let stories: AnyStory[] = docsContext.componentStories(); @@ -76,19 +88,23 @@ export const BebopDocsPage: React.FC = () => { const remainingStories = stories.slice(1); return ( - <div className="sb-unstyled bebop-docs-page"> + <div className="sb-unstyled headless-docs-page"> {/* The `@fluentui/react-storybook-addon-export-to-sandbox` decorator looks for `.docblock-code-toggle` inside `.docs-story` of each story to anchor its "Open in Stackblitz" button. We keep Canvas's default sourceState ('hidden') so the native "Show code" toggle is rendered there too โ€” the Stackblitz button sits next to it inside the canvas footer (see - `BebopSource` for how its clicks drive our tabbed panel). + `HeadlessSourcePanel` for how its clicks drive our tabbed panel). */} - <style>{bebopDocsPageCss}</style> + <style>{headlessDocsPageCss}</style> <Title /> <Subtitle /> <Description /> + <aside style={disclaimerStyle} role="note"> + <strong>Heads up:</strong> headless components ship without default styles. The CSS shown in these stories is a + demonstration theme โ€” feel free to swap it out with your own design system. + </aside> {primaryStory && ( <> @@ -98,7 +114,7 @@ export const BebopDocsPage: React.FC = () => { </HeaderMdx> <Anchor storyId={primaryStory.id}> <Canvas of={primaryStory.moduleExport} /> - <BebopSource of={primaryStory.moduleExport} /> + <HeadlessSourcePanel of={primaryStory.moduleExport} /> </Anchor> </> )} @@ -116,7 +132,7 @@ export const BebopDocsPage: React.FC = () => { </HeaderMdx> <Description of={story.moduleExport} /> <Canvas of={story.moduleExport} /> - <BebopSource of={story.moduleExport} /> + <HeadlessSourcePanel of={story.moduleExport} /> </Anchor> ))} </> @@ -128,18 +144,18 @@ export const BebopDocsPage: React.FC = () => { // We let Storybook's native "Show code" toggle render inside the Canvas // footer (alongside the "Open in Stackblitz" button injected by // `@fluentui/react-storybook-addon-export-to-sandbox`). We hide only the -// expanded source pane it would normally reveal โ€” `<BebopSource>` listens to +// expanded source pane it would normally reveal โ€” `<HeadlessSourcePanel>` listens to // the native toggle's clicks and renders our tabbed panel **into the same -// canvas card** via a portal target (`.bebop-source-portal`). +// canvas card** via a portal target (`.headless-source-portal`). // // Storybook 9 sets a fixed `height` and `overflow: hidden` on `.sbdocs-preview` // so its border tightly hugs the rendered story; when our panel is expanded // inside the same card we need to release both so the code panel can flow. -const bebopDocsPageCss = ` -.bebop-docs-page .sbdocs-preview > *:not(.docs-story):not(.bebop-source-portal) { +const headlessDocsPageCss = ` +.headless-docs-page .sbdocs-preview > *:not(.docs-story):not(.headless-source-portal) { display: none !important; } -.bebop-docs-page .sbdocs-preview:has(> .bebop-source-portal:not(:empty)) { +.headless-docs-page .sbdocs-preview:has(> .headless-source-portal:not(:empty)) { height: auto !important; } `; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx similarity index 92% rename from packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx rename to packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx index 3fc9d5d25a784..5abcdc30c179f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/BebopSource.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx @@ -1,5 +1,5 @@ /** - * `BebopSource` โ€” a docs block that renders the "Show code" panel for a + * `HeadlessSourcePanel` โ€” a docs block that renders the "Show code" panel for a * headless story with **tabs**: one for the story TSX, one per CSS Module * referenced by the story's meta. Replaces Storybook's built-in single-blob * Source block (which can't show two languages side-by-side). @@ -12,9 +12,9 @@ * sitting together in the canvas footer (matching the deployed Fluent docs) * while still showing the multi-language tabbed panel below the canvas card. * - * Wired up by `BebopDocsPage`. The story's TSX comes from + * Wired up by `HeadlessDocsPage`. The story's TSX comes from * `parameters.docs.source.originalSource` (set via `withStorySource`); the CSS - * comes from `parameters.bebop.cssModules` (set via `withCssModuleSource`). + * comes from `parameters.theme.cssModules` (set via `withCssModuleSource`). */ /* eslint-disable @nx/workspace-no-restricted-globals -- Storybook docs block running in the manager iframe; uses DOM APIs to bridge to the native Canvas toggle that lives outside React. */ import * as React from 'react'; @@ -32,19 +32,19 @@ import { DocsContext, SourceContext, useOf, useSourceProps } from '@storybook/ad import { SyntaxHighlighter } from 'storybook/internal/components'; /** A CSS Module file surfaced as a tab in the code panel. */ -export interface BebopCssModule { +export interface CssModule { /** Display name shown on the tab (e.g. `button.module.css`). */ name: string; /** Raw CSS source for the module (typically imported via `?raw`). */ source: string; } -export interface BebopParameters { +export interface HeadlessSourceParameters { /** Extra CSS Module sources to surface as tabs after the story TSX tab. */ - cssModules?: BebopCssModule[]; + cssModules?: CssModule[]; } -interface BebopSourceProps { +interface HeadlessSourcePanelProps { /** Reference to the story being rendered (`story.moduleExport`). */ // eslint-disable-next-line @typescript-eslint/no-explicit-any of: any; @@ -144,7 +144,7 @@ function useNativeToggleState(storyId: string): boolean { /** * Find the canvas card (`.sbdocs-preview`) for `storyId` and append (once) a * portal target div as its last child. Returns the element when ready so - * `BebopSource` can render its tabbed panel **inside** the same bordered card + * `HeadlessSourcePanel` can render its tabbed panel **inside** the same bordered card * as the story preview, rather than as a detached block below it. */ function useCanvasPortalTarget(storyId: string): HTMLElement | null { @@ -165,12 +165,12 @@ function useCanvasPortalTarget(storyId: string): HTMLElement | null { if (!card) { return false; } - // Look for an existing target so multiple mounts of `BebopSource` (in + // Look for an existing target so multiple mounts of `HeadlessSourcePanel` (in // dev / fast-refresh) reuse the same node. - let existing = card.querySelector<HTMLDivElement>(':scope > .bebop-source-portal'); + let existing = card.querySelector<HTMLDivElement>(':scope > .headless-source-portal'); if (!existing) { existing = document.createElement('div'); - existing.className = 'bebop-source-portal'; + existing.className = 'headless-source-portal'; // Storybook's `.sbdocs-preview > div` global rules paint a near-black // background and drop shadow on direct children โ€” explicitly reset // both so the canvas card colour shows through behind our inset, @@ -207,7 +207,7 @@ function useCanvasPortalTarget(storyId: string): HTMLElement | null { return target; } -export const BebopSource: React.FC<BebopSourceProps> = ({ of }) => { +export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) => { const { story } = useOf(of || 'story', ['story']) as { story: AnyProps }; const docsContext = React.useContext(DocsContext); const sourceContext = React.useContext(SourceContext); @@ -222,13 +222,14 @@ export const BebopSource: React.FC<BebopSourceProps> = ({ of }) => { const tsxCode: string = typeof sourceProps.code === 'string' ? sourceProps.code : ''; const tsxLanguage: string = typeof sourceProps.language === 'string' ? sourceProps.language : 'tsx'; - const allCssModules: BebopCssModule[] = (story.parameters?.bebop as BebopParameters | undefined)?.cssModules ?? []; + const allCssModules: CssModule[] = + (story.parameters?.theme as HeadlessSourceParameters | undefined)?.cssModules ?? []; // The meta typically registers every CSS module a component touches across // all stories so the Stackblitz sandbox can bundle them. For the per-story // tab strip we only want the modules actually referenced in the displayed // TSX โ€” match by basename in import strings (e.g. `./styles/dialog.module.css` - // after `cleanStorySource`, or `bebop/components/dialog.module.css?raw`). + // after `cleanStorySource`, or `theme/components/dialog.module.css?raw`). const referencedBasenames = new Set(Array.from(tsxCode.matchAll(/([a-z][a-z0-9-]*\.module\.css)/gi), m => m[1])); const cssModules = referencedBasenames.size ? allCssModules.filter(m => referencedBasenames.has(m.name)) diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts index 3a0eb0a3288de..889680f5fb548 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -1,17 +1,17 @@ /** * Story meta helper โ€” registers the CSS Module source(s) a story relies on so: * - * 1. The custom docs page (`BebopDocsPage` โ†’ `BebopSource`) can surface them + * 1. The custom docs page (`HeadlessDocsPage` โ†’ `HeadlessSourcePanel`) can surface them * as tabs in the "Show code" panel. * 2. The "Open in Stackblitz" button (provided by * `@fluentui/react-storybook-addon-export-to-sandbox`) can bundle them โ€” - * together with `bebop/tokens.css` โ€” into the generated sandbox so the + * together with `theme/tokens.css` โ€” into the generated sandbox so the * example renders with the correct theme out of the box. * * Spread the result into a story's `parameters` object: * * ```tsx - * import buttonCss from '../../../../../../bebop/components/button.module.css?raw'; + * import buttonCss from '../../../../../../theme/components/button.module.css?raw'; * import { withCssModuleSource } from '../_helpers/withCssModuleSource'; * * export default { @@ -31,11 +31,11 @@ // Loaded via the `?raw` resourceQuery rule configured in `.storybook/main.js`. // Bundling tokens.css inline lets the Stackblitz scaffold include them without // requiring story authors to wire imports manually. -import tokensCss from '../../../../../../bebop/tokens.css?raw'; +import tokensCss from '../../../../../../theme/tokens.css?raw'; -import type { BebopCssModule, BebopParameters } from './BebopSource'; +import type { CssModule, HeadlessSourceParameters } from './HeadlessSourcePanel'; -export type { BebopCssModule } from './BebopSource'; +export type { CssModule } from './HeadlessSourcePanel'; /** * Minimal local mirror of the `SandboxContext` shape from @@ -59,10 +59,10 @@ interface ExportToSandboxFragment { } export function withCssModuleSource( - ...modules: BebopCssModule[] -): { bebop: BebopParameters } & ExportToSandboxFragment { + ...modules: CssModule[] +): { theme: HeadlessSourceParameters } & ExportToSandboxFragment { return { - bebop: { cssModules: modules }, + theme: { cssModules: modules }, exportToSandbox: { transformFiles: (files, ctx) => buildSandboxFiles(files, ctx, modules), }, @@ -71,11 +71,11 @@ export function withCssModuleSource( /** * The story file imports each CSS Module via a long relative path that points - * back to `bebop/components/<name>.module.css`. In the sandbox, that path + * back to `theme/components/<name>.module.css`. In the sandbox, that path * doesn't exist โ€” so we: * * 1. Drop a flat copy of `tokens.css` and each module under `src/styles/`. - * 2. Rewrite every `bebop/components/<โ€ฆ>.module.css` import in the story + * 2. Rewrite every `theme/components/<โ€ฆ>.module.css` import in the story * file to `./styles/<basename>` (or `../styles/<basename>` from `App`). * 3. Inject `import './styles/tokens.css'` at the top of `src/App.tsx` * so the design tokens cascade onto the rendered example. @@ -83,7 +83,7 @@ export function withCssModuleSource( function buildSandboxFiles( files: Record<string, string>, _ctx: SandboxContext, - modules: BebopCssModule[], + modules: CssModule[], ): Record<string, string> { const next = { ...files }; @@ -108,10 +108,10 @@ function buildSandboxFiles( } // Story file lives at `src/example.tsx`; rewrite the deeply-relative - // `bebop/components/<file>.module.css` import to a sibling path. + // `theme/components/<file>.module.css` import to a sibling path. if (typeof example === 'string') { next['src/example.tsx'] = example.replace( - /(['"])(?:\.\.\/)+bebop\/components\/([^'"]+\.module\.css)\1/g, + /(['"])(?:\.\.\/)+theme\/components\/([^'"]+\.module\.css)\1/g, (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, ); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts index 7e0fb5e90b4d3..6b3845f6b63a6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts @@ -46,12 +46,12 @@ function cleanStorySource(source: string): string { .replace(/^import\s+\w+\s+from\s+['"]\..*?\.stories\?raw['"];\s*\r?\n/m, '') .replace(/^import\s*\{\s*withStorySource\s*\}\s*from\s+['"][^'"]+['"];\s*\r?\n/m, '') .replace(/^\w+\.parameters\s*=\s*withStorySource\([\s\S]*?\);\s*\r?\n?/m, '') - // Rewrite the deeply-relative `bebop/components/<file>.module.css` paths + // Rewrite the deeply-relative `theme/components/<file>.module.css` paths // to a colocated `./styles/<file>.module.css` so the snippet matches what // the user actually gets in the Stackblitz sandbox (and is paste-ready // into a project that follows the same colocation convention). .replace( - /(['"])(?:\.\.\/)+bebop\/components\/([^'"]+\.module\.css)\1/g, + /(['"])(?:\.\.\/)+theme\/components\/([^'"]+\.module\.css)\1/g, (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, ) .replace(/\n{3,}/g, '\n\n') @@ -85,7 +85,7 @@ export function withStorySource(storySource: string, extra: Parameters = {}): Pa // `Default.parameters.fullSource = '<post-babel source>';` statement at the // end of the compiled story file. That post-babel source has had every // relative import stripped โ€” including our `import styles from - // '../../../../../../bebop/components/<file>.module.css';` line โ€” which + // '../../../../../../theme/components/<file>.module.css';` line โ€” which // breaks the "Open in Stackblitz" sandbox (the example references `styles` // but never imports it). // diff --git a/bebop/components/accordion.module.css b/theme/components/accordion.module.css similarity index 100% rename from bebop/components/accordion.module.css rename to theme/components/accordion.module.css diff --git a/bebop/components/avatar.module.css b/theme/components/avatar.module.css similarity index 100% rename from bebop/components/avatar.module.css rename to theme/components/avatar.module.css diff --git a/bebop/components/badge.module.css b/theme/components/badge.module.css similarity index 100% rename from bebop/components/badge.module.css rename to theme/components/badge.module.css diff --git a/bebop/components/breadcrumb.module.css b/theme/components/breadcrumb.module.css similarity index 100% rename from bebop/components/breadcrumb.module.css rename to theme/components/breadcrumb.module.css diff --git a/bebop/components/button.module.css b/theme/components/button.module.css similarity index 98% rename from bebop/components/button.module.css rename to theme/components/button.module.css index 351fc1750adaa..526a95a413ec8 100644 --- a/bebop/components/button.module.css +++ b/theme/components/button.module.css @@ -1,4 +1,4 @@ -/* Bebop button โ€” pill-shaped, monochrome */ +/* button โ€” pill-shaped, monochrome */ .button { display: inline-flex; align-items: center; diff --git a/bebop/components/chat-input.module.css b/theme/components/chat-input.module.css similarity index 95% rename from bebop/components/chat-input.module.css rename to theme/components/chat-input.module.css index 3d45d2c979925..abd31b6b1646c 100644 --- a/bebop/components/chat-input.module.css +++ b/theme/components/chat-input.module.css @@ -1,4 +1,4 @@ -/* Bebop chat-input โ€” borderless field on a soft surface, +/* chat-input โ€” borderless field on a soft surface, with a hairline bottom rule and inline icon buttons. Layout: [+] [text ยท placeholder text] [mic] [waveform | send] @@ -101,7 +101,7 @@ } /* Underline variant โ€” borderless, thin bottom rule (matches the larger - Bebop chat input shown in the canvas screenshot). */ + chat input shown in the canvas screenshot). */ .chatUnderline { background: transparent; diff --git a/bebop/components/checkbox.module.css b/theme/components/checkbox.module.css similarity index 100% rename from bebop/components/checkbox.module.css rename to theme/components/checkbox.module.css diff --git a/bebop/components/dialog.module.css b/theme/components/dialog.module.css similarity index 100% rename from bebop/components/dialog.module.css rename to theme/components/dialog.module.css diff --git a/bebop/components/divider.module.css b/theme/components/divider.module.css similarity index 100% rename from bebop/components/divider.module.css rename to theme/components/divider.module.css diff --git a/bebop/components/field.module.css b/theme/components/field.module.css similarity index 100% rename from bebop/components/field.module.css rename to theme/components/field.module.css diff --git a/bebop/components/input.module.css b/theme/components/input.module.css similarity index 96% rename from bebop/components/input.module.css rename to theme/components/input.module.css index 9f4474fc2b133..7ec8e587d49c7 100644 --- a/bebop/components/input.module.css +++ b/theme/components/input.module.css @@ -1,4 +1,4 @@ -/* Bebop input wrapper โ€” clean rounded, light border */ +/* input wrapper โ€” clean rounded, light border */ .wrap { display: flex; align-items: center; diff --git a/bebop/components/link.module.css b/theme/components/link.module.css similarity index 100% rename from bebop/components/link.module.css rename to theme/components/link.module.css diff --git a/bebop/components/message-bar.module.css b/theme/components/message-bar.module.css similarity index 100% rename from bebop/components/message-bar.module.css rename to theme/components/message-bar.module.css diff --git a/bebop/components/progress-bar.module.css b/theme/components/progress-bar.module.css similarity index 100% rename from bebop/components/progress-bar.module.css rename to theme/components/progress-bar.module.css diff --git a/bebop/components/radio-group.module.css b/theme/components/radio-group.module.css similarity index 100% rename from bebop/components/radio-group.module.css rename to theme/components/radio-group.module.css diff --git a/bebop/components/rating-display.module.css b/theme/components/rating-display.module.css similarity index 100% rename from bebop/components/rating-display.module.css rename to theme/components/rating-display.module.css diff --git a/bebop/components/rating.module.css b/theme/components/rating.module.css similarity index 100% rename from bebop/components/rating.module.css rename to theme/components/rating.module.css diff --git a/bebop/components/select.module.css b/theme/components/select.module.css similarity index 100% rename from bebop/components/select.module.css rename to theme/components/select.module.css diff --git a/bebop/components/skeleton.module.css b/theme/components/skeleton.module.css similarity index 100% rename from bebop/components/skeleton.module.css rename to theme/components/skeleton.module.css diff --git a/bebop/components/slider.module.css b/theme/components/slider.module.css similarity index 100% rename from bebop/components/slider.module.css rename to theme/components/slider.module.css diff --git a/bebop/components/spin-button.module.css b/theme/components/spin-button.module.css similarity index 100% rename from bebop/components/spin-button.module.css rename to theme/components/spin-button.module.css diff --git a/bebop/components/spinner.module.css b/theme/components/spinner.module.css similarity index 100% rename from bebop/components/spinner.module.css rename to theme/components/spinner.module.css diff --git a/bebop/components/switch.module.css b/theme/components/switch.module.css similarity index 100% rename from bebop/components/switch.module.css rename to theme/components/switch.module.css diff --git a/bebop/components/tab-list.module.css b/theme/components/tab-list.module.css similarity index 100% rename from bebop/components/tab-list.module.css rename to theme/components/tab-list.module.css diff --git a/bebop/components/textarea.module.css b/theme/components/textarea.module.css similarity index 100% rename from bebop/components/textarea.module.css rename to theme/components/textarea.module.css diff --git a/bebop/components/toggle-button.module.css b/theme/components/toggle-button.module.css similarity index 100% rename from bebop/components/toggle-button.module.css rename to theme/components/toggle-button.module.css diff --git a/bebop/components/toolbar.module.css b/theme/components/toolbar.module.css similarity index 100% rename from bebop/components/toolbar.module.css rename to theme/components/toolbar.module.css diff --git a/bebop/tokens.css b/theme/tokens.css similarity index 96% rename from bebop/tokens.css rename to theme/tokens.css index 17368ac44c6ed..24a88d06ceb8b 100644 --- a/bebop/tokens.css +++ b/theme/tokens.css @@ -1,7 +1,7 @@ /* ------------------------------------------------------------------ - * Bebop design tokens + * Design tokens * - * Mapped from FOUNDATIONS pages of the Bebop web components Figma file: + * Mapped from FOUNDATIONS pages of the design Figma file: * - color algorithm/primitives/generics * - elevation * - gap & padding generics @@ -29,7 +29,7 @@ --text-faint: #a1a1aa; --text-on-accent: #ffffff; - /* accent โ€” primary action is rich Bebop magenta on white (Figma --prmt-color-red-45) */ + /* accent โ€” primary action is rich the brand magenta on white (Figma --prmt-color-red-45) */ --accent: #9b1f5a; --accent-strong: #7a1a4a; --accent-soft: #fff1f3; From 01fd2c2af830bc4a45442c848441e622f83cea7d Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 21:29:29 +0200 Subject: [PATCH 03/25] refactor(headless-docsite): colocate CSS modules with their stories 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> --- .../stories/README.md | 8 ++++---- .../Accordion/AccordionCollapsible.stories.tsx | 2 +- .../src/Accordion/AccordionDefault.stories.tsx | 2 +- .../stories/src/Accordion}/accordion.module.css | 0 .../stories/src/Accordion/index.stories.tsx | 2 +- .../stories/src/Avatar/AvatarDefault.stories.tsx | 2 +- .../stories/src/Avatar}/avatar.module.css | 0 .../stories/src/Avatar/index.stories.tsx | 2 +- .../stories/src/Badge/BadgeDefault.stories.tsx | 2 +- .../stories/src/Badge}/badge.module.css | 0 .../stories/src/Badge/index.stories.tsx | 2 +- .../src/Breadcrumb/BreadcrumbDefault.stories.tsx | 2 +- .../src/Breadcrumb}/breadcrumb.module.css | 0 .../stories/src/Breadcrumb/index.stories.tsx | 2 +- .../stories/src/Button/ButtonDefault.stories.tsx | 2 +- .../stories/src/Button}/button.module.css | 0 .../stories/src/Button/index.stories.tsx | 2 +- .../src/Checkbox/CheckboxDefault.stories.tsx | 2 +- .../stories/src/Checkbox}/checkbox.module.css | 0 .../stories/src/Checkbox/index.stories.tsx | 2 +- .../stories/src/Dialog/DialogAlert.stories.tsx | 2 +- .../src/Dialog/DialogControlled.stories.tsx | 2 +- .../stories/src/Dialog/DialogDefault.stories.tsx | 2 +- .../src/Dialog/DialogKeepMounted.stories.tsx | 4 ++-- .../stories/src/Dialog/DialogNested.stories.tsx | 2 +- .../src/Dialog/DialogNoTrigger.stories.tsx | 2 +- .../src/Dialog/DialogNonModal.stories.tsx | 4 ++-- .../src/Dialog/DialogWithCloseButton.stories.tsx | 4 ++-- .../stories/src/Dialog}/dialog.module.css | 0 .../stories/src/Dialog/index.stories.tsx | 8 ++++---- .../src/Divider/DividerDefault.stories.tsx | 2 +- .../src/Divider/DividerVertical.stories.tsx | 2 +- .../stories/src/Divider}/divider.module.css | 0 .../stories/src/Divider/index.stories.tsx | 2 +- .../stories/src/Field/FieldDefault.stories.tsx | 4 ++-- .../stories/src/Field}/field.module.css | 0 .../stories/src/Field/index.stories.tsx | 4 ++-- .../stories/src/Input/InputBasic.stories.tsx | 2 +- .../stories/src/Input/InputDefault.stories.tsx | 2 +- .../stories/src/Input}/chat-input.module.css | 0 .../stories/src/Input/index.stories.tsx | 4 ++-- .../stories/src/Input}/input.module.css | 0 .../stories/src/Link/LinkDefault.stories.tsx | 2 +- .../stories/src/Link/index.stories.tsx | 2 +- .../stories/src/Link}/link.module.css | 0 .../src/MessageBar/MessageBarDefault.stories.tsx | 4 ++-- .../src/MessageBar/MessageBarIntent.stories.tsx | 4 ++-- .../stories/src/MessageBar/index.stories.tsx | 4 ++-- .../src/MessageBar}/message-bar.module.css | 0 .../ProgressBar/ProgressBarDefault.stories.tsx | 2 +- .../stories/src/ProgressBar/index.stories.tsx | 2 +- .../src/ProgressBar}/progress-bar.module.css | 0 .../src/RadioGroup/RadioGroupDefault.stories.tsx | 2 +- .../stories/src/RadioGroup/index.stories.tsx | 2 +- .../src/RadioGroup}/radio-group.module.css | 0 .../stories/src/Rating/RatingDefault.stories.tsx | 2 +- .../stories/src/Rating/index.stories.tsx | 2 +- .../stories/src/Rating}/rating.module.css | 0 .../RatingDisplayCompact.stories.tsx | 2 +- .../RatingDisplayDefault.stories.tsx | 2 +- .../stories/src/RatingDisplay/index.stories.tsx | 2 +- .../src/RatingDisplay}/rating-display.module.css | 0 .../src/SearchBox/SearchBoxDefault.stories.tsx | 2 +- .../stories/src/SearchBox/index.stories.tsx | 2 +- .../stories/src/Select/SelectDefault.stories.tsx | 4 ++-- .../stories/src/Select/index.stories.tsx | 4 ++-- .../stories/src/Select}/select.module.css | 0 .../src/Skeleton/SkeletonDefault.stories.tsx | 2 +- .../stories/src/Skeleton/index.stories.tsx | 2 +- .../stories/src/Skeleton}/skeleton.module.css | 0 .../stories/src/Slider/SliderDefault.stories.tsx | 2 +- .../stories/src/Slider/index.stories.tsx | 2 +- .../stories/src/Slider}/slider.module.css | 0 .../src/SpinButton/SpinButtonDefault.stories.tsx | 4 ++-- .../stories/src/SpinButton/index.stories.tsx | 4 ++-- .../src/SpinButton}/spin-button.module.css | 0 .../src/Spinner/SpinnerDefault.stories.tsx | 2 +- .../src/Spinner/SpinnerLabels.stories.tsx | 2 +- .../stories/src/Spinner/index.stories.tsx | 2 +- .../stories/src/Spinner}/spinner.module.css | 0 .../stories/src/Switch/SwitchDefault.stories.tsx | 2 +- .../stories/src/Switch/index.stories.tsx | 2 +- .../stories/src/Switch}/switch.module.css | 0 .../src/TabList/TabListDefault.stories.tsx | 2 +- .../stories/src/TabList/index.stories.tsx | 2 +- .../stories/src/TabList}/tab-list.module.css | 0 .../src/Textarea/TextareaDefault.stories.tsx | 2 +- .../stories/src/Textarea/index.stories.tsx | 2 +- .../stories/src/Textarea}/textarea.module.css | 0 .../ToggleButton/ToggleButtonDefault.stories.tsx | 2 +- .../stories/src/ToggleButton/index.stories.tsx | 2 +- .../src/ToggleButton}/toggle-button.module.css | 0 .../src/Toolbar/ToolbarDefault.stories.tsx | 2 +- .../src/Toolbar/ToolbarToggleButton.stories.tsx | 2 +- .../src/Toolbar/ToolbarVertical.stories.tsx | 2 +- .../stories/src/Toolbar/index.stories.tsx | 2 +- .../stories/src/Toolbar}/toolbar.module.css | 0 .../stories/src/_helpers/HeadlessSourcePanel.tsx | 2 +- .../stories/src/_helpers/withCssModuleSource.ts | 16 ++++++++-------- .../stories/src/_helpers/withStorySource.ts | 8 ++++---- 100 files changed, 102 insertions(+), 102 deletions(-) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Accordion}/accordion.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Avatar}/avatar.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Badge}/badge.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Breadcrumb}/breadcrumb.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Button}/button.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Checkbox}/checkbox.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Dialog}/dialog.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Divider}/divider.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Field}/field.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Input}/chat-input.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Input}/input.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Link}/link.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/MessageBar}/message-bar.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/ProgressBar}/progress-bar.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/RadioGroup}/radio-group.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Rating}/rating.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/RatingDisplay}/rating-display.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Select}/select.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Skeleton}/skeleton.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Slider}/slider.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/SpinButton}/spin-button.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Spinner}/spinner.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Switch}/switch.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/TabList}/tab-list.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Textarea}/textarea.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/ToggleButton}/toggle-button.module.css (100%) rename {theme/components => packages/react-components/react-headless-components-preview/stories/src/Toolbar}/toolbar.module.css (100%) diff --git a/packages/react-components/react-headless-components-preview/stories/README.md b/packages/react-components/react-headless-components-preview/stories/README.md index c3df7a57b4cf5..2a423ff61afab 100644 --- a/packages/react-components/react-headless-components-preview/stories/README.md +++ b/packages/react-components/react-headless-components-preview/stories/README.md @@ -35,7 +35,7 @@ For each new component: 1. Create the headless component under `library/src/components/<Name>/` (out of scope for this guide). -2. Add a CSS Module at `theme/components/<name>.module.css` driven entirely by +2. Add a CSS Module at `stories/src/<Component>/<name>.module.css` driven entirely by `var(--โ€ฆ)` from `theme/tokens.css`. **Do not hardcode colors, sizes, or typography.** 3. Add a stories folder at `stories/src/<Name>/` containing: @@ -52,7 +52,7 @@ the CSS Module, and stories pull both together. ```tsx import * as React from 'react'; import { MyComponent } from '@fluentui/react-headless-components-preview/my-component'; -import styles from '../../../../../../theme/components/my-component.module.css'; +import styles from './my-component.module.css'; export const Default = (): React.ReactNode => <MyComponent className={styles.root} />; ``` @@ -79,7 +79,7 @@ helper at `stories/src/_helpers/withCssModuleSource.ts` does the stitching: import { MyComponent } from '@fluentui/react-headless-components-preview/my-component'; import descriptionMd from './MyComponentDescription.md'; -import myComponentCss from '../../../../../../theme/components/my-component.module.css?raw'; +import myComponentCss from './my-component.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './MyComponentDefault.stories'; @@ -193,7 +193,7 @@ These are the things that took time to discover. Keep them in mind: | Path | Purpose | | ---------------------------------------------------- | ------------------------------------------------------------------- | | `theme/tokens.css` | CSS custom properties, light + dark. Imported once in `preview.js`. | -| `theme/components/<name>.module.css` | Per-component scoped styles. | +| `stories/src/<Component>/<name>.module.css` | Per-component scoped styles. | | `stories/src/<Name>/<Name>Default.stories.tsx` | Default story body using CSS Module classes. | | `stories/src/<Name>/<Name>Description.md` | Component description shown in the Docs panel. | | `stories/src/<Name>/index.stories.tsx` | Meta + `parameters.docs.source.transform` wiring. | diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx index 00fbce7e41e07..eb2aa37b81e5b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/accordion.module.css'; +import styles from './accordion.module.css'; import storySource from './AccordionCollapsible.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx index 8edc91202b490..9b05b34029ec6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/accordion.module.css'; +import styles from './accordion.module.css'; import storySource from './AccordionDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/theme/components/accordion.module.css b/packages/react-components/react-headless-components-preview/stories/src/Accordion/accordion.module.css similarity index 100% rename from theme/components/accordion.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Accordion/accordion.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx index e09510f79461b..78e8911a7112f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/index.stories.tsx @@ -6,7 +6,7 @@ import { } from '@fluentui/react-headless-components-preview/accordion'; import descriptionMd from './AccordionDescription.md'; -import accordionCss from '../../../../../../theme/components/accordion.module.css?raw'; +import accordionCss from './accordion.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './AccordionDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx index 7c80678336f7f..9e874beae01af 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Avatar } from '@fluentui/react-headless-components-preview/avatar'; -import styles from '../../../../../../theme/components/avatar.module.css'; +import styles from './avatar.module.css'; import storySource from './AvatarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/theme/components/avatar.module.css b/packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css similarity index 100% rename from theme/components/avatar.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx index 99703ff29afe7..02402ff7c8dfb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/index.stories.tsx @@ -1,7 +1,7 @@ import { Avatar } from '@fluentui/react-headless-components-preview/avatar'; import descriptionMd from './AvatarDescription.md'; -import avatarCss from '../../../../../../theme/components/avatar.module.css?raw'; +import avatarCss from './avatar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './AvatarDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx index d02cf5b4be9b0..6ad096e6bfd91 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Badge } from '@fluentui/react-headless-components-preview/badge'; -import styles from '../../../../../../theme/components/badge.module.css'; +import styles from './badge.module.css'; import storySource from './BadgeDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/theme/components/badge.module.css b/packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css similarity index 100% rename from theme/components/badge.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx index 3e44d71d5acea..44141410ff286 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/index.stories.tsx @@ -1,7 +1,7 @@ import { Badge } from '@fluentui/react-headless-components-preview/badge'; import descriptionMd from './BadgeDescription.md'; -import badgeCss from '../../../../../../theme/components/badge.module.css?raw'; +import badgeCss from './badge.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './BadgeDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx index 6390876628736..8ad3e8e4d8798 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx @@ -7,7 +7,7 @@ import { } from '@fluentui/react-headless-components-preview/breadcrumb'; import { ChevronRightRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/breadcrumb.module.css'; +import styles from './breadcrumb.module.css'; import storySource from './BreadcrumbDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/theme/components/breadcrumb.module.css b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/breadcrumb.module.css similarity index 100% rename from theme/components/breadcrumb.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/breadcrumb.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx index 34e7739a3cfa5..db81817c52a04 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/index.stories.tsx @@ -6,7 +6,7 @@ import { } from '@fluentui/react-headless-components-preview/breadcrumb'; import descriptionMd from './BreadcrumbDescription.md'; -import breadcrumbCss from '../../../../../../theme/components/breadcrumb.module.css?raw'; +import breadcrumbCss from './breadcrumb.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './BreadcrumbDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx index c05dd08fd19f2..adfa217c1cf88 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Button } from '@fluentui/react-headless-components-preview/button'; import { AddRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/button.module.css'; +import styles from './button.module.css'; import storySource from './ButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; diff --git a/theme/components/button.module.css b/packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css similarity index 100% rename from theme/components/button.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx index 19327c424d8a0..c5e8a5b52cc8b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/index.stories.tsx @@ -1,7 +1,7 @@ import { Button } from '@fluentui/react-headless-components-preview/button'; import descriptionMd from './ButtonDescription.md'; -import buttonCss from '../../../../../../theme/components/button.module.css?raw'; +import buttonCss from './button.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ButtonDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx index 4590ec27afb40..6e7ba12e86480 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import { CheckmarkRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/checkbox.module.css'; +import styles from './checkbox.module.css'; import storySource from './CheckboxDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/theme/components/checkbox.module.css b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/checkbox.module.css similarity index 100% rename from theme/components/checkbox.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Checkbox/checkbox.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx index 596fcb5772a2c..73e5c58b7d946 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/index.stories.tsx @@ -1,7 +1,7 @@ import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import descriptionMd from './CheckboxDescription.md'; -import checkboxCss from '../../../../../../theme/components/checkbox.module.css?raw'; +import checkboxCss from './checkbox.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './CheckboxDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx index b5854109699fb..1149db08affa7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../theme/components/dialog.module.css'; +import styles from './dialog.module.css'; import storySource from './DialogAlert.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx index df499cea19cd1..b5884c62a6c5d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../theme/components/dialog.module.css'; +import styles from './dialog.module.css'; import storySource from './DialogControlled.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx index 2ba6ece33cb13..ab2c8578dd51b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../theme/components/dialog.module.css'; +import styles from './dialog.module.css'; import storySource from './DialogDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx index 8b621de928023..11ca6bf4b62fb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx @@ -9,8 +9,8 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; -import styles from '../../../../../../theme/components/dialog.module.css'; -import textareaStyles from '../../../../../../theme/components/textarea.module.css'; +import styles from './dialog.module.css'; +import textareaStyles from '../Textarea/textarea.module.css'; import storySource from './DialogKeepMounted.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx index 4648917a0c4f5..37db865f60447 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx @@ -8,7 +8,7 @@ import { DialogTrigger, } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../theme/components/dialog.module.css'; +import styles from './dialog.module.css'; import storySource from './DialogNested.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx index 278a14ca0d487..a1142d371f4ff 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx @@ -8,7 +8,7 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import type { DialogOpenChangeData } from '@fluentui/react-headless-components-preview/dialog'; -import styles from '../../../../../../theme/components/dialog.module.css'; +import styles from './dialog.module.css'; import storySource from './DialogNoTrigger.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx index 454775c0ee5b7..ac1dfb95a6d62 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx @@ -9,8 +9,8 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import { Input } from '@fluentui/react-headless-components-preview/input'; -import styles from '../../../../../../theme/components/dialog.module.css'; -import inputStyles from '../../../../../../theme/components/input.module.css'; +import styles from './dialog.module.css'; +import inputStyles from '../Input/input.module.css'; import storySource from './DialogNonModal.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx index cb233cd5fd67e..d84debcdeece4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx @@ -10,8 +10,8 @@ import { import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import { CheckmarkRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/dialog.module.css'; -import checkboxStyles from '../../../../../../theme/components/checkbox.module.css'; +import styles from './dialog.module.css'; +import checkboxStyles from '../Checkbox/checkbox.module.css'; import storySource from './DialogWithCloseButton.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; /** diff --git a/theme/components/dialog.module.css b/packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css similarity index 100% rename from theme/components/dialog.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx index 5cc47a10bfb1f..77ec0a89c950c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/index.stories.tsx @@ -8,10 +8,10 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import descriptionMd from './DialogDescription.md'; -import dialogCss from '../../../../../../theme/components/dialog.module.css?raw'; -import checkboxCss from '../../../../../../theme/components/checkbox.module.css?raw'; -import textareaCss from '../../../../../../theme/components/textarea.module.css?raw'; -import inputCss from '../../../../../../theme/components/input.module.css?raw'; +import dialogCss from './dialog.module.css?raw'; +import checkboxCss from '../Checkbox/checkbox.module.css?raw'; +import textareaCss from '../Textarea/textarea.module.css?raw'; +import inputCss from '../Input/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './DialogDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx index b08bfafd3c2cf..b2d3718b62351 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Divider } from '@fluentui/react-headless-components-preview/divider'; -import styles from '../../../../../../theme/components/divider.module.css'; +import styles from './divider.module.css'; import storySource from './DividerDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx index da9e8af685307..db02a24d29c9e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Divider } from '@fluentui/react-headless-components-preview/divider'; import { CircleRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/divider.module.css'; +import styles from './divider.module.css'; import storySource from './DividerVertical.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( diff --git a/theme/components/divider.module.css b/packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css similarity index 100% rename from theme/components/divider.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx index ca0de2d53c1f3..983c7e25e7119 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/index.stories.tsx @@ -1,7 +1,7 @@ import { Divider } from '@fluentui/react-headless-components-preview/divider'; import descriptionMd from './DividerDescription.md'; -import dividerCss from '../../../../../../theme/components/divider.module.css?raw'; +import dividerCss from './divider.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './DividerDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx index be66a09e6703b..25a9b6ee5ff69 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx @@ -3,8 +3,8 @@ import { Field } from '@fluentui/react-headless-components-preview/field'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { ErrorCircleRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../theme/components/field.module.css'; -import inputStyles from '../../../../../../theme/components/input.module.css'; +import fieldStyles from './field.module.css'; +import inputStyles from '../Input/input.module.css'; import storySource from './FieldDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/theme/components/field.module.css b/packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css similarity index 100% rename from theme/components/field.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx index 762133a2ad580..fdfe84c35e50f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/index.stories.tsx @@ -1,8 +1,8 @@ import { Field } from '@fluentui/react-headless-components-preview/field'; import descriptionMd from './FieldDescription.md'; -import fieldCss from '../../../../../../theme/components/field.module.css?raw'; -import inputCss from '../../../../../../theme/components/input.module.css?raw'; +import fieldCss from './field.module.css?raw'; +import inputCss from '../Input/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './FieldDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx index f6f84a8325b21..51874980f90aa 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { SearchRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/input.module.css'; +import styles from './input.module.css'; import storySource from './InputBasic.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Basic = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx index 834cb5fe580a4..d565438f465d7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Input } from '@fluentui/react-headless-components-preview/input'; import { AddRegular, MicRegular, MicPulseRegular, SendRegular } from '@fluentui/react-icons'; -import chatStyles from '../../../../../../theme/components/chat-input.module.css'; +import chatStyles from './chat-input.module.css'; import storySource from './InputDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/theme/components/chat-input.module.css b/packages/react-components/react-headless-components-preview/stories/src/Input/chat-input.module.css similarity index 100% rename from theme/components/chat-input.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Input/chat-input.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx index 6e919eb258d0c..a432828a69a54 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/index.stories.tsx @@ -1,8 +1,8 @@ import { Input } from '@fluentui/react-headless-components-preview/input'; import descriptionMd from './InputDescription.md'; -import inputCss from '../../../../../../theme/components/input.module.css?raw'; -import chatInputCss from '../../../../../../theme/components/chat-input.module.css?raw'; +import inputCss from './input.module.css?raw'; +import chatInputCss from './chat-input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './InputDefault.stories'; diff --git a/theme/components/input.module.css b/packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css similarity index 100% rename from theme/components/input.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx index 504a77d8b87f3..c8fd9e167daad 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Link } from '@fluentui/react-headless-components-preview/link'; -import styles from '../../../../../../theme/components/link.module.css'; +import styles from './link.module.css'; import storySource from './LinkDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx index a49ffa40c9f6c..f9bc1d45c2558 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/index.stories.tsx @@ -1,7 +1,7 @@ import { Link } from '@fluentui/react-headless-components-preview/link'; import descriptionMd from './LinkDescription.md'; -import linkCss from '../../../../../../theme/components/link.module.css?raw'; +import linkCss from './link.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './LinkDefault.stories'; diff --git a/theme/components/link.module.css b/packages/react-components/react-headless-components-preview/stories/src/Link/link.module.css similarity index 100% rename from theme/components/link.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Link/link.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx index 37f3817080564..81a2221a7992d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx @@ -8,8 +8,8 @@ import { } from '@fluentui/react-headless-components-preview/message-bar'; import { DismissRegular, InfoRegular } from '@fluentui/react-icons'; -import linkStyles from '../../../../../../theme/components/link.module.css'; -import styles from '../../../../../../theme/components/message-bar.module.css'; +import linkStyles from '../Link/link.module.css'; +import styles from './message-bar.module.css'; import storySource from './MessageBarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx index 04e05d5e66ab8..f895b39b52e67 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx @@ -3,8 +3,8 @@ import { Link } from '@fluentui/react-headless-components-preview/link'; import { MessageBar, MessageBarBody, MessageBarTitle } from '@fluentui/react-headless-components-preview/message-bar'; import { CheckmarkCircleRegular, ErrorCircleRegular, InfoRegular, WarningRegular } from '@fluentui/react-icons'; -import linkStyles from '../../../../../../theme/components/link.module.css'; -import styles from '../../../../../../theme/components/message-bar.module.css'; +import linkStyles from '../Link/link.module.css'; +import styles from './message-bar.module.css'; import storySource from './MessageBarIntent.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const items = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx index 99ef2eab81d51..87360d5bbe8c0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/index.stories.tsx @@ -6,8 +6,8 @@ import { } from '@fluentui/react-headless-components-preview/message-bar'; import descriptionMd from './MessageBarDescription.md'; -import messageBarCss from '../../../../../../theme/components/message-bar.module.css?raw'; -import linkCss from '../../../../../../theme/components/link.module.css?raw'; +import messageBarCss from './message-bar.module.css?raw'; +import linkCss from '../Link/link.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './MessageBarDefault.stories'; diff --git a/theme/components/message-bar.module.css b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css similarity index 100% rename from theme/components/message-bar.module.css rename to packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx index b2f90241aa3ab..32455f33138b1 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { ProgressBar } from '@fluentui/react-headless-components-preview/progress-bar'; -import styles from '../../../../../../theme/components/progress-bar.module.css'; +import styles from './progress-bar.module.css'; import storySource from './ProgressBarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx index c9f1fb122abb7..0094fe2b163b4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/index.stories.tsx @@ -1,7 +1,7 @@ import { ProgressBar } from '@fluentui/react-headless-components-preview/progress-bar'; import descriptionMd from './ProgressBarDescription.md'; -import progressBarCss from '../../../../../../theme/components/progress-bar.module.css?raw'; +import progressBarCss from './progress-bar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ProgressBarDefault.stories'; diff --git a/theme/components/progress-bar.module.css b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css similarity index 100% rename from theme/components/progress-bar.module.css rename to packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx index e4947c4c22cd3..b1713108b8c3c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { RadioGroup, Radio } from '@fluentui/react-headless-components-preview/radio-group'; -import styles from '../../../../../../theme/components/radio-group.module.css'; +import styles from './radio-group.module.css'; import storySource from './RadioGroupDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const plans = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx index b643f86fc37f3..084baaa1cf748 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/index.stories.tsx @@ -1,7 +1,7 @@ import { RadioGroup, Radio } from '@fluentui/react-headless-components-preview/radio-group'; import descriptionMd from './RadioGroupDescription.md'; -import radioGroupCss from '../../../../../../theme/components/radio-group.module.css?raw'; +import radioGroupCss from './radio-group.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RadioGroupDefault.stories'; diff --git a/theme/components/radio-group.module.css b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/radio-group.module.css similarity index 100% rename from theme/components/radio-group.module.css rename to packages/react-components/react-headless-components-preview/stories/src/RadioGroup/radio-group.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx index 78d1b7b923012..8b4e08096e3e9 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Rating, RatingItem } from '@fluentui/react-headless-components-preview/rating'; import { StarFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/rating.module.css'; +import styles from './rating.module.css'; import storySource from './RatingDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx index 81df0057870fa..cfaa38a9b9be5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/index.stories.tsx @@ -1,7 +1,7 @@ import { Rating, RatingItem } from '@fluentui/react-headless-components-preview/rating'; import descriptionMd from './RatingDescription.md'; -import ratingCss from '../../../../../../theme/components/rating.module.css?raw'; +import ratingCss from './rating.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RatingDefault.stories'; diff --git a/theme/components/rating.module.css b/packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css similarity index 100% rename from theme/components/rating.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx index cb7fe7308dd20..19424f04e367e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/rating-display.module.css'; +import styles from './rating-display.module.css'; import storySource from './RatingDisplayCompact.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx index 78204555679a4..f57fc1b7da342 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/rating-display.module.css'; +import styles from './rating-display.module.css'; import storySource from './RatingDisplayDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx index 13280dcd0938f..3f1d68d842822 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/index.stories.tsx @@ -1,7 +1,7 @@ import { RatingDisplay } from '@fluentui/react-headless-components-preview/rating-display'; import descriptionMd from './RatingDisplayDescription.md'; -import ratingDisplayCss from '../../../../../../theme/components/rating-display.module.css?raw'; +import ratingDisplayCss from './rating-display.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './RatingDisplayDefault.stories'; diff --git a/theme/components/rating-display.module.css b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css similarity index 100% rename from theme/components/rating-display.module.css rename to packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx index 679c30dcf018f..43c382a315b2b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx @@ -3,7 +3,7 @@ import { SearchBox } from '@fluentui/react-headless-components-preview/search-bo import { SearchRegular } from '@fluentui/react-icons'; // SearchBox reuses the input CSS module per the story authoring guide. -import styles from '../../../../../../theme/components/input.module.css'; +import styles from '../Input/input.module.css'; import storySource from './SearchBoxDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx index 23b01c4f2ae31..0f42650d79777 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/index.stories.tsx @@ -1,7 +1,7 @@ import { SearchBox } from '@fluentui/react-headless-components-preview/search-box'; import descriptionMd from './SearchBoxDescription.md'; -import inputCss from '../../../../../../theme/components/input.module.css?raw'; +import inputCss from '../Input/input.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SearchBoxDefault.stories'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx index 3a045e55e4def..12759deb8f00b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import { Select } from '@fluentui/react-headless-components-preview/select'; import { ChevronDownRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../theme/components/field.module.css'; -import styles from '../../../../../../theme/components/select.module.css'; +import fieldStyles from '../Field/field.module.css'; +import styles from './select.module.css'; import storySource from './SelectDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx index 0ec78ea7e2093..0ef7114a1f4b0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/index.stories.tsx @@ -1,8 +1,8 @@ import { Select } from '@fluentui/react-headless-components-preview/select'; import descriptionMd from './SelectDescription.md'; -import selectCss from '../../../../../../theme/components/select.module.css?raw'; -import fieldCss from '../../../../../../theme/components/field.module.css?raw'; +import selectCss from './select.module.css?raw'; +import fieldCss from '../Field/field.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SelectDefault.stories'; diff --git a/theme/components/select.module.css b/packages/react-components/react-headless-components-preview/stories/src/Select/select.module.css similarity index 100% rename from theme/components/select.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Select/select.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx index 875188a4601a4..46c77033fe21a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Skeleton, SkeletonItem } from '@fluentui/react-headless-components-preview/skeleton'; -import styles from '../../../../../../theme/components/skeleton.module.css'; +import styles from './skeleton.module.css'; import storySource from './SkeletonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx index f3abaa9d91b36..a0274887b1014 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/index.stories.tsx @@ -1,7 +1,7 @@ import { Skeleton, SkeletonItem } from '@fluentui/react-headless-components-preview/skeleton'; import descriptionMd from './SkeletonDescription.md'; -import skeletonCss from '../../../../../../theme/components/skeleton.module.css?raw'; +import skeletonCss from './skeleton.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SkeletonDefault.stories'; diff --git a/theme/components/skeleton.module.css b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css similarity index 100% rename from theme/components/skeleton.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx index 67b2544c02157..fbf976b70477c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Slider } from '@fluentui/react-headless-components-preview/slider'; -import styles from '../../../../../../theme/components/slider.module.css'; +import styles from './slider.module.css'; import storySource from './SliderDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx index d5b633f0157f2..8371b8e3a6782 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/index.stories.tsx @@ -1,7 +1,7 @@ import { Slider } from '@fluentui/react-headless-components-preview/slider'; import descriptionMd from './SliderDescription.md'; -import sliderCss from '../../../../../../theme/components/slider.module.css?raw'; +import sliderCss from './slider.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SliderDefault.stories'; diff --git a/theme/components/slider.module.css b/packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css similarity index 100% rename from theme/components/slider.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx index 80bf9bc7869bc..e483127a558c2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import { SpinButton } from '@fluentui/react-headless-components-preview/spin-button'; import { ChevronDownRegular, ChevronUpRegular } from '@fluentui/react-icons'; -import fieldStyles from '../../../../../../theme/components/field.module.css'; -import styles from '../../../../../../theme/components/spin-button.module.css'; +import fieldStyles from '../Field/field.module.css'; +import styles from './spin-button.module.css'; import storySource from './SpinButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx index b275a83b32287..c082b7203299b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/index.stories.tsx @@ -1,8 +1,8 @@ import { SpinButton } from '@fluentui/react-headless-components-preview/spin-button'; import descriptionMd from './SpinButtonDescription.md'; -import spinButtonCss from '../../../../../../theme/components/spin-button.module.css?raw'; -import fieldCss from '../../../../../../theme/components/field.module.css?raw'; +import spinButtonCss from './spin-button.module.css?raw'; +import fieldCss from '../Field/field.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SpinButtonDefault.stories'; diff --git a/theme/components/spin-button.module.css b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/spin-button.module.css similarity index 100% rename from theme/components/spin-button.module.css rename to packages/react-components/react-headless-components-preview/stories/src/SpinButton/spin-button.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx index 5bc124ca88bf0..85add36656d25 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/spinner.module.css'; +import styles from './spinner.module.css'; import storySource from './SpinnerDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx index a7fdaef32be58..d3580c7a25793 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/spinner.module.css'; +import styles from './spinner.module.css'; import storySource from './SpinnerLabels.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Labels = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx index 12220a79642db..153f2a5c5cb0f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/index.stories.tsx @@ -1,7 +1,7 @@ import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import descriptionMd from './SpinnerDescription.md'; -import spinnerCss from '../../../../../../theme/components/spinner.module.css?raw'; +import spinnerCss from './spinner.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SpinnerDefault.stories'; diff --git a/theme/components/spinner.module.css b/packages/react-components/react-headless-components-preview/stories/src/Spinner/spinner.module.css similarity index 100% rename from theme/components/spinner.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Spinner/spinner.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx index 0fed055a1f912..2bb15b382dc6b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Switch } from '@fluentui/react-headless-components-preview/switch'; -import styles from '../../../../../../theme/components/switch.module.css'; +import styles from './switch.module.css'; import storySource from './SwitchDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx index c387424c513aa..a4d51a6a5b3da 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/index.stories.tsx @@ -1,7 +1,7 @@ import { Switch } from '@fluentui/react-headless-components-preview/switch'; import descriptionMd from './SwitchDescription.md'; -import switchCss from '../../../../../../theme/components/switch.module.css?raw'; +import switchCss from './switch.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './SwitchDefault.stories'; diff --git a/theme/components/switch.module.css b/packages/react-components/react-headless-components-preview/stories/src/Switch/switch.module.css similarity index 100% rename from theme/components/switch.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Switch/switch.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx index 10461104ef390..72c0c95f23b91 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { TabList, Tab } from '@fluentui/react-headless-components-preview/tab-list'; -import styles from '../../../../../../theme/components/tab-list.module.css'; +import styles from './tab-list.module.css'; import storySource from './TabListDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const tabs = [ diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx index 26810d2e6cb8e..aacf5126c43dd 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/index.stories.tsx @@ -1,7 +1,7 @@ import { TabList } from '@fluentui/react-headless-components-preview/tab-list'; import descriptionMd from './TabListDescription.md'; -import tabListCss from '../../../../../../theme/components/tab-list.module.css?raw'; +import tabListCss from './tab-list.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './TabListDefault.stories'; diff --git a/theme/components/tab-list.module.css b/packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css similarity index 100% rename from theme/components/tab-list.module.css rename to packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx index d5b7322639de2..302623d11b957 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; -import styles from '../../../../../../theme/components/textarea.module.css'; +import styles from './textarea.module.css'; import storySource from './TextareaDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx index 1301794bff9fd..2acb7294f0338 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/index.stories.tsx @@ -1,7 +1,7 @@ import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; import descriptionMd from './TextareaDescription.md'; -import textareaCss from '../../../../../../theme/components/textarea.module.css?raw'; +import textareaCss from './textarea.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './TextareaDefault.stories'; diff --git a/theme/components/textarea.module.css b/packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css similarity index 100% rename from theme/components/textarea.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx index 4901e0907a4df..213d51cacca46 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ToggleButton } from '@fluentui/react-headless-components-preview/toggle-button'; import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/toggle-button.module.css'; +import styles from './toggle-button.module.css'; import storySource from './ToggleButtonDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx index 44ba1ff23087e..8d085286e28f7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/index.stories.tsx @@ -1,7 +1,7 @@ import { ToggleButton } from '@fluentui/react-headless-components-preview/toggle-button'; import descriptionMd from './ToggleButtonDescription.md'; -import toggleButtonCss from '../../../../../../theme/components/toggle-button.module.css?raw'; +import toggleButtonCss from './toggle-button.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ToggleButtonDefault.stories'; diff --git a/theme/components/toggle-button.module.css b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css similarity index 100% rename from theme/components/toggle-button.module.css rename to packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx index 20b5a5bdf9587..7dd450b19cb37 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx @@ -19,7 +19,7 @@ import { TextUnderlineRegular, } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/toolbar.module.css'; +import styles from './toolbar.module.css'; import storySource from './ToolbarDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; const alignIcons = { diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx index 28be5e56d3e63..4645760f8389b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Toolbar, ToolbarGroup, ToolbarToggleButton } from '@fluentui/react-headless-components-preview/toolbar'; import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/toolbar.module.css'; +import styles from './toolbar.module.css'; import storySource from './ToolbarToggleButton.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Toggle = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx index 15b574d1dc8ee..dc812b05098a7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx @@ -14,7 +14,7 @@ import { TextUnderlineRegular, } from '@fluentui/react-icons'; -import styles from '../../../../../../theme/components/toolbar.module.css'; +import styles from './toolbar.module.css'; import storySource from './ToolbarVertical.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx index ffb1759e4fdc2..4cafb8ea690ff 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/index.stories.tsx @@ -8,7 +8,7 @@ import { } from '@fluentui/react-headless-components-preview/toolbar'; import descriptionMd from './ToolbarDescription.md'; -import toolbarCss from '../../../../../../theme/components/toolbar.module.css?raw'; +import toolbarCss from './toolbar.module.css?raw'; import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './ToolbarDefault.stories'; diff --git a/theme/components/toolbar.module.css b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css similarity index 100% rename from theme/components/toolbar.module.css rename to packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx index 5abcdc30c179f..e8ad6affe2a45 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx @@ -229,7 +229,7 @@ export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) // all stories so the Stackblitz sandbox can bundle them. For the per-story // tab strip we only want the modules actually referenced in the displayed // TSX โ€” match by basename in import strings (e.g. `./styles/dialog.module.css` - // after `cleanStorySource`, or `theme/components/dialog.module.css?raw`). + // after `cleanStorySource`, or `./dialog.module.css?raw`). const referencedBasenames = new Set(Array.from(tsxCode.matchAll(/([a-z][a-z0-9-]*\.module\.css)/gi), m => m[1])); const cssModules = referencedBasenames.size ? allCssModules.filter(m => referencedBasenames.has(m.name)) diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts index 889680f5fb548..3140fda7b6950 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -11,7 +11,7 @@ * Spread the result into a story's `parameters` object: * * ```tsx - * import buttonCss from '../../../../../../theme/components/button.module.css?raw'; + * import buttonCss from '../Button/button.module.css?raw'; * import { withCssModuleSource } from '../_helpers/withCssModuleSource'; * * export default { @@ -70,12 +70,12 @@ export function withCssModuleSource( } /** - * The story file imports each CSS Module via a long relative path that points - * back to `theme/components/<name>.module.css`. In the sandbox, that path - * doesn't exist โ€” so we: + * The story file imports each CSS Module via a colocated relative path that + * points to `<Component>/<name>.module.css`. In the sandbox, those folders + * don't exist โ€” so we: * * 1. Drop a flat copy of `tokens.css` and each module under `src/styles/`. - * 2. Rewrite every `theme/components/<โ€ฆ>.module.css` import in the story + * 2. Rewrite every relative `<โ€ฆ>/<name>.module.css` import in the story * file to `./styles/<basename>` (or `../styles/<basename>` from `App`). * 3. Inject `import './styles/tokens.css'` at the top of `src/App.tsx` * so the design tokens cascade onto the rendered example. @@ -107,11 +107,11 @@ function buildSandboxFiles( next[`src/styles/${m.name}`] = m.source; } - // Story file lives at `src/example.tsx`; rewrite the deeply-relative - // `theme/components/<file>.module.css` import to a sibling path. + // Story file lives at `src/example.tsx`; rewrite the colocated + // `<...>/<file>.module.css` import to a sibling path under `./styles/`. if (typeof example === 'string') { next['src/example.tsx'] = example.replace( - /(['"])(?:\.\.\/)+theme\/components\/([^'"]+\.module\.css)\1/g, + /(['"])(?:\.{1,2}\/)+(?:[^'"\/]+\/)*([^'"\/]+\.module\.css)\1/g, (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, ); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts index 6b3845f6b63a6..a11040ef26274 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts @@ -46,12 +46,12 @@ function cleanStorySource(source: string): string { .replace(/^import\s+\w+\s+from\s+['"]\..*?\.stories\?raw['"];\s*\r?\n/m, '') .replace(/^import\s*\{\s*withStorySource\s*\}\s*from\s+['"][^'"]+['"];\s*\r?\n/m, '') .replace(/^\w+\.parameters\s*=\s*withStorySource\([\s\S]*?\);\s*\r?\n?/m, '') - // Rewrite the deeply-relative `theme/components/<file>.module.css` paths - // to a colocated `./styles/<file>.module.css` so the snippet matches what + // Rewrite the deeply-relative or sibling `*.module.css` paths to a + // colocated `./styles/<file>.module.css` so the snippet matches what // the user actually gets in the Stackblitz sandbox (and is paste-ready // into a project that follows the same colocation convention). .replace( - /(['"])(?:\.\.\/)+theme\/components\/([^'"]+\.module\.css)\1/g, + /(['"])(?:\.{1,2}\/)+(?:[^'"\/]+\/)*([^'"\/]+\.module\.css)\1/g, (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, ) .replace(/\n{3,}/g, '\n\n') @@ -85,7 +85,7 @@ export function withStorySource(storySource: string, extra: Parameters = {}): Pa // `Default.parameters.fullSource = '<post-babel source>';` statement at the // end of the compiled story file. That post-babel source has had every // relative import stripped โ€” including our `import styles from - // '../../../../../../theme/components/<file>.module.css';` line โ€” which + // colocated <Component>/<name>.module.css;` line โ€” which // breaks the "Open in Stackblitz" sandbox (the example references `styles` // but never imports it). // From aa9ac09b6f7239d8024be69b876747c74761acb4 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 22:33:05 +0200 Subject: [PATCH 04/25] refactor(headless-docsite): move theme/ into public-docsite-v9-headless 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> --- apps/public-docsite-v9-headless/.storybook/preview.js | 2 +- apps/public-docsite-v9-headless/project.json | 4 ++-- .../public-docsite-v9-headless/theme}/tokens.css | 0 .../stories/.storybook/preview.js | 2 +- .../react-headless-components-preview/stories/project.json | 7 ++++++- .../stories/src/_helpers/withCssModuleSource.ts | 2 +- 6 files changed, 11 insertions(+), 6 deletions(-) rename {theme => apps/public-docsite-v9-headless/theme}/tokens.css (100%) diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 48a9233dec33f..37505dd64bcca 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -5,7 +5,7 @@ import * as rootPreview from '../../../.storybook/preview'; // Design tokens (light + dark CSS custom properties on :root and // [data-theme="dark"]), plus a few base resets for body/html. Loaded once // for every story rendered in this Storybook. -import '../../../theme/tokens.css'; +import '../theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. // See `packages/.../stories/src/_helpers/HeadlessDocsPage.tsx` for rationale. diff --git a/apps/public-docsite-v9-headless/project.json b/apps/public-docsite-v9-headless/project.json index 31a47992caa6e..0f418ff88e365 100644 --- a/apps/public-docsite-v9-headless/project.json +++ b/apps/public-docsite-v9-headless/project.json @@ -12,7 +12,7 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{projectRoot}/theme/**"] }, "build-storybook:docsite": { "dependsOn": [ @@ -21,7 +21,7 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{projectRoot}/theme/**"] } } } diff --git a/theme/tokens.css b/apps/public-docsite-v9-headless/theme/tokens.css similarity index 100% rename from theme/tokens.css rename to apps/public-docsite-v9-headless/theme/tokens.css diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js index 48bfb3c978d07..c95c7705ae6d7 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js @@ -5,7 +5,7 @@ import * as rootPreview from '../../../../../.storybook/preview'; // Design tokens โ€” mirrors the import in // apps/public-docsite-v9-headless/.storybook/preview.js so the per-package // storybook (built by `pr-website-deploy.yml`) renders identical stories. -import '../../../../../theme/tokens.css'; +import '../../../../../apps/public-docsite-v9-headless/theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. // See `stories/src/_helpers/HeadlessDocsPage.tsx` for the rationale (Storybook's diff --git a/packages/react-components/react-headless-components-preview/stories/project.json b/packages/react-components/react-headless-components-preview/stories/project.json index 2f4b38232fe78..94f31fa4d80e4 100644 --- a/packages/react-components/react-headless-components-preview/stories/project.json +++ b/packages/react-components/react-headless-components-preview/stories/project.json @@ -7,7 +7,12 @@ "implicitDependencies": [], "targets": { "build-storybook": { - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{workspaceRoot}/theme/**"] + "inputs": [ + "default", + "{workspaceRoot}/.storybook/**", + "{projectRoot}/.storybook/**", + "{workspaceRoot}/apps/public-docsite-v9-headless/theme/**" + ] } } } diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts index 3140fda7b6950..75414c6a41da5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -31,7 +31,7 @@ // Loaded via the `?raw` resourceQuery rule configured in `.storybook/main.js`. // Bundling tokens.css inline lets the Stackblitz scaffold include them without // requiring story authors to wire imports manually. -import tokensCss from '../../../../../../theme/tokens.css?raw'; +import tokensCss from '../../../../../../apps/public-docsite-v9-headless/theme/tokens.css?raw'; import type { CssModule, HeadlessSourceParameters } from './HeadlessSourcePanel'; From 3dc45fa58a0bd9e13eeb0153be60a50260cd7c07 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 22:45:16 +0200 Subject: [PATCH 05/25] refactor(headless-docsite): move theme/ into the stories package 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> --- .../.storybook/preview.js | 2 +- apps/public-docsite-v9-headless/project.json | 14 ++++++++++++-- .../stories/.storybook/preview.js | 2 +- .../stories/project.json | 7 +------ .../stories/src/_helpers/withCssModuleSource.ts | 2 +- .../stories}/theme/tokens.css | 0 6 files changed, 16 insertions(+), 11 deletions(-) rename {apps/public-docsite-v9-headless => packages/react-components/react-headless-components-preview/stories}/theme/tokens.css (100%) diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 37505dd64bcca..34ed78bd1f5a3 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -5,7 +5,7 @@ import * as rootPreview from '../../../.storybook/preview'; // Design tokens (light + dark CSS custom properties on :root and // [data-theme="dark"]), plus a few base resets for body/html. Loaded once // for every story rendered in this Storybook. -import '../theme/tokens.css'; +import '../../../packages/react-components/react-headless-components-preview/stories/theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. // See `packages/.../stories/src/_helpers/HeadlessDocsPage.tsx` for rationale. diff --git a/apps/public-docsite-v9-headless/project.json b/apps/public-docsite-v9-headless/project.json index 0f418ff88e365..ae1c88653ca5e 100644 --- a/apps/public-docsite-v9-headless/project.json +++ b/apps/public-docsite-v9-headless/project.json @@ -12,7 +12,12 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{projectRoot}/theme/**"] + "inputs": [ + "default", + "{workspaceRoot}/.storybook/**", + "{projectRoot}/.storybook/**", + "{workspaceRoot}/packages/react-components/react-headless-components-preview/stories/theme/**" + ] }, "build-storybook:docsite": { "dependsOn": [ @@ -21,7 +26,12 @@ "target": "build" } ], - "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{projectRoot}/theme/**"] + "inputs": [ + "default", + "{workspaceRoot}/.storybook/**", + "{projectRoot}/.storybook/**", + "{workspaceRoot}/packages/react-components/react-headless-components-preview/stories/theme/**" + ] } } } diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js index c95c7705ae6d7..6e23910c801f6 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js @@ -5,7 +5,7 @@ import * as rootPreview from '../../../../../.storybook/preview'; // Design tokens โ€” mirrors the import in // apps/public-docsite-v9-headless/.storybook/preview.js so the per-package // storybook (built by `pr-website-deploy.yml`) renders identical stories. -import '../../../../../apps/public-docsite-v9-headless/theme/tokens.css'; +import '../../theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. // See `stories/src/_helpers/HeadlessDocsPage.tsx` for the rationale (Storybook's diff --git a/packages/react-components/react-headless-components-preview/stories/project.json b/packages/react-components/react-headless-components-preview/stories/project.json index 94f31fa4d80e4..28c575776c667 100644 --- a/packages/react-components/react-headless-components-preview/stories/project.json +++ b/packages/react-components/react-headless-components-preview/stories/project.json @@ -7,12 +7,7 @@ "implicitDependencies": [], "targets": { "build-storybook": { - "inputs": [ - "default", - "{workspaceRoot}/.storybook/**", - "{projectRoot}/.storybook/**", - "{workspaceRoot}/apps/public-docsite-v9-headless/theme/**" - ] + "inputs": ["default", "{workspaceRoot}/.storybook/**", "{projectRoot}/.storybook/**", "{projectRoot}/theme/**"] } } } diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts index 75414c6a41da5..df23d4d715a2b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -31,7 +31,7 @@ // Loaded via the `?raw` resourceQuery rule configured in `.storybook/main.js`. // Bundling tokens.css inline lets the Stackblitz scaffold include them without // requiring story authors to wire imports manually. -import tokensCss from '../../../../../../apps/public-docsite-v9-headless/theme/tokens.css?raw'; +import tokensCss from '../../theme/tokens.css?raw'; import type { CssModule, HeadlessSourceParameters } from './HeadlessSourcePanel'; diff --git a/apps/public-docsite-v9-headless/theme/tokens.css b/packages/react-components/react-headless-components-preview/stories/theme/tokens.css similarity index 100% rename from apps/public-docsite-v9-headless/theme/tokens.css rename to packages/react-components/react-headless-components-preview/stories/theme/tokens.css From 70325a3bdab0628b1906fc3177e4d09080ce0f91 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 22:49:27 +0200 Subject: [PATCH 06/25] docs(headless-docsite): rephrase styling disclaimer 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> --- .../stories/src/_helpers/HeadlessDocsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx index 7047ce554a4d2..5aa01a7fc274b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx @@ -102,8 +102,8 @@ export const HeadlessDocsPage: React.FC = () => { <Subtitle /> <Description /> <aside style={disclaimerStyle} role="note"> - <strong>Heads up:</strong> headless components ship without default styles. The CSS shown in these stories is a - demonstration theme โ€” feel free to swap it out with your own design system. + <strong>Heads up:</strong> headless components ship without default styles. The CSS shown in these stories is + provided purely as a demonstration of one possible look. </aside> {primaryStory && ( From d5e275974c23e3e37820ebfe4f665802de1c77d1 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 22:55:02 +0200 Subject: [PATCH 07/25] fix(headless-docsite): correct tokens.css path in stories storybook preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- ...k-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json | 7 +++++++ .../stories/.storybook/preview.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json diff --git a/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json b/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json new file mode 100644 index 0000000000000..0bd5de89ef7fd --- /dev/null +++ b/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "chore: skip warnings for known-safe relative imports (*.module.css, ?raw queries, withStorySource/withCssModuleSource helpers, *.stories?raw)", + "packageName": "@fluentui/babel-preset-storybook-full-source", + "email": "tudor.popa@microsoft.com", + "dependentChangeType": "none" +} diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js index 6e23910c801f6..c1489be6a95b2 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js @@ -5,7 +5,7 @@ import * as rootPreview from '../../../../../.storybook/preview'; // Design tokens โ€” mirrors the import in // apps/public-docsite-v9-headless/.storybook/preview.js so the per-package // storybook (built by `pr-website-deploy.yml`) renders identical stories. -import '../../theme/tokens.css'; +import '../theme/tokens.css'; // Custom docs page that renders the "Show code" panel with TSX | CSS tabs. // See `stories/src/_helpers/HeadlessDocsPage.tsx` for the rationale (Storybook's From c4e0ac25cd9d0c3d8a3c170b4211213d44215b0e Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Tue, 28 Apr 2026 23:15:15 +0200 Subject: [PATCH 08/25] fix(headless-docsite): exclude stories from test-ssr and fix RIT type-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> --- nx.json | 7 ++++++- .../src/RatingDisplay/RatingDisplayCompact.stories.tsx | 2 +- .../src/RatingDisplay/RatingDisplayDefault.stories.tsx | 2 +- .../stories/src/_helpers/HeadlessSourcePanel.tsx | 6 +++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/nx.json b/nx.json index ec7986898dad7..f9e181aa463ff 100644 --- a/nx.json +++ b/nx.json @@ -141,7 +141,12 @@ "plugin": "./tools/workspace-plugin/src/plugins/workspace-plugin.ts", "options": { "testSSR": { - "exclude": ["react-theme-stories", "react-migration-v8-v9-stories", "react-migration-v0-v9-stories"] + "exclude": [ + "react-theme-stories", + "react-migration-v8-v9-stories", + "react-migration-v0-v9-stories", + "react-headless-components-preview-stories" + ] }, "verifyPackaging": { "include": ["react-text", "react-components"] diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx index 19424f04e367e..ece02782bb394 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx @@ -5,7 +5,7 @@ import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; import styles from './rating-display.module.css'; import storySource from './RatingDisplayCompact.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; -const RatingIcon = (): React.ReactNode => ( +const RatingIcon: React.FC = () => ( <> <StarFilled className={`${styles.icon} ${styles.iconFilled}`} /> <StarHalfFilled className={`${styles.icon} ${styles.iconHalf}`} /> diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx index f57fc1b7da342..150ee571744be 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx @@ -5,7 +5,7 @@ import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; import styles from './rating-display.module.css'; import storySource from './RatingDisplayDefault.stories?raw'; import { withStorySource } from '../_helpers/withStorySource'; -const RatingIcon = (): React.ReactNode => ( +const RatingIcon: React.FC = () => ( <> <StarFilled className={`${styles.icon} ${styles.iconFilled}`} /> <StarHalfFilled className={`${styles.icon} ${styles.iconHalf}`} /> diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx index e8ad6affe2a45..0349a076a7ac2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx @@ -221,7 +221,7 @@ export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) const [activeTabId, setActiveTabId] = React.useState<string>('story-tsx'); const tsxCode: string = typeof sourceProps.code === 'string' ? sourceProps.code : ''; - const tsxLanguage: string = typeof sourceProps.language === 'string' ? sourceProps.language : 'tsx'; + const tsxLanguage = 'tsx' as const; const allCssModules: CssModule[] = (story.parameters?.theme as HeadlessSourceParameters | undefined)?.cssModules ?? []; @@ -242,10 +242,10 @@ export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) return null; } - type Tab = { id: string; label: string; code: string; language: string }; + type Tab = { id: string; label: string; code: string; language: 'tsx' | 'css' }; const tabs: Tab[] = [ { id: 'story-tsx', label: 'Story.tsx', code: tsxCode, language: tsxLanguage }, - ...cssModules.map((m, i) => ({ id: `css-${i}`, label: m.name, code: m.source.trim(), language: 'css' })), + ...cssModules.map((m, i) => ({ id: `css-${i}`, label: m.name, code: m.source.trim(), language: 'css' as const })), ]; const activeTab = tabs.find(t => t.id === activeTabId) ?? tabs[0]; From f3f4ee8e27d11394d85c2d925dc681a55684153c Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Wed, 29 Apr 2026 12:09:15 +0200 Subject: [PATCH 09/25] fix(headless-docsite): bigger disclaimer + preview note + magenta button 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> --- .../stories/src/_helpers/HeadlessDocsPage.tsx | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx index 5aa01a7fc274b..5876bc6e7b28e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx @@ -55,14 +55,22 @@ const nameToHash = (name: string) => const disclaimerStyle: React.CSSProperties = { margin: '20px 0 0', - padding: '12px 16px', + padding: '16px 20px', border: '1px solid #e1dfdd', - borderLeft: '3px solid #9b1f5a', + borderLeft: '4px solid #9b1f5a', borderRadius: 6, background: '#fdf6f9', color: '#3c3c3c', - fontSize: 13, - lineHeight: 1.5, + fontSize: 16, + lineHeight: 1.55, +}; + +const disclaimerNoteStyle: React.CSSProperties = { + marginTop: 10, + paddingTop: 10, + borderTop: '1px dashed #e1c2d2', + fontSize: 14, + color: '#5a5a5a', }; export const HeadlessDocsPage: React.FC = () => { @@ -102,8 +110,13 @@ export const HeadlessDocsPage: React.FC = () => { <Subtitle /> <Description /> <aside style={disclaimerStyle} role="note"> - <strong>Heads up:</strong> headless components ship without default styles. The CSS shown in these stories is - provided purely as a demonstration of one possible look. + <div> + <strong>Heads up:</strong> headless components ship without default styles. The CSS shown in these stories is + provided purely as a demonstration of one possible look. + </div> + <div style={disclaimerNoteStyle}> + <strong>Preview:</strong> these controls are in preview and their APIs are subject to change. + </div> </aside> {primaryStory && ( @@ -158,4 +171,22 @@ const headlessDocsPageCss = ` .headless-docs-page .sbdocs-preview:has(> .headless-source-portal:not(:empty)) { height: auto !important; } + +/* + Force the magenta accent for the "Show code" / "Open in Stackblitz" hover & + focus underlines. Storybook's ActionBar paints the underline via an inset + box-shadow driven by \`theme.color.secondary\`, and the + \`@fluentui/react-storybook-addon-export-to-sandbox\` styles hard-code a blue + underline on the Stackblitz button โ€” both are overridden here so the canvas + action buttons match the rest of the headless docs accent. +*/ +.headless-docs-page .sbdocs-preview .docblock-code-toggle:hover, +.headless-docs-page .sbdocs-preview .docblock-code-toggle:focus, +.headless-docs-page .sbdocs-preview .docblock-code-toggle.docblock-code-toggle--expanded, +.headless-docs-page .docs-story .with-code-sandbox-button:hover, +.headless-docs-page .docs-story .with-code-sandbox-button:focus { + outline: none !important; + box-shadow: #9b1f5a 0 -3px 0 0 inset !important; + color: #9b1f5a !important; +} `; From 94e8c374a7c33778a07c1a5a001fe3528f41208a Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Wed, 29 Apr 2026 12:10:52 +0200 Subject: [PATCH 10/25] fix(headless-docsite): enlarge disclaimer text, match preview note size Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../stories/src/_helpers/HeadlessDocsPage.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx index 5876bc6e7b28e..c9dbd5e119a21 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx @@ -55,22 +55,23 @@ const nameToHash = (name: string) => const disclaimerStyle: React.CSSProperties = { margin: '20px 0 0', - padding: '16px 20px', + padding: '18px 22px', border: '1px solid #e1dfdd', borderLeft: '4px solid #9b1f5a', borderRadius: 6, background: '#fdf6f9', color: '#3c3c3c', - fontSize: 16, + fontSize: 19, lineHeight: 1.55, }; const disclaimerNoteStyle: React.CSSProperties = { - marginTop: 10, - paddingTop: 10, + marginTop: 12, + paddingTop: 12, borderTop: '1px dashed #e1c2d2', - fontSize: 14, - color: '#5a5a5a', + fontSize: 19, + lineHeight: 1.55, + color: '#3c3c3c', }; export const HeadlessDocsPage: React.FC = () => { From 694e404d77ce96b3bfb28f0ed4213b3c580b823b Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Wed, 29 Apr 2026 15:32:57 +0200 Subject: [PATCH 11/25] fix(headless-docsite): annotate stories storybook main.js for type-check 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> --- .../stories/.storybook/main.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js index 833640b6588cc..8c3a7c80ec3a7 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js @@ -37,6 +37,8 @@ const cssModuleRule = { /** * Mirrors `patchRules` in the docsite config โ€” see that file's comment for * the full rationale (CSS Modules carve-out + `?raw` resourceQuery skip). + * + * @param {any[]} rules */ function patchRules(rules) { for (const rule of rules) { @@ -74,7 +76,7 @@ module.exports = /** @type {Omit<import('../../../../../.storybook/main'), 'type stories: [...rootMain.stories, '../src/**/*.mdx', '../src/**/index.stories.@(ts|tsx)'], addons: [...rootMain.addons], webpackFinal: (config, options) => { - const localConfig = { ...rootMain.webpackFinal(config, options) }; + const localConfig = /** @type {any} */ ({ ...rootMain.webpackFinal(config, options) }); localConfig.module = localConfig.module || { rules: [] }; const rules = patchRules([...(localConfig.module.rules || [])]); From 8d3605350ea0c703b171bd09d1ed4025f3181c31 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Wed, 29 Apr 2026 17:41:00 +0200 Subject: [PATCH 12/25] fix(headless-stories): repair mangled CSS module class names 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> --- .../stories/src/Avatar/avatar.module.css | 2 +- .../stories/src/Badge/badge.module.css | 2 +- .../stories/src/Button/button.module.css | 4 ++-- .../src/Dialog/DialogAlert.stories.tsx | 2 +- .../src/Dialog/DialogNested.stories.tsx | 2 +- .../src/Dialog/DialogNonModal.stories.tsx | 2 +- .../stories/src/Dialog/dialog.module.css | 4 ++-- .../stories/src/Divider/divider.module.css | 18 ++++++++--------- .../stories/src/Field/field.module.css | 4 ++-- .../stories/src/Input/input.module.css | 2 +- .../src/MessageBar/message-bar.module.css | 20 +++++++++---------- .../src/ProgressBar/progress-bar.module.css | 2 +- .../stories/src/Rating/rating.module.css | 2 +- .../RatingDisplay/rating-display.module.css | 4 ++-- .../stories/src/Skeleton/skeleton.module.css | 2 +- .../stories/src/Slider/slider.module.css | 4 ++-- .../stories/src/TabList/tab-list.module.css | 6 +++--- .../stories/src/Textarea/textarea.module.css | 2 +- .../src/ToggleButton/toggle-button.module.css | 12 +++++------ .../stories/src/Toolbar/toolbar.module.css | 4 ++-- 20 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css b/packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css index 79ef61fcd5f4f..265d685083b8a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/avatar.module.css @@ -89,7 +89,7 @@ gap: 12px; } -.avatar-module__meta--0\+MMt { +.meta { display: flex; flex-direction: column; } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css b/packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css index bc754d065bace..2ffe77fef34ee 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/badge.module.css @@ -75,7 +75,7 @@ /* Demo helpers (used by Storybook examples) */ -.badge-module__demo--uj\+MF { +.demo { display: flex; align-items: center; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css b/packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css index 526a95a413ec8..3040ff354df2f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/button.module.css @@ -76,13 +76,13 @@ } /* Outline โ€” bordered transparent */ -.button-module__outline--gaC\+9 { +.outline { background: transparent; color: var(--text); border-color: var(--border-strong); } -.button-module__outline--gaC\+9:hover { +.outline:hover { background: var(--surface-muted); border-color: var(--text); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx index 1149db08affa7..50c10ee27ccd0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx @@ -27,7 +27,7 @@ export const Alert = (): React.ReactNode => ( </button> </DialogTrigger> - <DialogSurface className={styles.alertSurface}> + <DialogSurface className={`${styles.surface} ${styles.alertSurface}`}> <DialogBody className={styles.body}> <DialogTitle className={styles.title}>Delete item?</DialogTitle> <p className={styles.copy}> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx index 37db865f60447..20dc1399a7331 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx @@ -38,7 +38,7 @@ export const Nested = (): React.ReactNode => ( </button> </DialogTrigger> - <DialogSurface className={styles.alertSurface}> + <DialogSurface className={`${styles.surface} ${styles.alertSurface}`}> <DialogBody className={styles.body}> <DialogTitle className={styles.title}>Inner dialog</DialogTitle> <p className={styles.copy}>Press Escape โ€” only this dialog closes; the outer stays open.</p> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx index ac1dfb95a6d62..d69bc71b17db0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx @@ -26,7 +26,7 @@ export const NonModal = (): React.ReactNode => ( </button> </DialogTrigger> - <DialogSurface className={styles.alertSurface}> + <DialogSurface className={`${styles.surface} ${styles.alertSurface}`}> <DialogBody className={styles.body}> <DialogTitle className={styles.title}>Non-modal</DialogTitle> <p className={styles.copy}> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css b/packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css index a02d7e07604cf..7b9866ba8ddd6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/dialog.module.css @@ -87,7 +87,7 @@ background: var(--brand-strong); } -.dialog-module__row--0\+lMS { +.row { display: flex; gap: 12px; align-items: center; @@ -104,7 +104,7 @@ margin-bottom: 16px; } -.demoCol- { +.demoCol { display: flex; flex-direction: column; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css b/packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css index 6a268f205bedf..baac32ad6b70d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/divider.module.css @@ -1,7 +1,7 @@ /* Headless Divider renders <root><wrapper>{children}</wrapper></root>. The line itself comes from ::before / ::after on the root. */ -.divider-module__divider--Qmh\+f { +.divider { display: flex; align-items: center; gap: 12px; @@ -9,8 +9,8 @@ color: var(--text-faint); } -.divider-module__divider--Qmh\+f::before, -.divider-module__divider--Qmh\+f::after { +.divider::before, +.divider::after { content: ''; flex: 1; height: 1px; @@ -18,12 +18,12 @@ } /* Start: short 8 px stub before content, full line after. */ -.divider-module__divider--Qmh\+f.start::before { +.divider.start::before { flex: 0 0 8px; } /* End: full line before content, short 8 px stub after. */ -.divider-module__divider--Qmh\+f.end::after { +.divider.end::after { flex: 0 0 8px; } @@ -48,7 +48,7 @@ } /* Sentence-case content (icon + text) used inside dividers. */ -.content- { +.content { display: inline-flex; align-items: center; gap: 6px; @@ -57,7 +57,7 @@ white-space: nowrap; } -.content- svg { +.content svg { width: 14px; height: 14px; } @@ -126,7 +126,7 @@ color: var(--text); } -.verticalGroup- { +.verticalGroup { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; @@ -172,7 +172,7 @@ background: var(--border); } -.divider-module__labelledText--L\+wxQ { +.labelledText { font-size: 11px; font-weight: 600; text-transform: uppercase; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css b/packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css index 438712e3c2500..9b19e1b78c278 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/field.module.css @@ -11,7 +11,7 @@ color: var(--text); } -.field-module__hint--jh\+Qf { +.hint { font-size: 12px; color: var(--text-muted); } @@ -31,7 +31,7 @@ color: var(--warning); } -.field-module__messageSuccess--H\+7ls { +.messageSuccess { color: var(--success); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css b/packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css index 7ec8e587d49c7..0c5575df57480 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/input.module.css @@ -54,7 +54,7 @@ cursor: not-allowed; } -.input-module__affix--y\+IXS { +.affix { display: inline-flex; align-items: center; justify-content: center; diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css index 8061f33256bc9..d03467267142d 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/message-bar.module.css @@ -1,4 +1,4 @@ -.bar- { +.bar { display: grid; grid-template-columns: auto 1fr auto; align-items: center; @@ -12,18 +12,18 @@ line-height: 1.45; } -.bar-[data-layout='multiline'] { +.bar[data-layout='multiline'] { grid-template-columns: auto 1fr; border-radius: var(--radius-lg); padding: 12px 16px; } -.bar-[data-layout='multiline'] .actions { +.bar[data-layout='multiline'] .actions { grid-column: 2; justify-self: end; } -.message-bar-module__icon--B5\+ob { +.icon { width: 20px; height: 20px; display: inline-flex; @@ -33,7 +33,7 @@ flex-shrink: 0; } -.message-bar-module__icon--B5\+ob svg { +.icon svg { width: 16px; height: 16px; } @@ -89,7 +89,7 @@ background: var(--info-soft); border-color: transparent; } -.info .message-bar-module__icon--B5\+ob { +.info .icon { color: var(--info); } @@ -97,15 +97,15 @@ background: var(--success-soft); border-color: transparent; } -.success .message-bar-module__icon--B5\+ob { +.success .icon { color: var(--success); } -.message-bar-module__warning--8iN\+a { +.warning { background: var(--warning-soft); border-color: transparent; } -.message-bar-module__warning--8iN\+a .message-bar-module__icon--B5\+ob { +.warning .icon { color: var(--warning); } @@ -113,7 +113,7 @@ background: var(--brand-soft); border-color: transparent; } -.danger .message-bar-module__icon--B5\+ob { +.danger .icon { color: var(--brand); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css index 6ccb1cb34ac73..3074223989d85 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/progress-bar.module.css @@ -14,7 +14,7 @@ transition: width var(--duration-medium) var(--ease-standard); } -.progress-bar-module__success--yG\+Ae .fill { +.success .fill { background: var(--success); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css b/packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css index 35a97e4ea4b2e..f637b5276a824 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/rating.module.css @@ -30,7 +30,7 @@ color: inherit; } -.rating-module__row--J7o\+R { +.row { display: flex; align-items: center; gap: 12px; diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css index 5c515ebcfac95..a37b296f025df 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/rating-display.module.css @@ -25,7 +25,7 @@ height: 18px; } -.display [data-appearance='filled'] .rating-display-module__iconHalf--\+w-kN, +.display [data-appearance='filled'] .iconHalf, .display [data-appearance='filled'] .iconOutline { visibility: hidden; } @@ -36,7 +36,7 @@ } .display [data-appearance='outline'] .iconFilled, -.display [data-appearance='outline'] .rating-display-module__iconHalf--\+w-kN { +.display [data-appearance='outline'] .iconHalf { visibility: hidden; } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css index 091a7aa54e3ea..0cc2d708f253f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css @@ -1,4 +1,4 @@ -.card\+ { +.card { display: flex; flex-direction: column; gap: 14px; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css b/packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css index d3d5bf3e826d1..969e2b54e300a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/slider.module.css @@ -22,7 +22,7 @@ cursor: not-allowed; } -.slider-module__rail--Y\+av4 { +.rail { position: relative; width: 100%; height: 4px; @@ -31,7 +31,7 @@ overflow: hidden; } -.slider-module__rail--Y\+av4::after { +.rail::after { content: ''; position: absolute; inset: 0; diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css b/packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css index a29d31454ae28..17594e81db255 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/tab-list.module.css @@ -1,4 +1,4 @@ -.tabs- { +.tabs { display: flex; gap: 4px; padding: 4px; @@ -6,7 +6,7 @@ border-radius: var(--radius-pill); } -.tab-list-module__tabsVertical--HTX\+H { +.tabsVertical { flex-direction: column; border-radius: var(--radius-lg); width: 200px; @@ -27,7 +27,7 @@ transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard); } -.tab-list-module__tabsVertical--HTX\+H .tab { +.tabsVertical .tab { border-radius: var(--radius-md); } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css b/packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css index 34f27c0f2d656..c99b21a0fc7a9 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/textarea.module.css @@ -41,7 +41,7 @@ /* Demo helpers (used by Storybook examples) */ -.demo\+ { +.demo { display: flex; flex-direction: column; diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css index 0eeb49f7dea6e..12fce645654d7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/toggle-button.module.css @@ -1,4 +1,4 @@ -.toggle-button-module__toggle--ob\+SX { +.toggle { display: inline-flex; align-items: center; justify-content: center; @@ -16,28 +16,28 @@ border-color var(--duration-fast) var(--ease-standard); } -.toggle-button-module__toggle--ob\+SX:hover { +.toggle:hover { background: var(--surface-muted); border-color: var(--border-strong); } -.toggle-button-module__toggle--ob\+SX:focus-visible { +.toggle:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--bg-elev), 0 0 0 4px var(--accent); } -.toggle-button-module__toggle--ob\+SX[data-checked] { +.toggle[data-checked] { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); } -.toggle-button-module__toggle--ob\+SX[data-checked]:hover { +.toggle[data-checked]:hover { background: var(--accent-strong); border-color: var(--accent-strong); } -.toggle-button-module__toggle--ob\+SX[data-disabled] { +.toggle[data-disabled] { opacity: 0.4; cursor: not-allowed; } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css index 4ec7108b4d1b4..01a75a455a2c5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/toolbar.module.css @@ -58,14 +58,14 @@ color: var(--accent-contrast); } -.toolbar-module__divider--lj\+su { +.divider { width: 1px; align-self: stretch; margin: 4px 4px; background: var(--border); } -.toolbar[data-vertical] .toolbar-module__divider--lj\+su { +.toolbar[data-vertical] .divider { width: auto; height: 1px; margin: 4px 4px; From 6f982cf24caa8d1f0b2263bbfa7c3af5e22794ba Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Wed, 29 Apr 2026 17:45:38 +0200 Subject: [PATCH 13/25] fix(headless-stories): match deployed Skeleton story layout 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> --- .../src/Skeleton/SkeletonDefault.stories.tsx | 5 +- .../stories/src/Skeleton/skeleton.module.css | 55 +++++++++++-------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx index 46c77033fe21a..466f93c728a20 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx @@ -8,15 +8,14 @@ export const Default = (): React.ReactNode => ( <Skeleton className={`${styles.card} ${styles.demo}`}> <div className={styles.row}> <SkeletonItem className={styles.circle} /> - <div className={`${styles.card} ${styles.demoFlex}`}> + <div className={styles.demoFlex}> <SkeletonItem className={`${styles.bar} ${styles.line60}`} /> <SkeletonItem className={`${styles.bar} ${styles.line40}`} /> </div> </div> - <SkeletonItem className={styles.thumb} /> <SkeletonItem className={`${styles.bar} ${styles.line100}`} /> <SkeletonItem className={`${styles.bar} ${styles.line100}`} /> - <SkeletonItem className={`${styles.bar} ${styles.line60}`} /> + <SkeletonItem className={`${styles.bar} ${styles.line80}`} /> </Skeleton> ); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css index 0cc2d708f253f..f77185792ae15 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/skeleton.module.css @@ -1,28 +1,41 @@ .card { display: flex; flex-direction: column; - gap: 14px; + gap: 12px; width: 100%; + padding: 16px; + background: var(--bg-elev); + border: 1px solid var(--border); + border-radius: var(--radius-lg); } .row { display: flex; - gap: 14px; + gap: 12px; align-items: center; } +.demoFlex { + display: flex; + flex: 1; + flex-direction: column; + gap: 8px; +} + .bar { height: 12px; border-radius: var(--radius-xs); - background: linear-gradient(90deg, var(--surface-muted) 0%, var(--surface-sunken) 50%, var(--surface-muted) 100%); - background-size: 200% 100%; - animation: shimmer 1.6s linear infinite; + background: var(--surface-muted); + animation: pulse 1.6s ease-in-out infinite; } .circle { - width: 44px; - height: 44px; + width: 40px; + height: 40px; border-radius: 50%; + background: var(--surface-muted); + flex-shrink: 0; + animation: pulse 1.6s ease-in-out infinite; } .line40 { @@ -33,32 +46,26 @@ width: 60%; } -.line100 { - width: 100%; +.line80 { + width: 80%; } -.thumb { - height: 140px; - border-radius: var(--radius-lg); +.line100 { + width: 100%; } -@keyframes shimmer { - 0% { - background-position: 200% 0; - } +@keyframes pulse { + 0%, 100% { - background-position: -200% 0; + opacity: 1; + } + 50% { + opacity: 0.5; } } /* Demo helpers (used by Storybook examples) */ .demo { - max-width: 400px; -} - -.demoFlex { - flex: 1; - - gap: 8px; + max-width: 384px; } From 941f3dcb3d692d1f60c0d5944ad99759a1f156e5 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:26:20 +0200 Subject: [PATCH 14/25] fix(headless-stories): re-enable SSR tests, teach esbuild ?raw + module.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- nx.json | 7 +- scripts/test-ssr/src/utils/buildAssets.ts | 8 +-- scripts/test-ssr/src/utils/esbuild-plugin.ts | 71 ++++++++++++++++++++ 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/nx.json b/nx.json index f9e181aa463ff..ec7986898dad7 100644 --- a/nx.json +++ b/nx.json @@ -141,12 +141,7 @@ "plugin": "./tools/workspace-plugin/src/plugins/workspace-plugin.ts", "options": { "testSSR": { - "exclude": [ - "react-theme-stories", - "react-migration-v8-v9-stories", - "react-migration-v0-v9-stories", - "react-headless-components-preview-stories" - ] + "exclude": ["react-theme-stories", "react-migration-v8-v9-stories", "react-migration-v0-v9-stories"] }, "verifyPackaging": { "include": ["react-text", "react-components"] diff --git a/scripts/test-ssr/src/utils/buildAssets.ts b/scripts/test-ssr/src/utils/buildAssets.ts index 5d3f8a60201cb..a5a53bb6464a5 100644 --- a/scripts/test-ssr/src/utils/buildAssets.ts +++ b/scripts/test-ssr/src/utils/buildAssets.ts @@ -1,7 +1,7 @@ import { build } from 'esbuild'; import type { BuildOptions } from 'esbuild'; -import { tsConfigPathsPlugin } from './esbuild-plugin'; +import { cssModulesShimPlugin, rawQueryPlugin, tsConfigPathsPlugin } from './esbuild-plugin'; const NODE_MAJOR_VERSION = process.versions.node.split('.')[0]; @@ -30,7 +30,7 @@ type BuildConfig = { export async function buildAssets(config: BuildConfig): Promise<void> { const { chromeVersion, cjsEntryPoint, cjsOutfile, esmEntryPoint, esmOutfile, distDirectory } = config; - const pluginInstance = tsConfigPathsPlugin({ cwd: distDirectory }); + const plugins = [tsConfigPathsPlugin({ cwd: distDirectory }), rawQueryPlugin(), cssModulesShimPlugin()]; try { // Used for SSR rendering, see renderToHTML.js @@ -44,7 +44,7 @@ export async function buildAssets(config: BuildConfig): Promise<void> { external: ['@griffel/core', '@griffel/react', 'react', 'react-dom', 'react-dom/server', 'scheduler'], format: 'cjs', target: `node${NODE_MAJOR_VERSION}`, - plugins: [pluginInstance], + plugins, }); // Used in generated bundle that will be server by a browser @@ -61,7 +61,7 @@ export async function buildAssets(config: BuildConfig): Promise<void> { ], format: 'iife', target: `chrome${chromeVersion}`, - plugins: [pluginInstance], + plugins, }); } catch (err) { throw new Error( diff --git a/scripts/test-ssr/src/utils/esbuild-plugin.ts b/scripts/test-ssr/src/utils/esbuild-plugin.ts index b9d2fef404d8e..8804f8462a590 100644 --- a/scripts/test-ssr/src/utils/esbuild-plugin.ts +++ b/scripts/test-ssr/src/utils/esbuild-plugin.ts @@ -1,3 +1,4 @@ +import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import type { Plugin } from 'esbuild'; @@ -43,3 +44,73 @@ export function tsConfigPathsPlugin(options: { cwd: string }): Plugin { return pluginConfig; } + +/** + * Resolves `import x from './foo.ext?raw'` by stripping the suffix and loading the + * underlying file as text. Webpack/Storybook handle this via `resourceQuery: /raw/`; + * esbuild has no built-in equivalent. Mirrors webpack's behaviour where the bare + * import path may omit the extension (e.g. `./Foo.stories?raw` resolves to + * `./Foo.stories.tsx`). + */ +export function rawQueryPlugin(): Plugin { + const candidateExtensions = ['', '.tsx', '.ts', '.jsx', '.js', '.css', '.json']; + + async function resolveExisting(candidatePath: string): Promise<string | null> { + for (const ext of candidateExtensions) { + const full = candidatePath + ext; + try { + await fs.access(full); + return full; + } catch { + // try next extension + } + } + return null; + } + + return { + name: 'raw-query', + setup({ onResolve, onLoad }) { + onResolve({ filter: /\?raw$/ }, async args => { + const cleanPath = args.path.replace(/\?raw$/, ''); + const base = path.isAbsolute(cleanPath) ? cleanPath : path.resolve(args.resolveDir, cleanPath); + const resolved = await resolveExisting(base); + if (!resolved) { + return { errors: [{ text: `raw-query: could not resolve ${args.path} from ${args.resolveDir}` }] }; + } + return { path: resolved, namespace: 'raw-query' }; + }); + onLoad({ filter: /.*/, namespace: 'raw-query' }, async args => { + const contents = await fs.readFile(args.path, 'utf8'); + return { contents, loader: 'text' }; + }); + }, + }; +} + +/** + * SSR shim for `*.module.css` imports. Returns a Proxy that echoes the requested + * property name (so `styles.foo === 'foo'`), which keeps className strings stable + * for SSR rendering without needing the actual CSS-Modules transform. + */ +export function cssModulesShimPlugin(): Plugin { + return { + name: 'css-modules-shim', + setup({ onResolve, onLoad }) { + onResolve({ filter: /\.module\.css$/ }, args => { + if (args.path.includes('?')) { + return null; + } + const absolute = path.isAbsolute(args.path) ? args.path : path.resolve(args.resolveDir, args.path); + return { path: absolute, namespace: 'css-modules-shim' }; + }); + onLoad({ filter: /.*/, namespace: 'css-modules-shim' }, () => ({ + contents: [ + `const styles = new Proxy({}, { get: (_, key) => typeof key === 'string' ? key : '' });`, + `export default styles;`, + ].join('\n'), + loader: 'js', + })); + }, + }; +} From 12a3e1bcd75245b4d9314ffac067e55747e207cc Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:26:30 +0200 Subject: [PATCH 15/25] fix(headless-stories): scope ?raw module declaration to this package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../stories/src/_helpers/raw.d.ts | 11 +++++++++++ typings/static-assets/index.d.ts | 9 --------- 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/raw.d.ts diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/raw.d.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/raw.d.ts new file mode 100644 index 0000000000000..a24fdafcf9fb0 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/raw.d.ts @@ -0,0 +1,11 @@ +/** + * Scoped to this package โ€” webpack/Storybook serve `?raw` imports as the file's + * raw text. Used by `withCssModuleSource` to bundle CSS-Module source into the + * Stackblitz sandbox; story TSX is auto-injected by + * `@fluentui/babel-preset-storybook-full-source`, so individual stories no + * longer import their own source via `?raw`. + */ +declare module '*?raw' { + const content: string; + export default content; +} diff --git a/typings/static-assets/index.d.ts b/typings/static-assets/index.d.ts index d4bb050f933c5..d73d3587940f8 100644 --- a/typings/static-assets/index.d.ts +++ b/typings/static-assets/index.d.ts @@ -36,12 +36,3 @@ declare module '*.module.css' { const classes: { readonly [key: string]: string }; export default classes; } - -/** - * Webpack `?raw` query โ€” imports a file's source as a string. - * Used by stories to display CSS module source in Storybook's "Show code" panel. - */ -declare module '*?raw' { - const content: string; - export default content; -} From a754d28803781b02e0d5767f732f4f4e26b0d33f Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:26:38 +0200 Subject: [PATCH 16/25] fix(babel-preset-storybook-full-source): drop relative-import allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../src/modifyImports.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts b/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts index 32d765bd7d412..e6d370c3c07a3 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts +++ b/packages/react-components/babel-preset-storybook-full-source/src/modifyImports.ts @@ -53,20 +53,7 @@ export function modifyImportsPlugin(babel: typeof Babel, options: BabelPluginOpt const isRelativeImportToIndexBarrel = importSource.value.endsWith('./index'); if (isRelativeImport && !isRelativeImportToIndexBarrel) { - // CSS Modules, raw-loader queries, and stories' co-located - // source-extraction helpers (`withStorySource`, - // `withCssModuleSource`) are stripped from the sandbox bundle on - // purpose โ€” sister utilities recover them through other channels - // (e.g. `parameters.exportToSandbox.transformFiles`). Skipping the - // warning for these well-known patterns keeps dev console output - // clean without changing extraction behavior. - const isKnownSafeRelative = - /\.module\.css(\?|$)/.test(importSource.value) || - /\?raw$/.test(importSource.value) || - /\/with(Story|CssModule)Source$/.test(importSource.value) || - /\.stories(\?raw)?$/.test(importSource.value); - - if (process.env.NODE_ENV !== 'production' && !isKnownSafeRelative) { + if (process.env.NODE_ENV !== 'production') { console.warn( [ `๐Ÿšจ Relative import '${importSource.value}' found in ${pluginState.filename} - removing from output - this might create invalid code.`, From 2f859dd99c1b6740c52b2247970c2178377f8c97 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:26:47 +0200 Subject: [PATCH 17/25] refactor(headless-stories): extract shared CSS-modules webpack rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../.storybook/main.js | 92 ++----------------- .../stories/.storybook/css-modules-webpack.js | 90 ++++++++++++++++++ .../stories/.storybook/main.js | 67 +------------- 3 files changed, 98 insertions(+), 151 deletions(-) create mode 100644 packages/react-components/react-headless-components-preview/stories/.storybook/css-modules-webpack.js diff --git a/apps/public-docsite-v9-headless/.storybook/main.js b/apps/public-docsite-v9-headless/.storybook/main.js index d01c99f35740b..0e30e57d55cb3 100644 --- a/apps/public-docsite-v9-headless/.storybook/main.js +++ b/apps/public-docsite-v9-headless/.storybook/main.js @@ -1,96 +1,16 @@ const path = require('path'); const rootMain = require('../../../.storybook/main'); +const { + createCssModuleRule, + patchRules, + STORIES_PACKAGE_ROOT, +} = require('../../../packages/react-components/react-headless-components-preview/stories/.storybook/css-modules-webpack'); const repoRoot = path.resolve(__dirname, '../../..'); const tokensDir = path.resolve(repoRoot, 'theme'); -const headlessStoriesDir = path.resolve( - repoRoot, - 'packages/react-components/react-headless-components-preview/stories', -); -/** - * CSS Modules webpack rule for the headless docsite. - * - * Storybook's `@storybook/builder-webpack5` ships a default `\.css$` rule that - * pipes any CSS through `style-loader` + plain `css-loader`. That handles - * `theme/tokens.css` correctly. For `*.module.css` files (the per-component - * design-system styles) we want CSS Modules, so we narrow the default rule to - * skip `.module.css` and add a dedicated rule that turns on `modules: true`. - * - * Scoped via `include` so we only handle CSS coming from the design-system - * tree and the headless stories tree. - */ -const cssIncludes = [tokensDir, headlessStoriesDir]; - -const cssModuleRule = { - test: /\.module\.css$/, - include: cssIncludes, - // Skip `?raw` imports โ€” those go through Storybook's `resourceQuery:/raw/` - // asset/source rule and yield the file's text content. Without this filter, - // webpack chains style-loader + css-loader on top of asset/source and the - // story ends up importing the style-loader JS wrapper instead of the CSS. - resourceQuery: { not: [/raw/] }, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[name]__[local]--[hash:base64:5]', - }, - importLoaders: 0, - }, - }, - ], -}; - -/** - * Patches existing webpack rules in two ways: - * 1. Storybook's default `\.css$` rule gets a `\.module\.css$` exclude, so - * our CSS Modules rule is the only one that handles `*.module.css`. - * 2. Any rule whose test matches `.css` or `.tsx?` and that doesn't already - * filter on `resourceQuery` is told to skip `?raw` queries. That keeps - * Storybook's built-in `resourceQuery:/raw/` asset/source rule as the - * sole handler for `import x from 'path?raw'`, which we use to surface - * story file source and CSS Module source in the Show-code panel. - * - * Returns the input array for chaining. - */ -function patchRules(rules) { - for (const rule of rules) { - if (!rule || typeof rule !== 'object') continue; - const test = rule.test; - const isRegExp = test instanceof RegExp; - const matchesPlainCss = isRegExp && test.source === /\.css$/.source; - if (matchesPlainCss) { - const existing = rule.exclude; - const moduleRegex = /\.module\.css$/; - if (Array.isArray(existing)) { - rule.exclude = [...existing, moduleRegex]; - } else if (existing) { - rule.exclude = [existing, moduleRegex]; - } else { - rule.exclude = moduleRegex; - } - } - // Probe with synthetic file paths so we don't have to parse the regex source. - // Includes `.stories.tsx`/`.stories.ts` shapes because Storybook's - // export-order-loader and the @fluentui export-to-sandbox addon both add - // rules that match those and would otherwise re-process `?raw` imports. - const matchesCssOrTs = - isRegExp && - (test.test('a.css') || - test.test('a.tsx') || - test.test('a.ts') || - test.test('a.stories.tsx') || - test.test('a.stories.ts')); - if (matchesCssOrTs && rule.resourceQuery == null) { - rule.resourceQuery = { not: [/raw/] }; - } - } - return rules; -} +const cssModuleRule = createCssModuleRule({ tokensDir, headlessStoriesDir: STORIES_PACKAGE_ROOT }); module.exports = /** @type {Omit<import('../../../.storybook/main'), 'typescript'|'babel'>} */ ({ ...rootMain, diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/css-modules-webpack.js b/packages/react-components/react-headless-components-preview/stories/.storybook/css-modules-webpack.js new file mode 100644 index 0000000000000..69c2765bf02ef --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/css-modules-webpack.js @@ -0,0 +1,90 @@ +/** + * Shared CSS-Modules + `?raw` webpack wiring for the headless stories. + * + * Source of truth lives here (in the stories package). The app at + * `apps/public-docsite-v9-headless/.storybook/main.js` consumes it via require. + * + * Storybook's `@storybook/builder-webpack5` ships a default `\.css$` rule that + * pipes any CSS through `style-loader` + plain `css-loader`. That handles + * `theme/tokens.css` correctly. For `*.module.css` files (the per-component + * design-system styles) we want CSS Modules, so we narrow the default rule to + * skip `.module.css` and add a dedicated rule that turns on `modules: true`. + * + * `?raw` imports must go through Storybook's built-in `resourceQuery:/raw/` + * asset/source rule. We mark our CSS-Modules rule (and any default rule that + * would otherwise re-process `?raw` imports) with `resourceQuery: { not: [/raw/] }` + * so the asset/source rule wins. + */ +const path = require('path'); + +const RAW_QUERY_NOT = { not: [/raw/] }; + +/** + * @param {{ tokensDir: string; headlessStoriesDir: string }} options + */ +function createCssModuleRule({ tokensDir, headlessStoriesDir }) { + return { + test: /\.module\.css$/, + include: [tokensDir, headlessStoriesDir], + resourceQuery: RAW_QUERY_NOT, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: { localIdentName: '[name]__[local]--[hash:base64:5]' }, + importLoaders: 0, + }, + }, + ], + }; +} + +/** + * Mutates rule entries in place: + * 1. Storybook's default `\.css$` rule gets a `\.module\.css$` exclude. + * 2. Any rule whose test matches `.css`/`.tsx?` and has no `resourceQuery` + * filter is told to skip `?raw` queries โ€” including `.stories.tsx?` shapes + * so the export-order-loader and the @fluentui export-to-sandbox addon + * don't re-process raw imports. + * + * @param {any[]} rules + */ +function patchRules(rules) { + for (const rule of rules) { + if (!rule || typeof rule !== 'object') continue; + const test = rule.test; + const isRegExp = test instanceof RegExp; + const matchesPlainCss = isRegExp && test.source === /\.css$/.source; + if (matchesPlainCss) { + const existing = rule.exclude; + const moduleRegex = /\.module\.css$/; + if (Array.isArray(existing)) { + rule.exclude = [...existing, moduleRegex]; + } else if (existing) { + rule.exclude = [existing, moduleRegex]; + } else { + rule.exclude = moduleRegex; + } + } + const matchesCssOrTs = + isRegExp && + (test.test('a.css') || + test.test('a.tsx') || + test.test('a.ts') || + test.test('a.stories.tsx') || + test.test('a.stories.ts')); + if (matchesCssOrTs && rule.resourceQuery == null) { + rule.resourceQuery = RAW_QUERY_NOT; + } + } + return rules; +} + +const STORIES_PACKAGE_ROOT = path.resolve(__dirname, '..'); + +module.exports = { + STORIES_PACKAGE_ROOT, + createCssModuleRule, + patchRules, +}; diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js index 8c3a7c80ec3a7..d1444a1eb1acc 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/main.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/main.js @@ -1,75 +1,12 @@ const path = require('path'); const rootMain = require('../../../../../.storybook/main'); +const { createCssModuleRule, patchRules, STORIES_PACKAGE_ROOT } = require('./css-modules-webpack'); const repoRoot = path.resolve(__dirname, '../../../../..'); const tokensDir = path.resolve(repoRoot, 'theme'); -const headlessStoriesDir = path.resolve(__dirname, '..'); -/** - * CSS Modules webpack rule for the per-package storybook. Mirrors the docsite - * at apps/public-docsite-v9-headless/.storybook/main.js โ€” see that file for - * the design rationale (default Storybook rule handles plain CSS; we narrow - * it to skip `.module.css` and add a CSS-Modules rule). - */ -const cssIncludes = [tokensDir, headlessStoriesDir]; - -const cssModuleRule = { - test: /\.module\.css$/, - include: cssIncludes, - // Skip `?raw` imports โ€” see the docsite at - // apps/public-docsite-v9-headless/.storybook/main.js for the rationale. - resourceQuery: { not: [/raw/] }, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[name]__[local]--[hash:base64:5]', - }, - importLoaders: 0, - }, - }, - ], -}; - -/** - * Mirrors `patchRules` in the docsite config โ€” see that file's comment for - * the full rationale (CSS Modules carve-out + `?raw` resourceQuery skip). - * - * @param {any[]} rules - */ -function patchRules(rules) { - for (const rule of rules) { - if (!rule || typeof rule !== 'object') continue; - const test = rule.test; - const isRegExp = test instanceof RegExp; - const matchesPlainCss = isRegExp && test.source === /\.css$/.source; - if (matchesPlainCss) { - const existing = rule.exclude; - const moduleRegex = /\.module\.css$/; - if (Array.isArray(existing)) { - rule.exclude = [...existing, moduleRegex]; - } else if (existing) { - rule.exclude = [existing, moduleRegex]; - } else { - rule.exclude = moduleRegex; - } - } - const matchesCssOrTs = - isRegExp && - (test.test('a.css') || - test.test('a.tsx') || - test.test('a.ts') || - test.test('a.stories.tsx') || - test.test('a.stories.ts')); - if (matchesCssOrTs && rule.resourceQuery == null) { - rule.resourceQuery = { not: [/raw/] }; - } - } - return rules; -} +const cssModuleRule = createCssModuleRule({ tokensDir, headlessStoriesDir: STORIES_PACKAGE_ROOT }); module.exports = /** @type {Omit<import('../../../../../.storybook/main'), 'typescript'|'babel'>} */ ({ ...rootMain, From 47151abd3d91785aec9e615a27a9f9406e26552a Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:27:00 +0200 Subject: [PATCH 18/25] refactor(headless-docsite): move docs page + source panel into docsite app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../.storybook}/HeadlessDocsPage.tsx | 41 +----------- .../.storybook}/HeadlessSourcePanel.tsx | 65 +++++++++---------- .../.storybook/headless-docs-page.css | 31 +++++++++ .../.storybook/preview.js | 7 +- .../stories/.storybook/preview.js | 9 --- .../src/_helpers/withCssModuleSource.ts | 18 +++-- 6 files changed, 81 insertions(+), 90 deletions(-) rename {packages/react-components/react-headless-components-preview/stories/src/_helpers => apps/public-docsite-v9-headless/.storybook}/HeadlessDocsPage.tsx (73%) rename {packages/react-components/react-headless-components-preview/stories/src/_helpers => apps/public-docsite-v9-headless/.storybook}/HeadlessSourcePanel.tsx (88%) create mode 100644 apps/public-docsite-v9-headless/.storybook/headless-docs-page.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx b/apps/public-docsite-v9-headless/.storybook/HeadlessDocsPage.tsx similarity index 73% rename from packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx rename to apps/public-docsite-v9-headless/.storybook/HeadlessDocsPage.tsx index c9dbd5e119a21..6150289904561 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage.tsx +++ b/apps/public-docsite-v9-headless/.storybook/HeadlessDocsPage.tsx @@ -9,7 +9,8 @@ * mirrors `packages/react-components/react-storybook-addon/src/docs/FluentDocsPage.tsx` * so the page matches what's deployed at storybooks.fluentui.dev/headless. * - * Wired in by `.storybook/preview.js` via `parameters.docs.page`. + * Wired in by `.storybook/preview.js` via `parameters.docs.page`. Lives in the + * docsite app (not the stories package) โ€” see PR #36073 review thread. */ import * as React from 'react'; @@ -106,7 +107,6 @@ export const HeadlessDocsPage: React.FC = () => { the Stackblitz button sits next to it inside the canvas footer (see `HeadlessSourcePanel` for how its clicks drive our tabbed panel). */} - <style>{headlessDocsPageCss}</style> <Title /> <Subtitle /> <Description /> @@ -154,40 +154,3 @@ export const HeadlessDocsPage: React.FC = () => { </div> ); }; - -// We let Storybook's native "Show code" toggle render inside the Canvas -// footer (alongside the "Open in Stackblitz" button injected by -// `@fluentui/react-storybook-addon-export-to-sandbox`). We hide only the -// expanded source pane it would normally reveal โ€” `<HeadlessSourcePanel>` listens to -// the native toggle's clicks and renders our tabbed panel **into the same -// canvas card** via a portal target (`.headless-source-portal`). -// -// Storybook 9 sets a fixed `height` and `overflow: hidden` on `.sbdocs-preview` -// so its border tightly hugs the rendered story; when our panel is expanded -// inside the same card we need to release both so the code panel can flow. -const headlessDocsPageCss = ` -.headless-docs-page .sbdocs-preview > *:not(.docs-story):not(.headless-source-portal) { - display: none !important; -} -.headless-docs-page .sbdocs-preview:has(> .headless-source-portal:not(:empty)) { - height: auto !important; -} - -/* - Force the magenta accent for the "Show code" / "Open in Stackblitz" hover & - focus underlines. Storybook's ActionBar paints the underline via an inset - box-shadow driven by \`theme.color.secondary\`, and the - \`@fluentui/react-storybook-addon-export-to-sandbox\` styles hard-code a blue - underline on the Stackblitz button โ€” both are overridden here so the canvas - action buttons match the rest of the headless docs accent. -*/ -.headless-docs-page .sbdocs-preview .docblock-code-toggle:hover, -.headless-docs-page .sbdocs-preview .docblock-code-toggle:focus, -.headless-docs-page .sbdocs-preview .docblock-code-toggle.docblock-code-toggle--expanded, -.headless-docs-page .docs-story .with-code-sandbox-button:hover, -.headless-docs-page .docs-story .with-code-sandbox-button:focus { - outline: none !important; - box-shadow: #9b1f5a 0 -3px 0 0 inset !important; - color: #9b1f5a !important; -} -`; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx b/apps/public-docsite-v9-headless/.storybook/HeadlessSourcePanel.tsx similarity index 88% rename from packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx rename to apps/public-docsite-v9-headless/.storybook/HeadlessSourcePanel.tsx index 0349a076a7ac2..007d7a02cf295 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessSourcePanel.tsx +++ b/apps/public-docsite-v9-headless/.storybook/HeadlessSourcePanel.tsx @@ -15,12 +15,14 @@ * Wired up by `HeadlessDocsPage`. The story's TSX comes from * `parameters.docs.source.originalSource` (set via `withStorySource`); the CSS * comes from `parameters.theme.cssModules` (set via `withCssModuleSource`). + * + * Lives in the docsite app (not the stories package) โ€” see PR #36073 review + * thread. Styled via Storybook's `styled` (emotion) so the panel inherits the + * active SB theme tokens and stays consistent with the rest of the docs chrome. */ /* eslint-disable @nx/workspace-no-restricted-globals -- Storybook docs block running in the manager iframe; uses DOM APIs to bridge to the native Canvas toggle that lives outside React. */ import * as React from 'react'; import { createPortal } from 'react-dom'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type AnyProps = Record<string, any>; // Storybook's docs blocks live behind a deep import. The `useSourceProps` hook // resolves the Source block's effective `code`/`language` for a story (honoring @@ -30,19 +32,16 @@ import { DocsContext, SourceContext, useOf, useSourceProps } from '@storybook/ad // matches the rest of the docs chrome โ€” reusing it keeps the panel visually // consistent with everything else Storybook renders. import { SyntaxHighlighter } from 'storybook/internal/components'; +import { styled } from 'storybook/theming'; -/** A CSS Module file surfaced as a tab in the code panel. */ -export interface CssModule { - /** Display name shown on the tab (e.g. `button.module.css`). */ - name: string; - /** Raw CSS source for the module (typically imported via `?raw`). */ - source: string; -} +import type { + CssModule, + HeadlessSourceParameters, + // eslint-disable-next-line @nx/enforce-module-boundaries -- relative import: stories package authoring helpers are colocated source, not a public npm dependency +} from '../../../packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource'; -export interface HeadlessSourceParameters { - /** Extra CSS Module sources to surface as tabs after the story TSX tab. */ - cssModules?: CssModule[]; -} +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyProps = Record<string, any>; interface HeadlessSourcePanelProps { /** Reference to the story being rendered (`story.moduleExport`). */ @@ -50,28 +49,24 @@ interface HeadlessSourcePanelProps { of: any; } -const SURFACE_BG = '#ffffff'; -const SURFACE_BORDER = 'rgba(38, 85, 115, 0.15)'; -const TAB_BAR_BG = '#f6f9fc'; const ACTIVE_TAB_FG = '#9b1f5a'; -const TAB_FG = '#666666'; -const containerStyle: React.CSSProperties = { +const PanelContainer = styled.div(({ theme }) => ({ // Blend into the canvas card: no own border/radius, just a top divider and // breathing room below the action bar (Show code / Open in Stackblitz). marginTop: 16, - borderTop: `1px solid ${SURFACE_BORDER}`, - background: SURFACE_BG, -}; + borderTop: `1px solid ${theme.appBorderColor}`, + background: theme.background.content, +})); -const tabBarStyle: React.CSSProperties = { +const TabBar = styled.div(({ theme }) => ({ display: 'flex', alignItems: 'stretch', - background: TAB_BAR_BG, - borderBottom: `1px solid ${SURFACE_BORDER}`, -}; + background: theme.background.app, + borderBottom: `1px solid ${theme.appBorderColor}`, +})); -const tabButtonStyle = (active: boolean): React.CSSProperties => ({ +const TabButton = styled.button<{ active: boolean }>(({ active, theme }) => ({ appearance: 'none', border: 0, background: 'transparent', @@ -79,12 +74,12 @@ const tabButtonStyle = (active: boolean): React.CSSProperties => ({ font: 'inherit', fontSize: 12, fontWeight: active ? 700 : 500, - color: active ? ACTIVE_TAB_FG : TAB_FG, + color: active ? ACTIVE_TAB_FG : theme.color.mediumdark, cursor: 'pointer', borderBottom: `2px solid ${active ? ACTIVE_TAB_FG : 'transparent'}`, marginBottom: -1, whiteSpace: 'nowrap', -}); +})); /** * Subscribe to the native "Show code" toggle that Canvas renders inside the @@ -250,22 +245,22 @@ export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) const activeTab = tabs.find(t => t.id === activeTabId) ?? tabs[0]; return createPortal( - <div className="sb-unstyled" style={containerStyle}> + <PanelContainer className="sb-unstyled"> {tabs.length > 1 && ( - <div style={tabBarStyle} role="tablist" aria-label="Source code"> + <TabBar role="tablist" aria-label="Source code"> {tabs.map(tab => ( - <button + <TabButton key={tab.id} type="button" role="tab" aria-selected={tab.id === activeTab.id} - style={tabButtonStyle(tab.id === activeTab.id)} + active={tab.id === activeTab.id} onClick={() => setActiveTabId(tab.id)} > {tab.label} - </button> + </TabButton> ))} - </div> + </TabBar> )} <div role="tabpanel"> <SyntaxHighlighter @@ -282,7 +277,7 @@ export const HeadlessSourcePanel: React.FC<HeadlessSourcePanelProps> = ({ of }) {activeTab.code} </SyntaxHighlighter> </div> - </div>, + </PanelContainer>, portalTarget, ); }; diff --git a/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css b/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css new file mode 100644 index 0000000000000..97b8c415cc5db --- /dev/null +++ b/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css @@ -0,0 +1,31 @@ +/* + Loaded once from .storybook/preview.js. Drives the docs-page chrome around + the canvas card so HeadlessSourcePanel can portal its tabbed code panel into + the same bordered preview. +*/ + +.headless-docs-page .sbdocs-preview > *:not(.docs-story):not(.headless-source-portal) { + display: none !important; +} + +.headless-docs-page .sbdocs-preview:has(> .headless-source-portal:not(:empty)) { + height: auto !important; +} + +/* + Force the magenta accent for the "Show code" / "Open in Stackblitz" hover & + focus underlines. Storybook's ActionBar paints the underline via an inset + box-shadow driven by `theme.color.secondary`, and the + `@fluentui/react-storybook-addon-export-to-sandbox` styles hard-code a blue + underline on the Stackblitz button โ€” both are overridden here so the canvas + action buttons match the rest of the headless docs accent. +*/ +.headless-docs-page .sbdocs-preview .docblock-code-toggle:hover, +.headless-docs-page .sbdocs-preview .docblock-code-toggle:focus, +.headless-docs-page .sbdocs-preview .docblock-code-toggle.docblock-code-toggle--expanded, +.headless-docs-page .docs-story .with-code-sandbox-button:hover, +.headless-docs-page .docs-story .with-code-sandbox-button:focus { + outline: none !important; + box-shadow: #9b1f5a 0 -3px 0 0 inset !important; + color: #9b1f5a !important; +} diff --git a/apps/public-docsite-v9-headless/.storybook/preview.js b/apps/public-docsite-v9-headless/.storybook/preview.js index 34ed78bd1f5a3..c817a2bfa220a 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview.js +++ b/apps/public-docsite-v9-headless/.storybook/preview.js @@ -7,9 +7,10 @@ import * as rootPreview from '../../../.storybook/preview'; // for every story rendered in this Storybook. import '../../../packages/react-components/react-headless-components-preview/stories/theme/tokens.css'; -// Custom docs page that renders the "Show code" panel with TSX | CSS tabs. -// See `packages/.../stories/src/_helpers/HeadlessDocsPage.tsx` for rationale. -import { HeadlessDocsPage } from '../../../packages/react-components/react-headless-components-preview/stories/src/_helpers/HeadlessDocsPage'; +// Custom docs page chrome (disclaimer card, divider, headings) and the +// docs-page wiring rules that let HeadlessSourcePanel portal into the canvas card. +import './headless-docs-page.css'; +import { HeadlessDocsPage } from './HeadlessDocsPage'; polyfillBodyAndObserve(); diff --git a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js index c1489be6a95b2..d423b8d194d14 100644 --- a/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js +++ b/packages/react-components/react-headless-components-preview/stories/.storybook/preview.js @@ -7,11 +7,6 @@ import * as rootPreview from '../../../../../.storybook/preview'; // storybook (built by `pr-website-deploy.yml`) renders identical stories. import '../theme/tokens.css'; -// Custom docs page that renders the "Show code" panel with TSX | CSS tabs. -// See `stories/src/_helpers/HeadlessDocsPage.tsx` for the rationale (Storybook's -// built-in Source block can't be made multi-language via MDX overrides). -import { HeadlessDocsPage } from '../src/_helpers/HeadlessDocsPage'; - polyfillBodyAndObserve(); /** @type {typeof rootPreview.decorators} */ @@ -20,10 +15,6 @@ export const decorators = [...rootPreview.decorators]; /** @type {typeof rootPreview.parameters} */ export const parameters = { ...rootPreview.parameters, - docs: { - ...(rootPreview.parameters && rootPreview.parameters.docs), - page: HeadlessDocsPage, - }, }; export const tags = ['autodocs']; diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts index df23d4d715a2b..74b23a898f6f7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts +++ b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withCssModuleSource.ts @@ -1,8 +1,9 @@ /** * Story meta helper โ€” registers the CSS Module source(s) a story relies on so: * - * 1. The custom docs page (`HeadlessDocsPage` โ†’ `HeadlessSourcePanel`) can surface them - * as tabs in the "Show code" panel. + * 1. The docsite app's custom docs page (`HeadlessSourcePanel`, lives at + * `apps/public-docsite-v9-headless/.storybook/HeadlessSourcePanel.tsx`) + * can surface them as tabs in the "Show code" panel. * 2. The "Open in Stackblitz" button (provided by * `@fluentui/react-storybook-addon-export-to-sandbox`) can bundle them โ€” * together with `theme/tokens.css` โ€” into the generated sandbox so the @@ -33,9 +34,18 @@ // requiring story authors to wire imports manually. import tokensCss from '../../theme/tokens.css?raw'; -import type { CssModule, HeadlessSourceParameters } from './HeadlessSourcePanel'; +/** A CSS Module file surfaced as a tab in the docs page's "Show code" panel. */ +export interface CssModule { + /** Display name shown on the tab (e.g. `button.module.css`). */ + name: string; + /** Raw CSS source for the module (typically imported via `?raw`). */ + source: string; +} -export type { CssModule } from './HeadlessSourcePanel'; +/** Shape consumed by the docsite's `HeadlessSourcePanel` via `parameters.theme`. */ +export interface HeadlessSourceParameters { + cssModules?: CssModule[]; +} /** * Minimal local mirror of the `SandboxContext` shape from From dc11b17fe3eac75a521a55703f1671b394674805 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:27:08 +0200 Subject: [PATCH 19/25] fix(headless-docsite): load Segoe UI from c.s-microsoft.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> --- .../.storybook/manager-head.html | 11 +++++++++++ .../.storybook/preview-head.html | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/apps/public-docsite-v9-headless/.storybook/manager-head.html b/apps/public-docsite-v9-headless/.storybook/manager-head.html index e8c4d5e419905..dd8096b434079 100644 --- a/apps/public-docsite-v9-headless/.storybook/manager-head.html +++ b/apps/public-docsite-v9-headless/.storybook/manager-head.html @@ -4,6 +4,17 @@ <link rel="shortcut icon" type="image/x-icon" href="https://react.fluentui.dev/favicon-192.ico" /> <link href="/shell.css" rel="stylesheet" /> +<!-- + Segoe UI from Microsoft's static font CDN. The fallbacks in `font-family` + below cover users on networks blocking the CDN, but on a healthy connection + the docsite renders Segoe UI on every platform โ€” not just Windows. + --> +<link rel="preconnect" href="https://c.s-microsoft.com" crossorigin /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.css" /> + <!-- Headless docsite chrome overrides for the Storybook manager UI. diff --git a/apps/public-docsite-v9-headless/.storybook/preview-head.html b/apps/public-docsite-v9-headless/.storybook/preview-head.html index d1ae5416412f3..8d2f43fb5e77d 100644 --- a/apps/public-docsite-v9-headless/.storybook/preview-head.html +++ b/apps/public-docsite-v9-headless/.storybook/preview-head.html @@ -5,6 +5,18 @@ reach the iframe via webpack. This file holds raw <link> / <style> tags that must be present in the iframe document head before any story renders. --> + +<!-- + Segoe UI from Microsoft's static font CDN โ€” same set as `manager-head.html`. + Story canvases declare `font-family: 'Segoe UI', system-ui` via the design + tokens, so loading the webfont here makes them render Segoe on every OS. +--> +<link rel="preconnect" href="https://c.s-microsoft.com" crossorigin /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.css" /> +<link rel="stylesheet" href="https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.css" /> + <style> :root { interpolate-size: allow-keywords; From d6242063b69f8de84ac76699fa96765c28f04888 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:27:21 +0200 Subject: [PATCH 20/25] feat(babel-preset-storybook-full-source): auto-inject docs source params 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> --- ...-533c6664-8c70-4ba3-9465-119e1f33b61c.json | 2 +- .../add-parameter/output.js | 7 ++ .../add-with-parameters/output.js | 6 + .../keep-sourcecode-spacing/output.js | 7 ++ .../mutilple-components/output.js | 7 ++ .../src/cleanSource.ts | 24 ++++ .../src/fullsource.ts | 43 ++++++++ .../AccordionCollapsible.stories.tsx | 4 - .../Accordion/AccordionDefault.stories.tsx | 4 - .../src/Avatar/AvatarDefault.stories.tsx | 4 - .../src/Badge/BadgeDefault.stories.tsx | 4 - .../Breadcrumb/BreadcrumbDefault.stories.tsx | 4 - .../src/Button/ButtonDefault.stories.tsx | 5 - .../stories/src/Card/CardDefault.stories.tsx | 52 +++------ .../stories/src/Card/CardDisabled.stories.tsx | 29 ++--- .../src/Card/CardSelectable.stories.tsx | 54 +++------ .../stories/src/Card/index.stories.tsx | 4 + .../src/Checkbox/CheckboxDefault.stories.tsx | 4 - .../src/Dialog/DialogAlert.stories.tsx | 4 - .../src/Dialog/DialogControlled.stories.tsx | 4 - .../src/Dialog/DialogDefault.stories.tsx | 4 - .../src/Dialog/DialogKeepMounted.stories.tsx | 4 - .../src/Dialog/DialogNested.stories.tsx | 4 - .../src/Dialog/DialogNoTrigger.stories.tsx | 4 - .../src/Dialog/DialogNonModal.stories.tsx | 4 - .../Dialog/DialogWithCloseButton.stories.tsx | 4 - .../src/Divider/DividerDefault.stories.tsx | 4 - .../src/Divider/DividerVertical.stories.tsx | 4 - .../src/Field/FieldDefault.stories.tsx | 4 - .../stories/src/Input/InputBasic.stories.tsx | 4 - .../src/Input/InputDefault.stories.tsx | 4 - .../stories/src/Link/LinkDefault.stories.tsx | 4 - .../MessageBar/MessageBarDefault.stories.tsx | 4 - .../MessageBar/MessageBarIntent.stories.tsx | 6 +- .../ProgressBarDefault.stories.tsx | 4 - .../RadioGroup/RadioGroupDefault.stories.tsx | 4 - .../src/Rating/RatingDefault.stories.tsx | 4 - .../RatingDisplayCompact.stories.tsx | 4 - .../RatingDisplayDefault.stories.tsx | 4 - .../SearchBox/SearchBoxDefault.stories.tsx | 4 - .../src/Select/SelectDefault.stories.tsx | 4 - .../src/Skeleton/SkeletonDefault.stories.tsx | 4 - .../src/Slider/SliderDefault.stories.tsx | 4 - .../SpinButton/SpinButtonDefault.stories.tsx | 4 - .../src/Spinner/SpinnerDefault.stories.tsx | 4 - .../src/Spinner/SpinnerLabels.stories.tsx | 4 - .../src/Switch/SwitchDefault.stories.tsx | 4 - .../src/TabList/TabListDefault.stories.tsx | 4 - .../src/Textarea/TextareaDefault.stories.tsx | 4 - .../ToggleButtonDefault.stories.tsx | 4 - .../src/Toolbar/ToolbarDefault.stories.tsx | 4 - .../Toolbar/ToolbarToggleButton.stories.tsx | 4 - .../src/Toolbar/ToolbarVertical.stories.tsx | 4 - .../stories/src/_helpers/withStorySource.ts | 104 ------------------ 54 files changed, 140 insertions(+), 370 deletions(-) create mode 100644 packages/react-components/babel-preset-storybook-full-source/src/cleanSource.ts delete mode 100644 packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts diff --git a/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json b/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json index 0bd5de89ef7fd..fed33d7a73354 100644 --- a/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json +++ b/change/@fluentui-babel-preset-storybook-full-source-533c6664-8c70-4ba3-9465-119e1f33b61c.json @@ -1,6 +1,6 @@ { "type": "none", - "comment": "chore: skip warnings for known-safe relative imports (*.module.css, ?raw queries, withStorySource/withCssModuleSource helpers, *.stories?raw)", + "comment": "chore: auto-inject parameters.docs.source.code/originalSource for stories (consumers no longer need to wire withStorySource)", "packageName": "@fluentui/babel-preset-storybook-full-source", "email": "tudor.popa@microsoft.com", "dependentChangeType": "none" diff --git a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-parameter/output.js b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-parameter/output.js index 43d4721bea42e..546932dea5f34 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-parameter/output.js +++ b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-parameter/output.js @@ -9,3 +9,10 @@ Default.parameters = { }; Default.parameters.fullSource = 'import * as React from "react";\n\nexport const Default = () => <Button>Click me</Button>;\n'; +Default.parameters.docs = Object.assign({}, Default.parameters.docs, { + source: Object.assign({}, Default.parameters.docs && Default.parameters.docs.source, { + code: "import * as React from 'react';\n\nexport const Default = () => <Button>Click me</Button>;\nDefault.parameters = {\n docsMode: {\n description: {\n story: 'The default story',\n },\n },\n};\n", + originalSource: + "import * as React from 'react';\n\nexport const Default = () => <Button>Click me</Button>;\nDefault.parameters = {\n docsMode: {\n description: {\n story: 'The default story',\n },\n },\n};\n", + }), +}); diff --git a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-with-parameters/output.js b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-with-parameters/output.js index 55b4bd34f66f3..09f6842151240 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-with-parameters/output.js +++ b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/add-with-parameters/output.js @@ -3,3 +3,9 @@ export const Default = () => /*#__PURE__*/ React.createElement(Button, null, 'Cl Default.parameters = {}; Default.parameters.fullSource = 'import * as React from "react";\n\nexport const Default = () => <Button>Click me</Button>;\n'; +Default.parameters.docs = Object.assign({}, Default.parameters.docs, { + source: Object.assign({}, Default.parameters.docs && Default.parameters.docs.source, { + code: "import * as React from 'react';\n\nexport const Default = () => <Button>Click me</Button>;\n", + originalSource: "import * as React from 'react';\n\nexport const Default = () => <Button>Click me</Button>;\n", + }), +}); diff --git a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/keep-sourcecode-spacing/output.js b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/keep-sourcecode-spacing/output.js index 74298daed100e..092d7213dc158 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/keep-sourcecode-spacing/output.js +++ b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/keep-sourcecode-spacing/output.js @@ -36,3 +36,10 @@ export const ButtonAppearance = () => ButtonAppearance.parameters = {}; ButtonAppearance.parameters.fullSource = 'import * as React from "react";\n\nexport const ButtonAppearance = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n'; +ButtonAppearance.parameters.docs = Object.assign({}, ButtonAppearance.parameters.docs, { + source: Object.assign({}, ButtonAppearance.parameters.docs && ButtonAppearance.parameters.docs.source, { + code: 'import * as React from \'react\';\n\nexport const ButtonAppearance = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n', + originalSource: + 'import * as React from \'react\';\n\nexport const ButtonAppearance = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n', + }), +}); diff --git a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/mutilple-components/output.js b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/mutilple-components/output.js index 2b8a474e59394..3ead6381f799b 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/mutilple-components/output.js +++ b/packages/react-components/babel-preset-storybook-full-source/src/__fixtures__/storybook-stories-fullsource/mutilple-components/output.js @@ -48,3 +48,10 @@ const Child2 = () => ButtonAppearance.parameters = {}; ButtonAppearance.parameters.fullSource = 'import * as React from "react";\n\nconst Child1 = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n </>\n);\n\nexport const ButtonAppearance = () => (\n <>\n <Child1 />\n <Child2 />\n </>\n);\n\nconst Child2 = () => (\n <>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n'; +ButtonAppearance.parameters.docs = Object.assign({}, ButtonAppearance.parameters.docs, { + source: Object.assign({}, ButtonAppearance.parameters.docs && ButtonAppearance.parameters.docs.source, { + code: 'import * as React from \'react\';\n\nconst Child1 = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n </>\n);\n\nexport const ButtonAppearance = () => (\n <>\n <Child1 />\n <Child2 />\n </>\n);\n\nconst Child2 = () => (\n <>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n', + originalSource: + 'import * as React from \'react\';\n\nconst Child1 = () => (\n <>\n <Button>Default button</Button>\n <Button appearance="primary">Primary button</Button>\n <Button appearance="outline">Outline button</Button>\n </>\n);\n\nexport const ButtonAppearance = () => (\n <>\n <Child1 />\n <Child2 />\n </>\n);\n\nconst Child2 = () => (\n <>\n <Button appearance="subtle">Subtle button</Button>\n <Button appearance="transparent">Transparent button</Button>\n </>\n);\n', + }), +}); diff --git a/packages/react-components/babel-preset-storybook-full-source/src/cleanSource.ts b/packages/react-components/babel-preset-storybook-full-source/src/cleanSource.ts new file mode 100644 index 0000000000000..769ea6ec4d03b --- /dev/null +++ b/packages/react-components/babel-preset-storybook-full-source/src/cleanSource.ts @@ -0,0 +1,24 @@ +/** + * Normalize a story file's raw source for display in the docs page's + * "Show code" panel: + * + * - Strip any leftover `withStorySource` plumbing (kept idempotent in case a + * few stories haven't been migrated yet). + * - Rewrite colocated `*.module.css` import paths to `./styles/<basename>`, + * matching the layout used by the generated Stackblitz sandbox so the + * snippet is paste-ready. + * - Collapse blank-line runs left behind by removed lines. + */ +export function cleanStorySource(source: string): string { + return source + .replace(/^import\s+\w+\s+from\s+['"]\..*?\.stories\?raw['"];\s*\r?\n/m, '') + .replace(/^import\s*\{\s*withStorySource\s*\}\s*from\s+['"][^'"]+['"];\s*\r?\n/m, '') + .replace(/^\w+\.parameters\s*=\s*withStorySource\([\s\S]*?\);\s*\r?\n?/m, '') + .replace( + /(['"])(?:\.{1,2}\/)+(?:[^'"\/]+\/)*([^'"\/]+\.module\.css)\1/g, + (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, + ) + .replace(/\n{3,}/g, '\n\n') + .trimEnd() + .concat('\n'); +} diff --git a/packages/react-components/babel-preset-storybook-full-source/src/fullsource.ts b/packages/react-components/babel-preset-storybook-full-source/src/fullsource.ts index e3134b7d63ee2..749a6c2007c83 100644 --- a/packages/react-components/babel-preset-storybook-full-source/src/fullsource.ts +++ b/packages/react-components/babel-preset-storybook-full-source/src/fullsource.ts @@ -2,6 +2,7 @@ import * as Babel from '@babel/core'; import * as prettier from 'prettier'; import * as fs from 'fs'; +import { cleanStorySource } from './cleanSource'; import { modifyImportsPlugin } from './modifyImports'; import { removeStorybookParameters } from './removeStorybookParameters'; import { BabelPluginOptions } from './types'; @@ -50,6 +51,47 @@ export function fullSourcePlugin(babel: typeof Babel, options: BabelPluginOption ); }; + /** + * Builds: + * + * Story.parameters.docs = Object.assign({}, Story.parameters.docs, { + * source: Object.assign({}, + * Story.parameters.docs && Story.parameters.docs.source, + * { code: <SOURCE>, originalSource: <SOURCE> }), + * }); + * + * Set as a separate statement (rather than inlined into the parameters + * literal) so it composes with whatever shape the story author already + * declared โ€” `parameters.docs.description.story`, custom transforms, etc. + */ + const createDocsSourceAssignmentExpression = (cleanedSource: string) => { + const storyParametersDocs = t.memberExpression( + t.memberExpression(t.identifier(storyName), t.identifier('parameters')), + t.identifier('docs'), + ); + const storyParametersDocsSource = t.memberExpression(storyParametersDocs, t.identifier('source')); + + const source = t.stringLiteral(cleanedSource); + const newSourceProps = t.objectExpression([ + t.objectProperty(t.identifier('code'), source), + t.objectProperty(t.identifier('originalSource'), source), + ]); + + const sourceAssign = t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [ + t.objectExpression([]), + t.logicalExpression('&&', storyParametersDocs, storyParametersDocsSource), + newSourceProps, + ]); + + const docsAssign = t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [ + t.objectExpression([]), + storyParametersDocs, + t.objectExpression([t.objectProperty(t.identifier('source'), sourceAssign)]), + ]); + + return t.expressionStatement(t.assignmentExpression('=', storyParametersDocs, docsAssign)); + }; + return { name: PLUGIN_NAME, visitor: { @@ -116,6 +158,7 @@ export function fullSourcePlugin(babel: typeof Babel, options: BabelPluginOption } path.pushContainer('body', createFullSourceAssignmentExpression(code)); + path.pushContainer('body', createDocsSourceAssignmentExpression(cleanStorySource(fileContents))); }, }, }, diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx index eb2aa37b81e5b..f35de098c055c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionCollapsible.stories.tsx @@ -8,8 +8,6 @@ import { import { ChevronRightRegular } from '@fluentui/react-icons'; import styles from './accordion.module.css'; -import storySource from './AccordionCollapsible.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const items = [ { value: 'item-1', header: 'Section one', panel: 'All items can be collapsed.' }, { value: 'item-2', header: 'Section two', panel: 'Click an open item to close it.' }, @@ -32,5 +30,3 @@ export const Collapsible = (): React.ReactNode => ( ))} </Accordion> ); - -Collapsible.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx index 9b05b34029ec6..9d1fe0af86a39 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Accordion/AccordionDefault.stories.tsx @@ -8,8 +8,6 @@ import { import { ChevronRightRegular } from '@fluentui/react-icons'; import styles from './accordion.module.css'; -import storySource from './AccordionDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const items = [ { value: 'overview', @@ -44,5 +42,3 @@ export const Default = (): React.ReactNode => ( ))} </Accordion> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx index 9e874beae01af..a6898d3c5deb2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Avatar/AvatarDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Avatar } from '@fluentui/react-headless-components-preview/avatar'; import styles from './avatar.module.css'; -import storySource from './AvatarDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <div className={styles.demoRow}> @@ -42,5 +40,3 @@ export const Default = (): React.ReactNode => ( </div> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx index 6ad096e6bfd91..8f6715cd7655c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Badge/BadgeDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Badge } from '@fluentui/react-headless-components-preview/badge'; import styles from './badge.module.css'; -import storySource from './BadgeDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <Badge className={styles.badge}>Default</Badge> @@ -27,5 +25,3 @@ export const Default = (): React.ReactNode => ( <Badge className={`${styles.badge} ${styles.counter}`}>9</Badge> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx index 8ad3e8e4d8798..a0a68daa8ad44 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Breadcrumb/BreadcrumbDefault.stories.tsx @@ -8,8 +8,6 @@ import { import { ChevronRightRegular } from '@fluentui/react-icons'; import styles from './breadcrumb.module.css'; -import storySource from './BreadcrumbDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <Breadcrumb aria-label="Navigation" className={styles.crumb} list={{ className: styles.list }}> <BreadcrumbItem className={styles.item}> @@ -31,5 +29,3 @@ export const Default = (): React.ReactNode => ( </BreadcrumbItem> </Breadcrumb> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx index adfa217c1cf88..76192faaf98d6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Button/ButtonDefault.stories.tsx @@ -3,9 +3,6 @@ import { Button } from '@fluentui/react-headless-components-preview/button'; import { AddRegular } from '@fluentui/react-icons'; import styles from './button.module.css'; -import storySource from './ButtonDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; - export const Default = (): React.ReactNode => ( <div className={styles.demo}> <div className={styles.demoRow}> @@ -50,5 +47,3 @@ export const Default = (): React.ReactNode => ( </div> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/CardDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Card/CardDefault.stories.tsx index 945601e50de4b..6dfd6dd971b04 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/CardDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/CardDefault.stories.tsx @@ -2,72 +2,50 @@ import * as React from 'react'; import { Card, CardHeader, CardPreview, CardFooter } from '@fluentui/react-headless-components-preview/card'; import { MoreHorizontalRegular, ShareRegular, ArrowReplyRegular } from '@fluentui/react-icons'; -const classes = { - card: - 'flex flex-col gap-3 w-80 p-3 bg-white rounded-lg border border-gray-200 shadow-sm ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2', - preview: 'flex items-center justify-center bg-gray-100 rounded-md overflow-hidden -mx-3 -mt-3', - previewImage: 'block w-full h-40 object-cover', - header: 'flex items-center gap-3', - headerImage: 'flex h-10 w-10 rounded-md overflow-hidden bg-gray-100', - headerImg: 'h-full w-full object-cover', - headerTitle: 'text-sm font-semibold text-gray-900 leading-tight', - headerDescription: 'text-xs text-gray-500 leading-tight', - headerAction: 'ml-auto flex items-center', - iconButton: - 'inline-flex items-center justify-center h-8 w-8 rounded-md text-gray-600 ' + - 'hover:bg-gray-100 active:bg-gray-200 ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2', - body: 'text-sm text-gray-700 leading-snug', - footer: 'flex items-center gap-2 pt-1', - footerButton: - 'inline-flex items-center gap-1.5 h-8 px-3 rounded-md text-sm text-gray-700 border border-gray-200 ' + - 'hover:bg-gray-100 active:bg-gray-200 ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2', -}; +import styles from './card.module.css'; export const Default = (): React.ReactNode => ( - <Card className={classes.card}> - <CardPreview className={classes.preview}> + <Card className={styles.card}> + <CardPreview className={styles.preview}> <img - className={classes.previewImage} + className={styles.previewImage} src="https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/image.png" alt="Preview" /> </CardPreview> <CardHeader - className={classes.header} + className={styles.header} image={ - <div className={classes.headerImage}> + <div className={styles.headerImage}> <img - className={classes.headerImg} + className={styles.headerImg} src="https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/square-image.png" alt="" /> </div> } - header={<div className={classes.headerTitle}>App Name</div>} - description={<div className={classes.headerDescription}>Developer</div>} + header={<div className={styles.headerTitle}>App Name</div>} + description={<div className={styles.headerDescription}>Developer</div>} action={ - <div className={classes.headerAction}> - <button type="button" aria-label="More options" className={classes.iconButton}> + <div className={styles.headerAction}> + <button type="button" aria-label="More options" className={styles.iconButton}> <MoreHorizontalRegular /> </button> </div> } /> - <div className={classes.body}> + <div className={styles.body}> Donut chocolate bar oat cake. Dragรฉe tiramisu lollipop bear claw. Marshmallow pastry jujubes toffee sugar plum. </div> - <CardFooter className={classes.footer}> - <button type="button" className={classes.footerButton}> + <CardFooter className={styles.footer}> + <button type="button" className={styles.footerButton}> <ArrowReplyRegular aria-hidden /> Reply </button> - <button type="button" className={classes.footerButton}> + <button type="button" className={styles.footerButton}> <ShareRegular aria-hidden /> Share </button> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/CardDisabled.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Card/CardDisabled.stories.tsx index 85f59ac289ab3..8107a80077440 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/CardDisabled.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/CardDisabled.stories.tsx @@ -1,44 +1,33 @@ import * as React from 'react'; import { Card, CardHeader, CardPreview } from '@fluentui/react-headless-components-preview/card'; -const classes = { - card: - 'relative flex flex-col gap-3 w-80 p-3 bg-white rounded-lg border border-gray-200 shadow-sm ' + - 'aria-disabled:opacity-50 aria-disabled:cursor-not-allowed', - checkbox: 'absolute top-3 left-3 h-4 w-4 accent-blue-600 disabled:cursor-not-allowed', - preview: 'flex items-center justify-center bg-gray-100 rounded-md overflow-hidden -mx-3 -mt-3', - previewImage: 'block w-full h-40 object-cover', - header: 'flex items-center gap-3 pl-6', - headerTitle: 'text-sm font-semibold text-gray-900 leading-tight', - headerDescription: 'text-xs text-gray-500 leading-tight', - body: 'text-sm text-gray-700 leading-snug', -}; +import styles from './card.module.css'; export const Disabled = (): React.ReactNode => ( <Card - className={classes.card} + className={`${styles.card} ${styles.cardSelectable}`} disabled selected onSelectionChange={() => { /* no-op */ }} - checkbox={{ className: classes.checkbox, 'aria-label': 'Select card' }} + checkbox={{ className: styles.checkbox, 'aria-label': 'Select card' }} > - <CardPreview className={classes.preview}> + <CardPreview className={styles.preview}> <img - className={classes.previewImage} + className={styles.previewImage} src="https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/image.png" alt="Preview" /> </CardPreview> <CardHeader - className={classes.header} - header={<div className={classes.headerTitle}>Disabled card</div>} - description={<div className={classes.headerDescription}>Selection is locked</div>} + className={`${styles.header} ${styles.headerWithSelect}`} + header={<div className={styles.headerTitle}>Disabled card</div>} + description={<div className={styles.headerDescription}>Selection is locked</div>} /> - <div className={classes.body}> + <div className={styles.body}> A disabled card sets `aria-disabled="true"` on the root and short-circuits selection toggling. </div> </Card> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/CardSelectable.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Card/CardSelectable.stories.tsx index dc6dfc5a0f00e..b759a2ea3f082 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/CardSelectable.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/CardSelectable.stories.tsx @@ -3,65 +3,41 @@ import type { CardOnSelectionChangeEvent } from '@fluentui/react-headless-compon import { Card, CardHeader, CardPreview } from '@fluentui/react-headless-components-preview/card'; import { MoreHorizontalRegular } from '@fluentui/react-icons'; -const classes = { - card: - 'relative flex flex-col gap-3 w-80 p-3 bg-white rounded-lg border border-gray-200 shadow-sm cursor-pointer ' + - 'hover:bg-gray-50 transition-colors ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2 ' + - 'data-[selected]:border-blue-500 data-[selected]:ring-2 data-[selected]:ring-blue-500 ' + - 'aria-disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:hover:bg-white', - checkbox: - 'absolute top-3 left-3 h-4 w-4 cursor-pointer accent-blue-600 ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2', - preview: 'flex items-center justify-center bg-gray-100 rounded-md overflow-hidden -mx-3 -mt-3', - previewImage: 'block w-full h-40 object-cover', - header: 'flex items-center gap-3 pl-6', - headerImage: 'flex h-10 w-10 rounded-md overflow-hidden bg-gray-100', - headerImg: 'h-full w-full object-cover', - headerTitle: 'text-sm font-semibold text-gray-900 leading-tight', - headerDescription: 'text-xs text-gray-500 leading-tight', - headerAction: 'ml-auto flex items-center', - iconButton: - 'inline-flex items-center justify-center h-8 w-8 rounded-md text-gray-600 ' + - 'hover:bg-gray-100 active:bg-gray-200 ' + - 'focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-2', - body: 'text-sm text-gray-700 leading-snug', - status: 'text-xs text-gray-500', -}; +import styles from './card.module.css'; const CardContent = ({ title }: { title: string }): React.ReactElement => ( <> - <CardPreview className={classes.preview}> + <CardPreview className={styles.preview}> <img - className={classes.previewImage} + className={styles.previewImage} src="https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/image.png" alt="Preview" /> </CardPreview> <CardHeader - className={classes.header} + className={`${styles.header} ${styles.headerWithSelect}`} image={ - <div className={classes.headerImage}> + <div className={styles.headerImage}> <img - className={classes.headerImg} + className={styles.headerImg} src="https://fabricweb.azureedge.net/fabric-website/assets/images/wireframe/square-image.png" alt="" /> </div> } - header={<div className={classes.headerTitle}>{title}</div>} - description={<div className={classes.headerDescription}>Developer</div>} + header={<div className={styles.headerTitle}>{title}</div>} + description={<div className={styles.headerDescription}>Developer</div>} action={ - <div className={classes.headerAction}> - <button type="button" aria-label="More options" className={classes.iconButton}> + <div className={styles.headerAction}> + <button type="button" aria-label="More options" className={styles.iconButton}> <MoreHorizontalRegular /> </button> </div> } /> - <div className={classes.body}> + <div className={styles.body}> Donut chocolate bar oat cake. Dragรฉe tiramisu lollipop bear claw. Marshmallow pastry jujubes toffee sugar plum. </div> </> @@ -75,17 +51,17 @@ export const Selectable = (): React.ReactNode => { }; return ( - <div className="flex flex-col gap-3"> + <div className={styles.list}> <Card - className={classes.card} + className={`${styles.card} ${styles.cardSelectable}`} selected={selected} onSelectionChange={onSelectionChange} - checkbox={{ className: classes.checkbox, 'aria-label': 'Select card' }} + checkbox={{ className: styles.checkbox, 'aria-label': 'Select card' }} > <CardContent title="Selectable card" /> </Card> - <p className={classes.status}>Selected: {String(selected)}</p> + <p className={styles.status}>Selected: {String(selected)}</p> </div> ); }; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx index 0ec1d869929e9..4938c44cccff2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/index.stories.tsx @@ -1,6 +1,8 @@ import { Card, CardHeader, CardPreview, CardFooter } from '@fluentui/react-headless-components-preview/card'; import descriptionMd from './CardDescription.md'; +import cardCss from './card.module.css?raw'; +import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './CardDefault.stories'; export { Selectable } from './CardSelectable.stories'; @@ -16,5 +18,7 @@ export default { component: descriptionMd, }, }, + + ...withCssModuleSource({ name: 'card.module.css', source: cardCss }), }, }; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx index 6e7ba12e86480..cd6f76bd35749 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Checkbox/CheckboxDefault.stories.tsx @@ -3,8 +3,6 @@ import { Checkbox } from '@fluentui/react-headless-components-preview/checkbox'; import { CheckmarkRegular } from '@fluentui/react-icons'; import styles from './checkbox.module.css'; -import storySource from './CheckboxDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.list}> <Checkbox @@ -38,5 +36,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx index 50c10ee27ccd0..2fe172fb3a76f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogAlert.stories.tsx @@ -9,8 +9,6 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import styles from './dialog.module.css'; -import storySource from './DialogAlert.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * An alert dialog uses `modalType="alert"`, which sets `role="alertdialog"` on the surface. * It is intended for critical messages that require the user to make a decision before proceeding. @@ -50,5 +48,3 @@ export const Alert = (): React.ReactNode => ( </DialogSurface> </Dialog> ); - -Alert.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx index b5884c62a6c5d..52f2d0069a96c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogControlled.stories.tsx @@ -9,8 +9,6 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import styles from './dialog.module.css'; -import storySource from './DialogControlled.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * In controlled mode the parent owns the open state. * Pass `open` and `onOpenChange` together โ€” `onOpenChange` fires for every @@ -49,5 +47,3 @@ export const Controlled = (): React.ReactNode => { </Dialog> ); }; - -Controlled.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx index ab2c8578dd51b..3bf385cbaea47 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogDefault.stories.tsx @@ -9,8 +9,6 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import styles from './dialog.module.css'; -import storySource from './DialogDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <Dialog> <DialogTrigger> @@ -40,5 +38,3 @@ export const Default = (): React.ReactNode => ( </DialogSurface> </Dialog> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx index 11ca6bf4b62fb..2cace5adaa3b6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogKeepMounted.stories.tsx @@ -11,8 +11,6 @@ import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; import styles from './dialog.module.css'; import textareaStyles from '../Textarea/textarea.module.css'; -import storySource from './DialogKeepMounted.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * `unmountOnClose={false}` keeps the dialog in the DOM at all times. The native * `<dialog>` element manages its own visibility via `show()`/`close()`, so any @@ -53,5 +51,3 @@ export const KeepMounted = (): React.ReactNode => ( </DialogSurface> </Dialog> ); - -KeepMounted.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx index 20dc1399a7331..1969becdb6cc2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNested.stories.tsx @@ -9,8 +9,6 @@ import { } from '@fluentui/react-headless-components-preview/dialog'; import styles from './dialog.module.css'; -import storySource from './DialogNested.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * Dialogs can be nested. The inner `Dialog` detects that it is inside a parent * `DialogContext` and sets `isNestedDialog=true` automatically. Each dialog @@ -65,5 +63,3 @@ export const Nested = (): React.ReactNode => ( </DialogSurface> </Dialog> ); - -Nested.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx index a1142d371f4ff..1f6ad50d8a0a0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNoTrigger.stories.tsx @@ -9,8 +9,6 @@ import { import type { DialogOpenChangeData } from '@fluentui/react-headless-components-preview/dialog'; import styles from './dialog.module.css'; -import storySource from './DialogNoTrigger.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * `DialogTrigger` is optional. When the open state is managed entirely by the * parent (e.g. opened by a network event, a timeout, or a button outside the @@ -55,5 +53,3 @@ export const NoTrigger = (): React.ReactNode => { </div> ); }; - -NoTrigger.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx index d69bc71b17db0..7053930f16180 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogNonModal.stories.tsx @@ -11,8 +11,6 @@ import { Input } from '@fluentui/react-headless-components-preview/input'; import styles from './dialog.module.css'; import inputStyles from '../Input/input.module.css'; -import storySource from './DialogNonModal.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * A non-modal dialog does not dim the background and does not trap focus. * Users can still interact with the rest of the page while it is open. @@ -52,5 +50,3 @@ export const NonModal = (): React.ReactNode => ( /> </div> ); - -NonModal.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx index d84debcdeece4..dc64444bb1990 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Dialog/DialogWithCloseButton.stories.tsx @@ -12,8 +12,6 @@ import { CheckmarkRegular } from '@fluentui/react-icons'; import styles from './dialog.module.css'; import checkboxStyles from '../Checkbox/checkbox.module.css'; -import storySource from './DialogWithCloseButton.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; /** * Use `DialogTrigger` with `action="close"` to wire up a close button anywhere * inside the dialog. It defaults to `type="button"` and calls `onOpenChange` @@ -67,5 +65,3 @@ export const WithCloseButton = (): React.ReactNode => ( </DialogSurface> </Dialog> ); - -WithCloseButton.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx index b2d3718b62351..a69d0849c7bcb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Divider } from '@fluentui/react-headless-components-preview/divider'; import styles from './divider.module.css'; -import storySource from './DividerDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.column}> <p className={styles.section}>Content above</p> @@ -23,5 +21,3 @@ export const Default = (): React.ReactNode => ( <Divider className={styles.horizontal} /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx index db02a24d29c9e..b1c8277aa58cb 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Divider/DividerVertical.stories.tsx @@ -3,8 +3,6 @@ import { Divider } from '@fluentui/react-headless-components-preview/divider'; import { CircleRegular } from '@fluentui/react-icons'; import styles from './divider.module.css'; -import storySource from './DividerVertical.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( <div className={styles.verticalGroup}> <div className={styles.verticalCol}> @@ -51,5 +49,3 @@ export const Vertical = (): React.ReactNode => ( </div> </div> ); - -Vertical.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx index 25a9b6ee5ff69..d758122ba9caa 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Field/FieldDefault.stories.tsx @@ -5,8 +5,6 @@ import { ErrorCircleRegular } from '@fluentui/react-icons'; import fieldStyles from './field.module.css'; import inputStyles from '../Input/input.module.css'; -import storySource from './FieldDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={fieldStyles.demo}> <Field label={{ children: 'Email address', className: fieldStyles.label }} className={fieldStyles.field}> @@ -51,5 +49,3 @@ export const Default = (): React.ReactNode => ( </Field> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx index 51874980f90aa..885fc6d78dec1 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputBasic.stories.tsx @@ -3,8 +3,6 @@ import { Input } from '@fluentui/react-headless-components-preview/input'; import { SearchRegular } from '@fluentui/react-icons'; import styles from './input.module.css'; -import storySource from './InputBasic.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Basic = (): React.ReactNode => ( <div className={`${styles.column} ${styles.demo}`}> <Input className={styles.wrap} input={{ className: styles.input }} placeholder="Default input" /> @@ -28,5 +26,3 @@ export const Basic = (): React.ReactNode => ( <Input placeholder="Disabled" disabled className={styles.wrap} input={{ className: styles.input }} /> </div> ); - -Basic.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx index d565438f465d7..ef9f91cee2442 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Input/InputDefault.stories.tsx @@ -3,8 +3,6 @@ import { Input } from '@fluentui/react-headless-components-preview/input'; import { AddRegular, MicRegular, MicPulseRegular, SendRegular } from '@fluentui/react-icons'; import chatStyles from './chat-input.module.css'; -import storySource from './InputDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { const [value, setValue] = React.useState(''); const hasText = value.trim().length > 0; @@ -49,5 +47,3 @@ export const Default = (): React.ReactNode => { </div> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx index c8fd9e167daad..f69452e2b9203 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Link/LinkDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Link } from '@fluentui/react-headless-components-preview/link'; import styles from './link.module.css'; -import storySource from './LinkDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <Link href="#" className={styles.link}> @@ -27,5 +25,3 @@ export const Default = (): React.ReactNode => ( </Link> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx index 81a2221a7992d..269139012a5d0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarDefault.stories.tsx @@ -10,8 +10,6 @@ import { DismissRegular, InfoRegular } from '@fluentui/react-icons'; import linkStyles from '../Link/link.module.css'; import styles from './message-bar.module.css'; -import storySource from './MessageBarDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <MessageBar className={`${styles.bar} ${styles.info}`} @@ -34,5 +32,3 @@ export const Default = (): React.ReactNode => ( </MessageBarActions> </MessageBar> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx index f895b39b52e67..c998815da7a51 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/MessageBar/MessageBarIntent.stories.tsx @@ -5,8 +5,6 @@ import { CheckmarkCircleRegular, ErrorCircleRegular, InfoRegular, WarningRegular import linkStyles from '../Link/link.module.css'; import styles from './message-bar.module.css'; -import storySource from './MessageBarIntent.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const items = [ { intent: 'info' as const, @@ -55,7 +53,7 @@ export const Intent = (): React.ReactNode => ( </div> ); -Intent.parameters = withStorySource(storySource, { +Intent.parameters = { docs: { description: { story: [ @@ -65,4 +63,4 @@ Intent.parameters = withStorySource(storySource, { ].join('\n'), }, }, -}); +}; diff --git a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx index 32455f33138b1..e0ae25f8581c7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ProgressBar/ProgressBarDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { ProgressBar } from '@fluentui/react-headless-components-preview/progress-bar'; import styles from './progress-bar.module.css'; -import storySource from './ProgressBarDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <div className={styles.row}> @@ -30,5 +28,3 @@ export const Default = (): React.ReactNode => ( </div> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx index b1713108b8c3c..2761e9ec00295 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RadioGroup/RadioGroupDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { RadioGroup, Radio } from '@fluentui/react-headless-components-preview/radio-group'; import styles from './radio-group.module.css'; -import storySource from './RadioGroupDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const plans = [ { value: 'free', title: 'Free', subtitle: '$0 / month ยท Up to 3 projects' }, { value: 'standard', title: 'Standard', subtitle: '$12 / month ยท Up to 20 projects' }, @@ -32,5 +30,3 @@ export const Default = (): React.ReactNode => ( ))} </RadioGroup> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx index 8b4e08096e3e9..1f3226042b5e0 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Rating/RatingDefault.stories.tsx @@ -3,8 +3,6 @@ import { Rating, RatingItem } from '@fluentui/react-headless-components-preview/ import { StarFilled, StarRegular } from '@fluentui/react-icons'; import styles from './rating.module.css'; -import storySource from './RatingDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { const [value, setValue] = React.useState(3); const max = 5; @@ -29,5 +27,3 @@ export const Default = (): React.ReactNode => { </div> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx index ece02782bb394..b0b111eeb601b 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayCompact.stories.tsx @@ -3,8 +3,6 @@ import { RatingDisplay } from '@fluentui/react-headless-components-preview/ratin import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; import styles from './rating-display.module.css'; -import storySource from './RatingDisplayCompact.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon: React.FC = () => ( <> <StarFilled className={`${styles.icon} ${styles.iconFilled}`} /> @@ -22,5 +20,3 @@ export const Compact = (): React.ReactNode => ( valueText={{ className: styles.value }} /> ); - -Compact.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx index 150ee571744be..c9e482ccc4657 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/RatingDisplay/RatingDisplayDefault.stories.tsx @@ -3,8 +3,6 @@ import { RatingDisplay } from '@fluentui/react-headless-components-preview/ratin import { StarFilled, StarHalfFilled, StarRegular } from '@fluentui/react-icons'; import styles from './rating-display.module.css'; -import storySource from './RatingDisplayDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const RatingIcon: React.FC = () => ( <> <StarFilled className={`${styles.icon} ${styles.iconFilled}`} /> @@ -23,5 +21,3 @@ export const Default = (): React.ReactNode => ( countText={{ className: styles.count, children: '(248)' }} /> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx index 43c382a315b2b..095fbb27ed0f2 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SearchBox/SearchBoxDefault.stories.tsx @@ -4,8 +4,6 @@ import { SearchRegular } from '@fluentui/react-icons'; // SearchBox reuses the input CSS module per the story authoring guide. import styles from '../Input/input.module.css'; -import storySource from './SearchBoxDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <SearchBox @@ -19,5 +17,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx index 12759deb8f00b..41c6454fa7b32 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Select/SelectDefault.stories.tsx @@ -4,8 +4,6 @@ import { ChevronDownRegular } from '@fluentui/react-icons'; import fieldStyles from '../Field/field.module.css'; import styles from './select.module.css'; -import storySource from './SelectDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={`${fieldStyles.field} ${styles.demo}`}> <label className={fieldStyles.label} htmlFor="color-select"> @@ -24,5 +22,3 @@ export const Default = (): React.ReactNode => ( </Select> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx index 466f93c728a20..05211bb105cb9 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Skeleton/SkeletonDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Skeleton, SkeletonItem } from '@fluentui/react-headless-components-preview/skeleton'; import styles from './skeleton.module.css'; -import storySource from './SkeletonDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <Skeleton className={`${styles.card} ${styles.demo}`}> <div className={styles.row}> @@ -18,5 +16,3 @@ export const Default = (): React.ReactNode => ( <SkeletonItem className={`${styles.bar} ${styles.line80}`} /> </Skeleton> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx index fbf976b70477c..73e9e0c409647 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Slider/SliderDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Slider } from '@fluentui/react-headless-components-preview/slider'; import styles from './slider.module.css'; -import storySource from './SliderDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { const [value, setValue] = React.useState(42); return ( @@ -23,5 +21,3 @@ export const Default = (): React.ReactNode => { </div> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx index e483127a558c2..b296cdcc57f1c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/SpinButton/SpinButtonDefault.stories.tsx @@ -4,8 +4,6 @@ import { ChevronDownRegular, ChevronUpRegular } from '@fluentui/react-icons'; import fieldStyles from '../Field/field.module.css'; import styles from './spin-button.module.css'; -import storySource from './SpinButtonDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={`${fieldStyles.field} ${styles.demo}`}> <label className={fieldStyles.label} htmlFor="quantity-spinbutton"> @@ -29,5 +27,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx index 85add36656d25..fe96ef6a958c6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerDefault.stories.tsx @@ -3,8 +3,6 @@ import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; import styles from './spinner.module.css'; -import storySource from './SpinnerDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demoRow}> <Spinner @@ -30,5 +28,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx index d3580c7a25793..02ac4d69131f5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Spinner/SpinnerLabels.stories.tsx @@ -3,8 +3,6 @@ import { Spinner } from '@fluentui/react-headless-components-preview/spinner'; import { SpinnerIosRegular } from '@fluentui/react-icons'; import styles from './spinner.module.css'; -import storySource from './SpinnerLabels.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Labels = (): React.ReactNode => ( <div className={styles.demoCol}> <Spinner @@ -26,5 +24,3 @@ export const Labels = (): React.ReactNode => ( /> </div> ); - -Labels.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx index 2bb15b382dc6b..63186f01baaf9 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Switch/SwitchDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Switch } from '@fluentui/react-headless-components-preview/switch'; import styles from './switch.module.css'; -import storySource from './SwitchDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.list}> <Switch @@ -42,5 +40,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx index 72c0c95f23b91..40bbbe1346e4c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/TabList/TabListDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { TabList, Tab } from '@fluentui/react-headless-components-preview/tab-list'; import styles from './tab-list.module.css'; -import storySource from './TabListDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const tabs = [ { value: 'account', label: 'Account', content: 'Manage your account settings and preferences.' }, { @@ -38,5 +36,3 @@ export const Default = (): React.ReactNode => { </div> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx index 302623d11b957..fa339202b4cea 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Textarea/TextareaDefault.stories.tsx @@ -2,8 +2,6 @@ import * as React from 'react'; import { Textarea } from '@fluentui/react-headless-components-preview/textarea'; import styles from './textarea.module.css'; -import storySource from './TextareaDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => ( <div className={styles.demo}> <Textarea placeholder="Write your messageโ€ฆ" className={styles.wrap} textarea={{ className: styles.textarea }} /> @@ -20,5 +18,3 @@ export const Default = (): React.ReactNode => ( /> </div> ); - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx index 213d51cacca46..43b38899f9d20 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/ToggleButton/ToggleButtonDefault.stories.tsx @@ -3,8 +3,6 @@ import { ToggleButton } from '@fluentui/react-headless-components-preview/toggle import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; import styles from './toggle-button.module.css'; -import storySource from './ToggleButtonDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Default = (): React.ReactNode => { const [bold, setBold] = React.useState(false); const [italic, setItalic] = React.useState(false); @@ -45,5 +43,3 @@ export const Default = (): React.ReactNode => { </div> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx index 7dd450b19cb37..48fb999e04dca 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarDefault.stories.tsx @@ -20,8 +20,6 @@ import { } from '@fluentui/react-icons'; import styles from './toolbar.module.css'; -import storySource from './ToolbarDefault.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; const alignIcons = { left: TextAlignLeftRegular, center: TextAlignCenterRegular, @@ -82,5 +80,3 @@ export const Default = (): React.ReactNode => { </Toolbar> ); }; - -Default.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx index 4645760f8389b..8e02d0e4b2799 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarToggleButton.stories.tsx @@ -3,8 +3,6 @@ import { Toolbar, ToolbarGroup, ToolbarToggleButton } from '@fluentui/react-head import { TextBoldRegular, TextItalicRegular, TextUnderlineRegular } from '@fluentui/react-icons'; import styles from './toolbar.module.css'; -import storySource from './ToolbarToggleButton.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Toggle = (): React.ReactNode => ( <Toolbar className={styles.toolbar} aria-label="Text formatting toggles"> <ToolbarGroup className={styles.group} aria-label="Toggle states"> @@ -32,5 +30,3 @@ export const Toggle = (): React.ReactNode => ( </ToolbarGroup> </Toolbar> ); - -Toggle.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx index dc812b05098a7..bf2e91f0950d3 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Toolbar/ToolbarVertical.stories.tsx @@ -15,8 +15,6 @@ import { } from '@fluentui/react-icons'; import styles from './toolbar.module.css'; -import storySource from './ToolbarVertical.stories?raw'; -import { withStorySource } from '../_helpers/withStorySource'; export const Vertical = (): React.ReactNode => ( <Toolbar className={styles.toolbar} vertical aria-label="Text formatting"> <ToolbarButton className={styles.btn} aria-label="Cut" icon={<CutRegular />} /> @@ -32,5 +30,3 @@ export const Vertical = (): React.ReactNode => ( </ToolbarGroup> </Toolbar> ); - -Vertical.parameters = withStorySource(storySource); diff --git a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts b/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts deleted file mode 100644 index a11040ef26274..0000000000000 --- a/packages/react-components/react-headless-components-preview/stories/src/_helpers/withStorySource.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Storybook docs helper: attaches a story file's own source as - * `parameters.docs.source.originalSource` so the Show-code panel renders the - * actual TSX (not just the meta-level CSS appended by `withCssModuleSource`). - * - * Storybook's CSF source loader (`enrichCsf`) doesn't follow re-exports โ€” when - * `index.stories.tsx` only does `export { Default } from './XDefault.stories'`, - * the loader emits no `originalSource` for `Default`, and the meta-level - * `parameters.docs.source.transform` ends up running on an empty string. This - * helper closes that gap by importing the file's own contents via webpack's - * `?raw` query and pinning them to the story. - * - * Usage in an `<Component>Default.stories.tsx`: - * - * import storySource from './ButtonDefault.stories?raw'; - * import { withStorySource } from '../_helpers/withStorySource'; - * - * export const Default = (): React.ReactNode => โ€ฆ; - * Default.parameters = withStorySource(storySource); - * - * Pass `extra` to merge additional `parameters` (e.g. existing - * `parameters.docs.description`): - * - * Default.parameters = withStorySource(storySource, { - * docs: { description: { story: 'โ€ฆ' } }, - * }); - */ - -type Parameters = Record<string, unknown> & { - docs?: Record<string, unknown> & { - source?: Record<string, unknown>; - }; -}; - -/** - * Strip the internal `withStorySource` plumbing so the displayed code is - * exactly what a consumer would paste into their own project. We remove: - * - the `import storySource from './*.stories?raw';` line - * - the `import { withStorySource } from '...';` line - * - the trailing `<StoryName>.parameters = withStorySource(storySourceโ€ฆ);` - * - the now-blank line(s) those leave behind - */ -function cleanStorySource(source: string): string { - return ( - source - .replace(/^import\s+\w+\s+from\s+['"]\..*?\.stories\?raw['"];\s*\r?\n/m, '') - .replace(/^import\s*\{\s*withStorySource\s*\}\s*from\s+['"][^'"]+['"];\s*\r?\n/m, '') - .replace(/^\w+\.parameters\s*=\s*withStorySource\([\s\S]*?\);\s*\r?\n?/m, '') - // Rewrite the deeply-relative or sibling `*.module.css` paths to a - // colocated `./styles/<file>.module.css` so the snippet matches what - // the user actually gets in the Stackblitz sandbox (and is paste-ready - // into a project that follows the same colocation convention). - .replace( - /(['"])(?:\.{1,2}\/)+(?:[^'"\/]+\/)*([^'"\/]+\.module\.css)\1/g, - (_match, quote, basename) => `${quote}./styles/${basename}${quote}`, - ) - .replace(/\n{3,}/g, '\n\n') - .trimEnd() - .concat('\n') - ); -} - -export function withStorySource(storySource: string, extra: Parameters = {}): Parameters { - const cleaned = cleanStorySource(storySource); - const extraDocs = (extra.docs ?? {}) as Record<string, unknown> & { - source?: Record<string, unknown>; - }; - const params: Parameters = { - ...extra, - docs: { - ...extraDocs, - source: { - ...(extraDocs.source ?? {}), - // `code` takes precedence in Storybook's Source block โ€” we set it - // explicitly so the panel shows the full file (imports included) - // rather than the bare JSX body that the CSF source loader extracts - // from the right-hand side of the `export const Default = ...`. - code: cleaned, - originalSource: cleaned, - }, - }, - }; - - // The `@fluentui/babel-preset-storybook-full-source` plugin appends a - // `Default.parameters.fullSource = '<post-babel source>';` statement at the - // end of the compiled story file. That post-babel source has had every - // relative import stripped โ€” including our `import styles from - // colocated <Component>/<name>.module.css;` line โ€” which - // breaks the "Open in Stackblitz" sandbox (the example references `styles` - // but never imports it). - // - // Defining `fullSource` as a non-writable getter swallows the babel - // assignment so our cleaned source (with imports preserved) wins. - Object.defineProperty(params, 'fullSource', { - get: () => cleaned, - set: () => { - /* swallow the babel preset's overwrite */ - }, - configurable: true, - enumerable: true, - }); - - return params; -} From 0cc060f84b0a5b067c59335bd93fda1399d6e06b Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:27:35 +0200 Subject: [PATCH 21/25] fix(headless-stories): convert Card to CSS modules + design tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/src/Card/card.module.css | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css b/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css new file mode 100644 index 0000000000000..f3bc83841850b --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css @@ -0,0 +1,199 @@ +.card { + display: flex; + flex-direction: column; + gap: var(--space-3); + width: 320px; + padding: var(--space-3); + background: var(--bg-elev); + border: var(--stroke-thin) solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + position: relative; +} + +.card:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.cardSelectable { + cursor: pointer; + transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease; +} + +.cardSelectable:hover { + background: var(--bg-soft); +} + +.cardSelectable[data-selected] { + border-color: var(--accent); + box-shadow: 0 0 0 1px var(--accent); +} + +.cardSelectable[aria-disabled='true'] { + opacity: 0.5; + cursor: not-allowed; +} + +.cardSelectable[aria-disabled='true']:hover { + background: var(--bg-elev); +} + +.preview { + display: flex; + align-items: center; + justify-content: center; + background: var(--surface-muted); + border-radius: var(--radius-md); + overflow: hidden; + margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) 0; +} + +.previewImage { + display: block; + width: 100%; + height: 160px; + object-fit: cover; +} + +.header { + display: flex; + align-items: center; + gap: var(--space-3); +} + +.headerWithSelect { + padding-left: var(--space-6); +} + +.headerImage { + display: flex; + height: 40px; + width: 40px; + border-radius: var(--radius-md); + overflow: hidden; + background: var(--surface-muted); + flex-shrink: 0; +} + +.headerImg { + height: 100%; + width: 100%; + object-fit: cover; +} + +.headerTitle { + font-size: 13.5px; + font-weight: 600; + color: var(--text); + line-height: 1.2; +} + +.headerDescription { + font-size: 12px; + color: var(--text-muted); + line-height: 1.2; +} + +.headerAction { + margin-left: auto; + display: flex; + align-items: center; +} + +.iconButton { + display: inline-flex; + align-items: center; + justify-content: center; + height: 32px; + width: 32px; + border: 0; + border-radius: var(--radius-md); + background: transparent; + color: var(--text-muted); + cursor: pointer; +} + +.iconButton:hover { + background: var(--surface-muted); + color: var(--text); +} + +.iconButton:active { + background: var(--surface-sunken); +} + +.iconButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.body { + font-size: 13.5px; + color: var(--text-muted); + line-height: 1.4; +} + +.footer { + display: flex; + align-items: center; + gap: var(--space-2); + padding-top: var(--space-1); +} + +.footerButton { + display: inline-flex; + align-items: center; + gap: 6px; + height: 32px; + padding: 0 var(--space-3); + border: var(--stroke-thin) solid var(--border); + border-radius: var(--radius-md); + background: var(--bg-elev); + color: var(--text); + font-size: 13px; + cursor: pointer; +} + +.footerButton:hover { + background: var(--surface-muted); +} + +.footerButton:active { + background: var(--surface-sunken); +} + +.footerButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.checkbox { + position: absolute; + top: var(--space-3); + left: var(--space-3); + height: 16px; + width: 16px; + cursor: pointer; + accent-color: var(--accent); +} + +.checkbox:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.checkbox:disabled { + cursor: not-allowed; +} + +.list { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.status { + font-size: 12px; + color: var(--text-muted); +} From d74e592c379aaf42737a42d5455110bfcb5554e4 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 19:46:01 +0200 Subject: [PATCH 22/25] docs: refresh READMEs after PR #36073 review changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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> --- .../README.md | 3 +- .../stories/README.md | 56 +++++++++++-------- scripts/test-ssr/README.md | 9 +++ 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/packages/react-components/babel-preset-storybook-full-source/README.md b/packages/react-components/babel-preset-storybook-full-source/README.md index 5267a310b95d2..bcaaf621b1518 100644 --- a/packages/react-components/babel-preset-storybook-full-source/README.md +++ b/packages/react-components/babel-preset-storybook-full-source/README.md @@ -18,7 +18,8 @@ To use this Babel preset, add it to your Babel configuration: - **Removes Storybook specific assignments**: Avoids issues with undefined stories and unnecessary clutter. - **Collects and modifies import declarations**: Ensures valid single-file code examples. -- **Adds the `context.parameters.fullSource` property**: Includes the full source code of the story in Storybook. +- **Adds the `context.parameters.fullSource` property**: post-processed, single-file source for the "Open in Sandbox" flow. +- **Adds `context.parameters.docs.source.code` / `originalSource`**: the cleaned raw file contents (with colocated `*.module.css` paths rewritten to the `./styles/<basename>` layout used by the Stackblitz sandbox), so Storybook's "Show code" panel and any custom docs page render the file the author wrote without per-story plumbing. ## Note diff --git a/packages/react-components/react-headless-components-preview/stories/README.md b/packages/react-components/react-headless-components-preview/stories/README.md index 2a423ff61afab..d7ff50963ba90 100644 --- a/packages/react-components/react-headless-components-preview/stories/README.md +++ b/packages/react-components/react-headless-components-preview/stories/README.md @@ -42,7 +42,8 @@ For each new component: - `<Name>Description.md` โ€” short MDX-friendly markdown component description. - `<Name>Default.stories.tsx` (and any extra variant `*.stories.tsx`). - `index.stories.tsx` โ€” meta export with `title`, `component`, and the - `parameters.docs.source.transform` wiring (see ยง3). + `withCssModuleSource(...)` spread that registers the CSS Module source + (see ยง3). The component itself stays unstyled in `library/`. All visual concerns live in the CSS Module, and stories pull both together. @@ -71,9 +72,17 @@ Notes: ### 3 ยท Show code wiring (`index.stories.tsx`) -Every component's `index.stories.tsx` exposes its CSS Module source in the -Storybook "Show code" panel via `parameters.docs.source.transform`. The shared -helper at `stories/src/_helpers/withCssModuleSource.ts` does the stitching: +Two pieces feed the docsite's tabbed "Show code" panel: + +- **Story TSX** is injected automatically. `@fluentui/babel-preset-storybook-full-source` + reads each `*.stories.tsx` file at build time and writes + `Story.parameters.docs.source.code` / `originalSource` for every story + export. Story files don't need to import their own source via `?raw` or + call any helper โ€” just author the component as usual. +- **CSS Module source** is registered at the meta level via the + `withCssModuleSource` helper (`stories/src/_helpers/withCssModuleSource.ts`). + Spread its return into `parameters` so the docsite picks up the modules and + the Stackblitz sandbox bundles them under `src/styles/`: ```tsx import { MyComponent } from '@fluentui/react-headless-components-preview/my-component'; @@ -90,10 +99,8 @@ export default { parameters: { docs: { description: { component: descriptionMd }, - source: { - transform: withCssModuleSource({ name: 'my-component.module.css', source: myComponentCss }), - }, }, + ...withCssModuleSource({ name: 'my-component.module.css', source: myComponentCss }), }, }; ``` @@ -102,15 +109,16 @@ If a story uses multiple modules (e.g. `Field` stories nest `Input`), pass them all to `withCssModuleSource` in render order: ```ts -transform: withCssModuleSource( +...withCssModuleSource( { name: 'field.module.css', source: fieldCss }, { name: 'input.module.css', source: inputCss }, -); +), ``` The `?raw` suffix is webpack's "asset/source" โ€” Storybook 9's -`@storybook/builder-webpack5` ships the rule out of the box, and ambient TS -declarations live at `typings/static-assets/index.d.ts`. +`@storybook/builder-webpack5` ships the rule out of the box. The ambient TS +declaration for `*?raw` is scoped to this package via +`stories/src/_helpers/raw.d.ts`. ### 4 ยท Token tiers @@ -190,14 +198,18 @@ These are the things that took time to discover. Keep them in mind: ### 8 ยท Where things live -| Path | Purpose | -| ---------------------------------------------------- | ------------------------------------------------------------------- | -| `theme/tokens.css` | CSS custom properties, light + dark. Imported once in `preview.js`. | -| `stories/src/<Component>/<name>.module.css` | Per-component scoped styles. | -| `stories/src/<Name>/<Name>Default.stories.tsx` | Default story body using CSS Module classes. | -| `stories/src/<Name>/<Name>Description.md` | Component description shown in the Docs panel. | -| `stories/src/<Name>/index.stories.tsx` | Meta + `parameters.docs.source.transform` wiring. | -| `stories/src/_helpers/withCssModuleSource.ts` | Helper that appends raw CSS to the Show-code output. | -| `stories/.storybook/main.js` | Per-package storybook config (CSS Modules webpack rule lives here). | -| `apps/public-docsite-v9-headless/.storybook/main.js` | Deployed docsite config (mirror of the webpack rule). | -| `typings/static-assets/index.d.ts` | Ambient declarations for `*.module.css` and `*?raw` imports. | +| Path | Purpose | +| -------------------------------------------------------------------- | ------------------------------------------------------------------- | +| `theme/tokens.css` | CSS custom properties, light + dark. Imported once in `preview.js`. | +| `stories/src/<Component>/<name>.module.css` | Per-component scoped styles. | +| `stories/src/<Name>/<Name>Default.stories.tsx` | Default story body using CSS Module classes. | +| `stories/src/<Name>/<Name>Description.md` | Component description shown in the Docs panel. | +| `stories/src/<Name>/index.stories.tsx` | Meta + `parameters.docs.source.transform` wiring. | +| `stories/src/_helpers/withCssModuleSource.ts` | Registers CSS Module source for the docs page + Stackblitz sandbox. | +| `stories/src/_helpers/raw.d.ts` | Ambient `*?raw` declaration, scoped to this package. | +| `stories/.storybook/css-modules-webpack.js` | Source-of-truth webpack wiring for `*.module.css` + `?raw`. | +| `stories/.storybook/main.js` | Per-package storybook (consumes the shared webpack module). | +| `apps/public-docsite-v9-headless/.storybook/main.js` | Deployed docsite config (also consumes the shared webpack module). | +| `apps/public-docsite-v9-headless/.storybook/HeadlessDocsPage.tsx` | Custom docs page wired into the docsite's `parameters.docs.page`. | +| `apps/public-docsite-v9-headless/.storybook/HeadlessSourcePanel.tsx` | Tabbed "Show code" panel (TSX + each referenced CSS Module). | +| `typings/static-assets/index.d.ts` | Ambient `*.module.css` declaration (workspace-wide). | diff --git a/scripts/test-ssr/README.md b/scripts/test-ssr/README.md index babe4ac17e7c8..45372dea6b820 100644 --- a/scripts/test-ssr/README.md +++ b/scripts/test-ssr/README.md @@ -52,3 +52,12 @@ flowchart TB #### Debugging All assets are available in `node_modules/.cache/ssr-tests` folder. You can open `./node_modules/.cache/ssr-tests/index.html` in any browser and debug relevant issues. + +#### Webpack-only loaders supported during SSR + +`buildAssets.ts` registers two custom esbuild plugins (`src/utils/esbuild-plugin.ts`) so stories that work in webpack-driven Storybook also work in the SSR pipeline: + +- `?raw` queries โ€” strips the suffix and loads the underlying file as text. Mirrors webpack's `resourceQuery: /raw/` asset/source rule, including extension-less imports like `'./Foo.stories?raw'` resolving to `./Foo.stories.tsx`. +- `*.module.css` imports โ€” shimmed to a `Proxy` whose getter echoes the property name (so `styles.foo === 'foo'`). Sufficient for SSR snapshots without running the full CSS-Modules transform. + +If a story authors needs another webpack-only loader (e.g. `?inline`, custom asset modules), extend the plugins in `src/utils/esbuild-plugin.ts` rather than excluding the package from `testSSR`. From 78c296a4f736cdb7679383c67e22e773314b986f Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Thu, 30 Apr 2026 20:15:10 +0200 Subject: [PATCH 23/25] fix(headless-docsite): override SB chrome painting code panel black 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> --- .../.storybook/headless-docs-page.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css b/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css index 97b8c415cc5db..3e65ab063698b 100644 --- a/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css +++ b/apps/public-docsite-v9-headless/.storybook/headless-docs-page.css @@ -12,6 +12,22 @@ height: auto !important; } +/* + Reset Storybook's default `.docs-story + div > div:last-child` chrome โ€” + it paints a near-black background (selector specificity (0,2,2)) for the + legacy single-blob Source block. Our portaled HeadlessSourcePanel renders + its own light surface in the same DOM position, but emotion class names + alone can't beat that specificity. `!important` here is the simplest way + to win; alternatives like &&& chains or inline styles negate the value of + the panel's themed `styled` components. +*/ +.headless-source-portal > div { + background: var(--bg-elev) !important; + box-shadow: none !important; + border-radius: 0 !important; + right: auto !important; +} + /* Force the magenta accent for the "Show code" / "Open in Stackblitz" hover & focus underlines. Storybook's ActionBar paints the underline via an inset From 825da9362e2fa2d1510ed4c7471e248a73655577 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Mon, 4 May 2026 09:57:13 +0200 Subject: [PATCH 24/25] fix(headless-stories): clip Card children to its rounded corners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../stories/src/Card/card.module.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css b/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css index f3bc83841850b..1fb057600a10c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css +++ b/packages/react-components/react-headless-components-preview/stories/src/Card/card.module.css @@ -9,6 +9,13 @@ border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); position: relative; + /* + Clip children to the card's rounded shape โ€” without this, the preview's + negative margins push its square top corners past the rounded card border + on selected/disabled variants. Border + box-shadow render outside the + overflow box and stay intact. + */ + overflow: hidden; } .card:focus-visible { From 94b171609320f268242bb7c963c1a538e7ae7750 Mon Sep 17 00:00:00 2001 From: Tudor Popa <popatudor@microsoft.com> Date: Mon, 4 May 2026 10:40:48 +0200 Subject: [PATCH 25/25] fix(headless-stories): convert Drawer + Popover to CSS modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .../src/Drawer/DefaultDrawer.stories.tsx | 33 +-- .../src/Drawer/InlineDrawer.stories.tsx | 36 +-- .../stories/src/Drawer/drawer.module.css | 222 +++++++++++++++ .../stories/src/Drawer/index.stories.tsx | 3 + .../PopoverAnchorToCustomTarget.stories.tsx | 36 +-- .../src/Popover/PopoverControlled.stories.tsx | 17 +- .../Popover/PopoverCustomTrigger.stories.tsx | 14 +- .../src/Popover/PopoverDefault.stories.tsx | 14 +- .../PopoverInternalUpdateContent.stories.tsx | 23 +- .../src/Popover/PopoverNested.stories.tsx | 52 ++-- .../Popover/PopoverOpenOnContext.stories.tsx | 18 +- .../Popover/PopoverOpenOnHover.stories.tsx | 14 +- .../src/Popover/PopoverWithArrow.stories.tsx | 49 +--- .../Popover/PopoverWithoutTrigger.stories.tsx | 17 +- .../stories/src/Popover/index.stories.tsx | 3 + .../stories/src/Popover/popover.module.css | 267 ++++++++++++++++++ 16 files changed, 609 insertions(+), 209 deletions(-) create mode 100644 packages/react-components/react-headless-components-preview/stories/src/Drawer/drawer.module.css create mode 100644 packages/react-components/react-headless-components-preview/stories/src/Popover/popover.module.css diff --git a/packages/react-components/react-headless-components-preview/stories/src/Drawer/DefaultDrawer.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Drawer/DefaultDrawer.stories.tsx index 77d27bb37e05d..2945af70d276e 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Drawer/DefaultDrawer.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Drawer/DefaultDrawer.stories.tsx @@ -8,7 +8,7 @@ import { } from '@fluentui/react-headless-components-preview/drawer'; import { DismissRegular } from '@fluentui/react-icons'; -const buttonClassName = 'rounded bg-zinc-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-zinc-800'; +import styles from './drawer.module.css'; export const Default = (): React.ReactNode => { const [open, setOpen] = React.useState(false); @@ -18,40 +18,38 @@ export const Default = (): React.ReactNode => { return ( <> <Drawer - className={ - 'fixed inset-y-0 right-0 m-0 hidden min-h-screen w-80 max-w-[calc(100vw-32px)] translate-x-full flex-col border-0 border-l border-zinc-200 bg-white p-0 shadow-xl transition-transform [&[open]]:flex [&[open]]:translate-x-0 [&[open]]:starting:-translate-x-full backdrop:bg-black/40' - } + className={styles.drawerOverlay} open={open} onOpenChange={(_, data) => setOpen(data.open)} unmountOnClose={false} > - <DrawerHeader className="border-b border-zinc-200 px-4 py-3"> + <DrawerHeader className={styles.drawerHeader}> <DrawerHeaderTitle action={ - <button aria-label="Close drawer" className="rounded size-8 hover:bg-zinc-100" onClick={closeDrawer}> + <button aria-label="Close drawer" className={styles.closeButton} onClick={closeDrawer}> <DismissRegular /> </button> } - className="flex items-start justify-between gap-3" - heading={{ className: 'text-lg font-semibold text-zinc-900' }} + className={styles.drawerHeaderTitle} + heading={{ className: styles.drawerHeading }} > Overlay drawer </DrawerHeaderTitle> </DrawerHeader> - <DrawerBody className="flex-grow overflow-auto px-3 py-3 text-sm text-zinc-700"> + <DrawerBody className={styles.drawerBody}> <DrawerContent /> </DrawerBody> - <DrawerFooter className="flex justify-end gap-2 border-t border-zinc-200 px-4 py-3"> - <button className={buttonClassName} onClick={closeDrawer}> + <DrawerFooter className={styles.drawerFooter}> + <button className={styles.primaryButton} onClick={closeDrawer}> Close </button> </DrawerFooter> </Drawer> - <div className="p-4"> - <button className={buttonClassName} onClick={toggleDrawer}> + <div className={styles.trigger}> + <button className={styles.primaryButton} onClick={toggleDrawer}> Open drawer </button> </div> @@ -63,14 +61,9 @@ const DrawerContent = () => { const items = ['Dashboard', 'Activity', 'Projects', 'Calendar', 'Settings']; return ( - <nav aria-label="Example navigation" className="flex flex-col gap-1"> + <nav aria-label="Example navigation" className={styles.nav}> {items.map((item, index) => ( - <a - key={item} - aria-current={index === 0 ? 'page' : undefined} - href="#" - className="rounded px-3 py-2 font-medium no-underline aria-[current]:bg-zinc-200 aria-[current]:text-zinc-950 text-zinc-700 hover:bg-zinc-100 hover:text-zinc-950" - > + <a key={item} aria-current={index === 0 ? 'page' : undefined} href="#" className={styles.navLink}> {item} </a> ))} diff --git a/packages/react-components/react-headless-components-preview/stories/src/Drawer/InlineDrawer.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Drawer/InlineDrawer.stories.tsx index 1ee0568e0da80..ca4e81859348f 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Drawer/InlineDrawer.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Drawer/InlineDrawer.stories.tsx @@ -7,41 +7,36 @@ import { } from '@fluentui/react-headless-components-preview/drawer'; import { DismissRegular } from '@fluentui/react-icons'; +import styles from './drawer.module.css'; + export const Inline = (): React.ReactNode => { const [open, setOpen] = React.useState(false); const toggleDrawer = () => setOpen(value => !value); const closeDrawer = () => setOpen(false); return ( - <div className="flex h-[420px] overflow-hidden rounded border border-zinc-200 bg-white text-zinc-900"> - <Drawer - className={ - 'shrink-0 overflow-hidden border-r bg-zinc-50 transition-[width,opacity,transform,border-color] duration-200 ease-linear w-0 border-r-transparent opacity-0 data-[open]:w-72 data-[open]:border-r-zinc-200 data-[open]:opacity-100' - } - type="inline" - open={open} - unmountOnClose={false} - > - <DrawerHeader className="border-b border-zinc-200 px-4 py-3"> + <div className={styles.inlineFrame}> + <Drawer className={styles.drawerInline} type="inline" open={open} unmountOnClose={false}> + <DrawerHeader className={styles.drawerHeader}> <DrawerHeaderTitle action={ - <button aria-label="Close drawer" className="rounded size-8 hover:bg-zinc-100" onClick={closeDrawer}> + <button aria-label="Close drawer" className={styles.closeButton} onClick={closeDrawer}> <DismissRegular /> </button> } - className="flex items-center justify-between gap-3" - heading={{ className: 'text-lg font-semibold' }} + className={`${styles.drawerHeaderTitle} ${styles.drawerHeaderTitleInline}`} + heading={{ className: styles.drawerHeadingInline }} > Inline drawer </DrawerHeaderTitle> </DrawerHeader> - <DrawerBody className="px-3 py-3 text-sm text-zinc-700"> + <DrawerBody className={styles.drawerBody}> <DrawerContent /> </DrawerBody> </Drawer> - <main className="flex flex-1 items-start flex-col gap-3 p-4"> - <button className="rounded border border-zinc-300 px-3 py-1.5 text-sm hover:bg-zinc-100" onClick={toggleDrawer}> + <main className={styles.inlineMain}> + <button className={styles.secondaryButton} onClick={toggleDrawer}> {open ? 'Hide inline drawer' : 'Show inline drawer'} </button> </main> @@ -53,14 +48,9 @@ const DrawerContent = () => { const items = ['Dashboard', 'Activity', 'Projects', 'Calendar', 'Settings']; return ( - <nav aria-label="Example navigation" className="flex flex-col gap-1"> + <nav aria-label="Example navigation" className={styles.nav}> {items.map((item, index) => ( - <a - key={item} - aria-current={index === 0 ? 'page' : undefined} - href="#" - className="rounded px-3 py-2 font-medium no-underline aria-[current]:bg-zinc-200 aria-[current]:text-zinc-950 text-zinc-700 hover:bg-zinc-100 hover:text-zinc-950" - > + <a key={item} aria-current={index === 0 ? 'page' : undefined} href="#" className={styles.navLink}> {item} </a> ))} diff --git a/packages/react-components/react-headless-components-preview/stories/src/Drawer/drawer.module.css b/packages/react-components/react-headless-components-preview/stories/src/Drawer/drawer.module.css new file mode 100644 index 0000000000000..f81fb3fcfe02d --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/Drawer/drawer.module.css @@ -0,0 +1,222 @@ +/* + Overlay variant: position fixed to the right edge, slides in via translateX + on the [open] attribute. Uses a `::backdrop` (per the native `<dialog>` + Drawer renders into) for the dim layer. +*/ +.drawerOverlay { + position: fixed; + top: 0; + right: 0; + bottom: 0; + /* `left: auto` overrides the user-agent `inset-inline-start: 0` on + `<dialog>` so the drawer pins to the right edge instead of centering. */ + left: auto; + margin: 0; + display: none; + min-height: 100vh; + width: 320px; + max-width: calc(100vw - 32px); + flex-direction: column; + border: 0; + border-left: var(--stroke-thin) solid var(--border); + background: var(--bg-elev); + padding: 0; + box-shadow: var(--shadow-4); + transform: translateX(100%); + transition: transform 200ms ease-in-out; +} + +.drawerOverlay[open] { + display: flex; + transform: translateX(0); +} + +@starting-style { + .drawerOverlay[open] { + transform: translateX(100%); + } +} + +.drawerOverlay::backdrop { + background: rgba(0, 0, 0, 0.4); +} + +/* + Inline variant: lives inside a container, animates width + border-color + + opacity on the [data-open] attribute. +*/ +.drawerInline { + flex-shrink: 0; + overflow: hidden; + background: var(--bg-soft); + border-right: var(--stroke-thin) solid transparent; + width: 0; + opacity: 0; + transition: width 200ms linear, opacity 200ms linear, border-color 200ms linear; +} + +.drawerInline[data-open] { + width: 288px; + opacity: 1; + border-right-color: var(--border); +} + +.drawerHeader { + border-bottom: var(--stroke-thin) solid var(--border); + padding: var(--space-3) var(--space-4); +} + +.drawerHeaderTitle { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--space-3); +} + +.drawerHeaderTitleInline { + align-items: center; +} + +.drawerHeading { + font-size: 17px; + font-weight: 600; + color: var(--text); + line-height: 1.3; +} + +.drawerHeadingInline { + font-size: 17px; + font-weight: 600; + line-height: 1.3; +} + +.drawerBody { + flex-grow: 1; + overflow: auto; + padding: var(--space-3); + font-size: 13.5px; + color: var(--text-muted); +} + +.drawerFooter { + display: flex; + justify-content: flex-end; + gap: var(--space-2); + border-top: var(--stroke-thin) solid var(--border); + padding: var(--space-3) var(--space-4); +} + +.closeButton { + display: inline-flex; + align-items: center; + justify-content: center; + height: 32px; + width: 32px; + border: 0; + border-radius: var(--radius-md); + background: transparent; + color: var(--text-muted); + cursor: pointer; +} + +.closeButton:hover { + background: var(--surface-muted); + color: var(--text); +} + +.closeButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.primaryButton { + display: inline-flex; + align-items: center; + height: 32px; + padding: 0 var(--space-3); + border: 0; + border-radius: var(--radius-md); + background: var(--text); + color: var(--text-on-accent); + font-size: 13.5px; + font-weight: 500; + cursor: pointer; +} + +.primaryButton:hover { + background: var(--text-muted); +} + +.primaryButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.secondaryButton { + display: inline-flex; + align-items: center; + height: 32px; + padding: 0 var(--space-3); + border: var(--stroke-thin) solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-elev); + color: var(--text); + font-size: 13.5px; + cursor: pointer; +} + +.secondaryButton:hover { + background: var(--surface-muted); +} + +.secondaryButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.trigger { + padding: var(--space-4); +} + +.inlineFrame { + display: flex; + height: 420px; + overflow: hidden; + border: var(--stroke-thin) solid var(--border); + border-radius: var(--radius-md); + background: var(--bg-elev); + color: var(--text); +} + +.inlineMain { + display: flex; + flex: 1; + align-items: flex-start; + flex-direction: column; + gap: var(--space-3); + padding: var(--space-4); +} + +.nav { + display: flex; + flex-direction: column; + gap: var(--space-1); +} + +.navLink { + border-radius: var(--radius-md); + padding: var(--space-2) var(--space-3); + font-weight: 500; + text-decoration: none; + color: var(--text-muted); +} + +.navLink:hover { + background: var(--surface-muted); + color: var(--text); +} + +.navLink[aria-current] { + background: var(--surface-sunken); + color: var(--text); +} diff --git a/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx index 056c77c49af09..a7b3d1470856a 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Drawer/index.stories.tsx @@ -10,6 +10,8 @@ import { } from '@fluentui/react-headless-components-preview/drawer'; import descriptionMd from './DrawerDescription.md'; +import drawerCss from './drawer.module.css?raw'; +import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './DefaultDrawer.stories'; export { Inline } from './InlineDrawer.stories'; @@ -32,5 +34,6 @@ export default { component: descriptionMd, }, }, + ...withCssModuleSource({ name: 'drawer.module.css', source: drawerCss }), }, }; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverAnchorToCustomTarget.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverAnchorToCustomTarget.stories.tsx index 25abec496644b..673b8b0c89731 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverAnchorToCustomTarget.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverAnchorToCustomTarget.stories.tsx @@ -1,42 +1,32 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - outer: 'p-16 min-h-[320px]', - container: 'flex items-start gap-10', - column: 'flex flex-col items-start gap-2', - label: 'text-xs font-semibold text-gray-500 uppercase tracking-wide', - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - target: - 'px-4 py-2 rounded-md bg-purple-600 text-white font-medium hover:bg-purple-700 focus-visible:outline-2 focus-visible:outline-purple-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs flex flex-col gap-2', -}; +import styles from './popover.module.css'; export const AnchorToCustomTarget = (): React.ReactNode => { const [target, setTarget] = React.useState<HTMLElement | null>(null); return ( - <div className={classes.outer}> - <div className={classes.container}> - <div className={classes.column}> - <span className={classes.label}>Custom anchor (target)</span> - <button ref={setTarget} className={classes.target}> + <div className={styles.outerPad}> + <div className={styles.cluster}> + <div className={styles.column}> + <span className={styles.fieldLabel}>Custom anchor (target)</span> + <button ref={setTarget} className={`${styles.trigger} ${styles.triggerSecondary}`}> Anchor </button> </div> - <div className={classes.column}> - <span className={classes.label}>Popover trigger (unrelated)</span> + <div className={styles.column}> + <span className={styles.fieldLabel}>Popover trigger (unrelated)</span> <Popover positioning={{ target, position: 'below', offset: 4 }}> <PopoverTrigger> - <button className={classes.trigger}>Open popover</button> + <button className={styles.trigger}>Open popover</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 m-0">Popover content</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={`${styles.surface} ${styles.surfaceColumn}`}> + <h3 className={styles.headingFlush}>Popover content</h3> + <p className={styles.body}> Clicking <em>Open popover</em> toggles this surface, but <code>positioning.target</code> makes it anchor - to the purple <em>Anchor</em> button instead of the trigger. It appears to the right of the anchor + to the magenta <em>Anchor</em> button instead of the trigger. It appears to the right of the anchor regardless of where the trigger sits. </p> </PopoverSurface> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverControlled.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverControlled.stories.tsx index 65d8fac2b65ee..7a143aac05846 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverControlled.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverControlled.stories.tsx @@ -1,28 +1,23 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs', - checkbox: 'flex items-center gap-2 mb-4 text-sm text-gray-700', -}; +import styles from './popover.module.css'; export const Controlled = (): React.ReactNode => { const [open, setOpen] = React.useState(false); return ( - <div className="flex flex-col gap-4"> - <label className={classes.checkbox}> + <div className={styles.columnSpacious}> + <label className={styles.checkbox}> <input type="checkbox" checked={open} onChange={e => setOpen(e.target.checked)} /> Popover open </label> <Popover open={open} onOpenChange={(_e, data) => setOpen(data.open)}> <PopoverTrigger> - <button className={classes.trigger}>Controlled popover</button> + <button className={styles.trigger}>Controlled popover</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <p className="text-sm text-gray-600"> + <PopoverSurface className={styles.surface}> + <p className={styles.body}> This popover is controlled externally. Toggle the checkbox above or click the trigger to open and close it. </p> </PopoverSurface> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverCustomTrigger.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverCustomTrigger.stories.tsx index f3707950e20ac..37641b38e1ded 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverCustomTrigger.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverCustomTrigger.stories.tsx @@ -1,16 +1,12 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs', -}; +import styles from './popover.module.css'; type CustomTriggerProps = React.ButtonHTMLAttributes<HTMLButtonElement>; const CustomTriggerButton = React.forwardRef<HTMLButtonElement, CustomTriggerProps>((props, ref) => ( - <button ref={ref} {...props} className={classes.trigger}> + <button ref={ref} {...props} className={styles.trigger}> Custom trigger </button> )); @@ -20,9 +16,9 @@ export const CustomTrigger = (): React.ReactNode => ( <PopoverTrigger> <CustomTriggerButton /> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 mb-1">Custom trigger</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={styles.surface}> + <h3 className={styles.heading}>Custom trigger</h3> + <p className={styles.body}> Native elements and Fluent components have first-class support as children of <code>PopoverTrigger</code>. To use your own component, forward its ref with <code>React.forwardRef</code> so the popover can wire up the trigger ref and aria attributes. diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverDefault.stories.tsx index 1ed9aa40c50da..919836daf1509 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverDefault.stories.tsx @@ -1,20 +1,16 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs', -}; +import styles from './popover.module.css'; export const Default = (): React.ReactNode => ( <Popover> <PopoverTrigger> - <button className={classes.trigger}>Show popover</button> + <button className={styles.trigger}>Show popover</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 mb-1">Popover title</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={styles.surface}> + <h3 className={styles.heading}>Popover title</h3> + <p className={styles.body}> This is the content of the popover. Click the trigger again or press Escape to close. </p> </PopoverSurface> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverInternalUpdateContent.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverInternalUpdateContent.stories.tsx index afb359fd4ec69..1154674b290dd 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverInternalUpdateContent.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverInternalUpdateContent.stories.tsx @@ -1,14 +1,7 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 w-80', - action: - 'px-3 py-1.5 rounded-md bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 cursor-pointer border-none', - link: 'text-blue-600 hover:text-blue-700 underline', -}; +import styles from './popover.module.css'; export const InternalUpdateContent = (): React.ReactNode => { const [revealed, setRevealed] = React.useState(false); @@ -23,25 +16,25 @@ export const InternalUpdateContent = (): React.ReactNode => { return ( <Popover onOpenChange={(_, data) => !data.open && setRevealed(false)}> <PopoverTrigger> - <button className={classes.trigger}>Popover trigger</button> + <button className={styles.trigger}>Popover trigger</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 mb-2">First panel</h3> - <p className="text-sm text-gray-600 mb-3"> + <PopoverSurface className={`${styles.surface} ${styles.surfaceWide}`}> + <h3 className={styles.heading}>First panel</h3> + <p className={`${styles.body} ${styles.bodySpaced}`}> Popover content can change while the popover is open. When new focusable content is revealed, move focus to it so keyboard users can continue interacting. </p> {revealed ? ( - <div className="text-sm text-gray-700"> + <div className={styles.body}> Revealed content with{' '} - <a ref={linkRef} href="#" className={classes.link}> + <a ref={linkRef} href="#" className={styles.link}> a focusable link </a> . </div> ) : ( - <button className={classes.action} onClick={() => setRevealed(true)}> + <button className={styles.actionButton} onClick={() => setRevealed(true)}> Reveal more </button> )} diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverNested.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverNested.stories.tsx index 083360f4ed231..c8e3c1d9ec212 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverNested.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverNested.stories.tsx @@ -3,31 +3,17 @@ import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headles import type { JSXElement } from '@fluentui/react-components'; import descriptionMd from './PopoverNestedDescription.md'; - -const classes = { - rootTrigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - nestedTrigger: - 'px-3 py-1.5 rounded-md bg-indigo-600 text-white text-sm font-medium hover:bg-indigo-700 data-[open]:bg-indigo-700 focus-visible:outline-2 focus-visible:outline-indigo-500 focus-visible:outline-offset-2 cursor-pointer border-none', - deepTrigger: - 'px-3 py-1.5 rounded-md bg-purple-600 text-white text-sm font-medium hover:bg-purple-700 data-[open]:bg-purple-700 focus-visible:outline-2 focus-visible:outline-purple-500 focus-visible:outline-offset-2 cursor-pointer border-none', - actionButton: - 'px-3 py-1.5 rounded-md bg-gray-200 text-gray-900 text-sm font-medium hover:bg-gray-300 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs flex flex-col gap-3', - heading: 'text-sm font-semibold text-gray-900 m-0', - body: 'text-sm text-gray-600', - row: 'flex flex-wrap items-center gap-2', -}; +import styles from './popover.module.css'; const SecondNestedPopover = (): JSXElement => ( <Popover> <PopoverTrigger> - <button className={classes.deepTrigger}>Second nested trigger</button> + <button className={`${styles.trigger} ${styles.triggerSmall}`}>Second nested trigger</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className={classes.heading}>Popover content</h3> - <div className={classes.body}>This is some popover content.</div> - <button className={classes.actionButton}>Second nested button</button> + <PopoverSurface className={`${styles.surface} ${styles.surfaceColumnLg}`}> + <h3 className={styles.headingFlush}>Popover content</h3> + <div className={styles.body}>This is some popover content.</div> + <button className={styles.actionButton}>Second nested button</button> </PopoverSurface> </Popover> ); @@ -35,13 +21,15 @@ const SecondNestedPopover = (): JSXElement => ( const FirstNestedPopover = (): JSXElement => ( <Popover> <PopoverTrigger> - <button className={classes.nestedTrigger}>First nested trigger</button> + <button className={`${styles.trigger} ${styles.triggerSecondary} ${styles.triggerSmall}`}> + First nested trigger + </button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className={classes.heading}>Popover content</h3> - <div className={classes.body}>This is some popover content.</div> - <button className={classes.actionButton}>First nested button</button> - <div className={classes.row}> + <PopoverSurface className={`${styles.surface} ${styles.surfaceColumnLg}`}> + <h3 className={styles.headingFlush}>Popover content</h3> + <div className={styles.body}>This is some popover content.</div> + <button className={styles.actionButton}>First nested button</button> + <div className={styles.row}> <SecondNestedPopover /> </div> </PopoverSurface> @@ -51,13 +39,13 @@ const FirstNestedPopover = (): JSXElement => ( export const Nested = (): React.ReactNode => ( <Popover> <PopoverTrigger> - <button className={classes.rootTrigger}>Root trigger</button> + <button className={styles.trigger}>Root trigger</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className={classes.heading}>Popover content</h3> - <div className={classes.body}>This is some popover content.</div> - <div className={classes.row}> - <button className={classes.actionButton}>Root button</button> + <PopoverSurface className={`${styles.surface} ${styles.surfaceColumnLg}`}> + <h3 className={styles.headingFlush}>Popover content</h3> + <div className={styles.body}>This is some popover content.</div> + <div className={styles.row}> + <button className={styles.actionButton}>Root button</button> <FirstNestedPopover /> </div> </PopoverSurface> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnContext.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnContext.stories.tsx index e42b0567b4d8b..28a6132e587d4 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnContext.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnContext.stories.tsx @@ -1,23 +1,17 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-6 py-4 rounded-md bg-gray-100 text-gray-700 font-medium border border-dashed border-gray-400 cursor-context-menu focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 py-2 min-w-[160px]', - menuItem: - 'block w-full px-4 py-1.5 text-sm text-gray-700 text-left hover:bg-gray-100 cursor-pointer border-none bg-transparent', -}; +import styles from './popover.module.css'; export const OpenOnContext = (): React.ReactNode => ( <Popover openOnContext> <PopoverTrigger> - <div className={classes.trigger}>Right-click this area</div> + <div className={styles.contextTrigger}>Right-click this area</div> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <button className={classes.menuItem}>Cut</button> - <button className={classes.menuItem}>Copy</button> - <button className={classes.menuItem}>Paste</button> + <PopoverSurface className={`${styles.surface} ${styles.surfaceMenu}`}> + <button className={styles.menuItem}>Cut</button> + <button className={styles.menuItem}>Copy</button> + <button className={styles.menuItem}>Paste</button> </PopoverSurface> </Popover> ); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnHover.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnHover.stories.tsx index e25fe7a5b5df9..ae58e1fcad8e5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnHover.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverOpenOnHover.stories.tsx @@ -1,20 +1,16 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs', -}; +import styles from './popover.module.css'; export const OpenOnHover = (): React.ReactNode => ( <Popover openOnHover> <PopoverTrigger> - <button className={classes.trigger}>Hover me</button> + <button className={styles.trigger}>Hover me</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 mb-1">Hover popover</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={styles.surface}> + <h3 className={styles.heading}>Hover popover</h3> + <p className={styles.body}> This popover opens when you hover over the trigger and closes when the mouse leaves both the trigger and the surface. </p> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithArrow.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithArrow.stories.tsx index 9507b31cd9d93..1957896890df7 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithArrow.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithArrow.stories.tsx @@ -1,41 +1,17 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - wrapper: 'flex flex-col items-start gap-4 p-16', - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: [ - // Base surface look - 'bg-white rounded-lg p-4 min-w-[240px] max-w-xs overflow-visible', - '[filter:drop-shadow(0_0_1px_rgba(0,0,0,0.12))_drop-shadow(0_4px_8px_rgba(0,0,0,0.14))]', - // Arrow base (the rotated square rendered by withArrow) - '[&_[data-arrow]]:absolute [&_[data-arrow]]:w-3 [&_[data-arrow]]:h-3 [&_[data-arrow]]:bg-white [&_[data-arrow]]:rotate-45', - // Main-axis offset โ€” arrow protrudes from the side that faces the trigger - "[&[data-placement^='above']_[data-arrow]]:-bottom-1.5", - "[&[data-placement^='below']_[data-arrow]]:-top-1.5", - "[&[data-placement^='before']_[data-arrow]]:-right-1.5", - "[&[data-placement^='after']_[data-arrow]]:-left-1.5", - // Cross-axis centering for the plain (center-aligned) placements - "[&[data-placement='above']_[data-arrow]]:inset-x-0 [&[data-placement='above']_[data-arrow]]:mx-auto", - "[&[data-placement='below']_[data-arrow]]:inset-x-0 [&[data-placement='below']_[data-arrow]]:mx-auto", - "[&[data-placement='before']_[data-arrow]]:inset-y-0 [&[data-placement='before']_[data-arrow]]:my-auto", - "[&[data-placement='after']_[data-arrow]]:inset-y-0 [&[data-placement='after']_[data-arrow]]:my-auto", - // Start/end-aligned placements โ€” arrow pinned via logical inset, padding from --arrow-padding - "[&[data-placement$='-start']_[data-arrow]]:start-[var(--arrow-padding,12px)]", - "[&[data-placement$='-end']_[data-arrow]]:end-[var(--arrow-padding,12px)]", - ].join(' '), -}; +import styles from './popover.module.css'; export const WithArrow = (): React.ReactNode => ( - <div className={classes.wrapper}> + <div className={styles.columnSpacious}> <Popover withArrow positioning={{ position: 'below', offset: 10 }}> <PopoverTrigger> - <button className={classes.trigger}>Center-aligned</button> + <button className={styles.trigger}>Center-aligned</button> </PopoverTrigger> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 mb-1">Arrow popover</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={`${styles.surface} ${styles.surfaceWithArrow}`}> + <h3 className={styles.heading}>Arrow popover</h3> + <p className={styles.body}> Arrow orientation follows the <code>data-placement</code> attribute, which <code>usePositioning</code> keeps in sync with the actual placement as you scroll or resize. </p> @@ -44,12 +20,15 @@ export const WithArrow = (): React.ReactNode => ( <Popover withArrow positioning={{ position: 'below', align: 'start', offset: 10 }}> <PopoverTrigger> - <button className={classes.trigger}>Start-aligned (--arrow-padding: 16px)</button> + <button className={styles.trigger}>Start-aligned (--arrow-padding: 16px)</button> </PopoverTrigger> - <PopoverSurface className={classes.surface} style={{ '--arrow-padding': '16px' } as React.CSSProperties}> - <h3 className="text-sm font-semibold text-gray-900 mb-1">Arrow padded from corner</h3> - <p className="text-sm text-gray-600"> - Arrow positioning is fully CSS-owned. For start/end alignments, the Tailwind variant reads{' '} + <PopoverSurface + className={`${styles.surface} ${styles.surfaceWithArrow}`} + style={{ '--arrow-padding': '16px' } as React.CSSProperties} + > + <h3 className={styles.heading}>Arrow padded from corner</h3> + <p className={styles.body}> + Arrow positioning is fully CSS-owned. For start/end alignments, the rule reads{' '} <code>var(--arrow-padding, 12px)</code>; this surface overrides the fallback by setting{' '} <code>--arrow-padding: 16px</code> in its inline <code>style</code>. </p> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithoutTrigger.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithoutTrigger.stories.tsx index 7305eefbdb9e3..e22d6889691cd 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithoutTrigger.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/PopoverWithoutTrigger.stories.tsx @@ -1,26 +1,21 @@ import * as React from 'react'; import { Popover, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - container: 'flex flex-col items-start gap-4 p-4', - trigger: - 'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[240px] max-w-xs flex flex-col gap-2', -}; +import styles from './popover.module.css'; export const WithoutTrigger = (): React.ReactNode => { const [open, setOpen] = React.useState(false); const [buttonEl, setButtonEl] = React.useState<HTMLButtonElement | null>(null); return ( - <div className={classes.container}> - <button ref={setButtonEl} className={classes.trigger} onClick={() => setOpen(value => !value)}> + <div className={styles.columnSpacious}> + <button ref={setButtonEl} className={styles.trigger} onClick={() => setOpen(value => !value)}> Toggle popover </button> <Popover open={open} onOpenChange={(_e, data) => setOpen(data.open)} positioning={{ target: buttonEl }}> - <PopoverSurface className={classes.surface}> - <h3 className="text-sm font-semibold text-gray-900 m-0">Popover content</h3> - <p className="text-sm text-gray-600"> + <PopoverSurface className={`${styles.surface} ${styles.surfaceColumn}`}> + <h3 className={styles.headingFlush}>Popover content</h3> + <p className={styles.body}> This popover has no <code>PopoverTrigger</code>. The surface is controlled externally and anchored to the button via the <code>positioning.target</code> prop. </p> diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx index 485c7f319013b..feec05ec7e850 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/index.stories.tsx @@ -2,6 +2,8 @@ import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headles import descriptionMd from './PopoverDescription.md'; import bestPracticesMd from './PopoverBestPractices.md'; +import popoverCss from './popover.module.css?raw'; +import { withCssModuleSource } from '../_helpers/withCssModuleSource'; export { Default } from './PopoverDefault.stories'; export { WithArrow } from './PopoverWithArrow.stories'; @@ -24,5 +26,6 @@ export default { component: [descriptionMd, bestPracticesMd].join('\n'), }, }, + ...withCssModuleSource({ name: 'popover.module.css', source: popoverCss }), }, }; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Popover/popover.module.css b/packages/react-components/react-headless-components-preview/stories/src/Popover/popover.module.css new file mode 100644 index 0000000000000..06874d55dacbe --- /dev/null +++ b/packages/react-components/react-headless-components-preview/stories/src/Popover/popover.module.css @@ -0,0 +1,267 @@ +.trigger { + display: inline-flex; + align-items: center; + height: 36px; + padding: 0 var(--space-4); + border: 0; + border-radius: var(--radius-md); + background: var(--text); + color: var(--text-on-accent); + font-size: 13.5px; + font-weight: 500; + cursor: pointer; +} + +.trigger:hover, +.trigger[data-open] { + background: var(--text-muted); +} + +.trigger:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.triggerSecondary { + background: var(--accent); +} + +.triggerSecondary:hover, +.triggerSecondary[data-open] { + background: var(--accent-strong); +} + +.triggerSecondary:focus-visible { + outline-color: var(--accent); +} + +.triggerSmall { + height: 28px; + padding: 0 var(--space-3); + font-size: 12.5px; +} + +.contextTrigger { + display: inline-block; + padding: var(--space-4) var(--space-6); + border-radius: var(--radius-md); + background: var(--surface-muted); + color: var(--text-muted); + font-weight: 500; + border: var(--stroke-thin) dashed var(--border-stronger); + cursor: context-menu; + user-select: none; +} + +.contextTrigger:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.surface { + background: var(--bg-elev); + border-radius: var(--radius-md); + border: var(--stroke-thin) solid var(--border); + box-shadow: var(--shadow-3); + padding: var(--space-4); + min-width: 240px; + max-width: 320px; +} + +.surfaceColumn { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.surfaceColumnLg { + display: flex; + flex-direction: column; + gap: var(--space-3); +} + +.surfaceWide { + width: 320px; +} + +.surfaceMenu { + padding: var(--space-2) 0; + min-width: 160px; +} + +.heading { + font-size: 13.5px; + font-weight: 600; + color: var(--text); + margin: 0 0 var(--space-1); +} + +.headingFlush { + margin: 0; +} + +.body { + font-size: 13px; + color: var(--text-muted); + line-height: 1.45; +} + +.bodySpaced { + margin: 0 0 var(--space-3); +} + +.actionButton { + display: inline-flex; + align-items: center; + height: 28px; + padding: 0 var(--space-3); + border: var(--stroke-thin) solid var(--border-strong); + border-radius: var(--radius-md); + background: var(--bg-elev); + color: var(--text); + font-size: 12.5px; + font-weight: 500; + cursor: pointer; +} + +.actionButton:hover { + background: var(--surface-muted); +} + +.actionButton:focus-visible { + outline: var(--stroke-thick) solid var(--text); + outline-offset: 2px; +} + +.menuItem { + display: block; + width: 100%; + padding: var(--space-1) var(--space-4); + border: 0; + background: transparent; + color: var(--text); + font-size: 13px; + text-align: left; + cursor: pointer; +} + +.menuItem:hover { + background: var(--surface-muted); +} + +.link { + color: var(--accent); + text-decoration: underline; +} + +.link:hover { + color: var(--accent-strong); +} + +.checkbox { + display: flex; + align-items: center; + gap: var(--space-2); + font-size: 13.5px; + color: var(--text-muted); +} + +.fieldLabel { + font-size: 11px; + font-weight: 600; + color: var(--text-soft); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.column { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-2); +} + +.columnSpacious { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-4); +} + +.row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-2); +} + +.cluster { + display: flex; + align-items: flex-start; + gap: var(--space-10); +} + +.outerPad { + padding: var(--space-12); + min-height: 320px; +} + +.localPad { + padding: var(--space-4); +} + +/* + Arrow rendering for PopoverWithArrow. The headless Popover paints a 12ร—12 + rotated square that tracks the surface's data-placement attribute via the + positioning hook. +*/ +.surfaceWithArrow { + overflow: visible; + filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.12)) drop-shadow(0 4px 8px rgba(0, 0, 0, 0.14)); + border: 0; + box-shadow: none; +} + +.surfaceWithArrow [data-arrow] { + position: absolute; + width: 12px; + height: 12px; + background: var(--bg-elev); + transform: rotate(45deg); +} + +.surfaceWithArrow[data-placement^='above'] [data-arrow] { + bottom: -6px; +} + +.surfaceWithArrow[data-placement^='below'] [data-arrow] { + top: -6px; +} + +.surfaceWithArrow[data-placement^='before'] [data-arrow] { + right: -6px; +} + +.surfaceWithArrow[data-placement^='after'] [data-arrow] { + left: -6px; +} + +.surfaceWithArrow[data-placement='above'] [data-arrow], +.surfaceWithArrow[data-placement='below'] [data-arrow] { + inset-inline: 0; + margin-inline: auto; +} + +.surfaceWithArrow[data-placement='before'] [data-arrow], +.surfaceWithArrow[data-placement='after'] [data-arrow] { + inset-block: 0; + margin-block: auto; +} + +.surfaceWithArrow[data-placement$='-start'] [data-arrow] { + inset-inline-start: var(--arrow-padding, 12px); +} + +.surfaceWithArrow[data-placement$='-end'] [data-arrow] { + inset-inline-end: var(--arrow-padding, 12px); +}