feat(react-components)!: migrate styling from Griffel to Tailwind v4 + CSS Modules (Griffel-zero) - #36513
Draft
Ray Knight (ArrayKnight) wants to merge 640 commits into
Draft
feat(react-components)!: migrate styling from Griffel to Tailwind v4 + CSS Modules (Griffel-zero)#36513Ray Knight (ArrayKnight) wants to merge 640 commits into
Ray Knight (ArrayKnight) wants to merge 640 commits into
Conversation
…es hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…s hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…styles hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… from styles hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…yles hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…n from styles hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…s hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…les hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…les hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…tyles hooks (Batch F) Non-mutating composition per the user-mandated D14; react-hooks/immutability now enforces with zero suppressions in converted packages. Class output verified identical (snapshots unchanged by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…hipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ribe the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…e shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…e the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…e shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…describe the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…nd docs describe the shipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…hipped contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…al kebab variables (theming Phase 2a) SCSS variable NAMES stay camelCase (a JS-like read API, as in Phase 1); only the emitted var() references change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Mechanical rename of raw var(--camelCaseToken) references (inline styles, .styles.ts, stories, module css) to the canonical kebab names from token-rename-map.json. chart-web-components excluded (separate system). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ing Phase 2a) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…theming Phase 2a) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: apps/public-docsite-v9 Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: apps/vr-tests-react-components, migration, packages/react-components/react-accordion, packages/react-components/react-aria, packages/react-components/react-avatar, packages/react-components/react-badge, packages/react-components/react-breadcrumb, packages/react-components/react-button, packages/react-components/react-calendar-compat, packages/react-components/react-card Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/react-carousel, packages/react-components/react-checkbox, packages/react-components/react-color-picker, packages/react-components/react-combobox, packages/react-components/react-datepicker-compat, packages/react-components/react-dialog, packages/react-components/react-divider Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/react-drawer, packages/react-components/react-field, packages/react-components/react-image, packages/react-components/react-infolabel, packages/react-components/react-input, packages/react-components/react-label, packages/react-components/react-link, packages/react-components/react-list, packages/react-components/react-menu, packages/react-components/react-menu-grid-preview Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/react-message-bar, packages/react-components/react-migration-v0-v9, packages/react-components/react-migration-v8-v9, packages/react-components/react-nav, packages/react-components/react-overflow, packages/react-components/react-persona, packages/react-components/react-popover, packages/react-components/react-portal Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/react-positioning, packages/react-components/react-progress, packages/react-components/react-radio, packages/react-components/react-rating, packages/react-components/react-search, packages/react-components/react-select, packages/react-components/react-skeleton, packages/react-components/react-slider, packages/react-components/react-spinbutton, packages/react-components/react-spinner, packages/react-components/react-storybook-addon, packages/react-components/react-swatch-picker, packages/react-components/react-switch, packages/react-components/react-table, packages/react-components/react-tabs Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/react-tag-picker, packages/react-components/react-tags, packages/react-components/react-teaching-popover, packages/react-components/react-text, packages/react-components/react-textarea, packages/react-components/react-theme, packages/react-components/react-toast, packages/react-components/react-toolbar, packages/react-components/react-tooltip, packages/react-components/react-tree, packages/react-components/recipes Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
Areas: packages/react-components/theme-designer, packages/tokens, tools Mechanical rename per reports/token-rename-map.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… map (md + json) Full-token-set kebab rename recorded: namespace mapping decisions, zero utility-name impact (467 @theme keys byte-identical), the dual-vocabulary provider-tag correction to the task premise, inline-reference adoption, sweep counts (5,331 repl / 438 files + tokens/sass/theme), and all gate results (identity probe 2,237 checks; VR 76/76 at zero tolerance; jest/SSR/lint/tc green; perf E-cells flat). token-rename-map.json is the machine-readable find/replace source for consumer migration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…pshot (theming 2b) Adds the 7 shipped theme CLASS-name constants (webLightThemeClassName, ..., themeClassNames map, ThemeClassName type) — the JS read path for the static CSS theme classes — plus the committed theme-values.json snapshot (7 themes x 459 keys) that feeds the react-tailwind-theme generator without a build-time dependency edge. Jest gates: themeClassNames derivation lockstep + themeValues.json drift (deep-equal vs computed themes); generate/verify scripts wired as nx targets and verify-theme-values added to the CI target lists. Theme objects and createTheme factories deliberately REMAIN exported here — they are the build-time generator/tooling input under the Phase 2b contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… classes (theming 2b) The generator now reads packages/tokens/theme-values.json + themeClassNames.ts (text-level, still no build dependency) and emits: (a) the 433 theme-variant web-light values into the existing @layer fui.theme { :root, :host } block — the artifact is the SOLE default value source now that FluentProvider's runtime tag is gone — and (b) NEW generated css/themes.css with one class per shipped theme (.fui-theme-web-light ... .fui-theme-teams-dark-v21; 433 custom-property declarations each, @layer fui.theme), imported by css/index.css. Cross-asserts theme-set/classname/tokens.ts lockstep, spacing/stroke theme-invariance (the 26 stay :root-only in density-knob form) and zIndex theme-absence. dist/styles.css 2,810 -> 175,638 B raw (14,603 B gzip). Generated css is prettier-ignored: byte-exact value fidelity vs the JS themes beats formatting (prettier would rewrite rgba spacing/trailing zeros). @reference emission verified unchanged (zero theme leakage into module output). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ag removed (theming 2b) BREAKING (the migration major): FluentProvider's theme prop (JS theme object) is REPLACED by themeClassName?: string — pass a shipped constant (webDarkThemeClassName, ...) or any class containing only custom-property declarations. The provider injects NO styles: useFluentProviderThemeStyleTag, createCSSRuleFromTheme, serverStyleProps + the SSR style element, the nonce prop + StyleNonceContext (D20.1 retired) and the theme-undefined dev warning are all deleted. Nested providers inherit the resolved class via the new FluentProviderThemeClassName context (useFluentProviderThemeClassName_unstable, @internal); portals keep the EXISTING ThemeClassNameContext propagation (root.className carries the class; bare class under applyStylesToPortals=false). styles hook leads with styles.root now that the theme class may be empty (D15.1 marker-position invariant). react-shared-contexts: ThemeContext_unstable/ThemeProvider_unstable/ ThemeContextValue_unstable removed (sole producer gone); react-theme dep dropped. react-portal-compat: v8 portals apply the RESOLVED theme class from the new context instead of regex-extracting the runtime fui-FluentProvider<id> class (which no longer exists); works for consumer-authored classes too. Tests rewritten to the new contract (37/37 provider, portal-compat green); SSR markup is a plain div (hydration head snapshot now empty); api.md regenerated. Nested-provider custom theming story now demos a module-css theme class. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…seCssVarValue memo+deps (theming 2b) BREAKING: @fluentui/react-components and @fluentui/react-theme stop exporting the 7 theme objects, create*Theme factories, themeToTokensObject and the Theme/PartialTheme/Brands/BrandVariants types (they remain in @fluentui/tokens for build-time tooling), plus the removed provider APIs (createCSSRuleFromTheme, useFluentProviderThemeStyleTag). New exports: the 7 *ThemeClassName constants, themeClassNames map, ThemeClassName type and useFluentProviderThemeClassName_unstable. tokens/typographyStyles unchanged. Bundle-size fixtures follow the new exports. react-utilities: useCssVarValue gains a module-level memo keyed by (element, variable) and an EXPLICIT re-read trigger — a deps option compared like an effect dependency list; providing deps opts that instance out of the cross-mount memo on first read (a cached hit after a theme change while unmounted would be stale). Default stays read-once; no per-render reads, no observers. 10/10 unit tests; api.md regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…seCssVarValue (theming 2b)
BREAKING: useIsDarkTheme now takes an element ref and compares the HSL
lightness of --color-neutral-background-1 vs --color-neutral-foreground-1
computed at that element (canonical names derived from the tokens.* strings —
lockstep, not hardcoded), replacing the removed JS ThemeContext read. The
duplicated DeclarativeChart copy is unified onto the shared hook;
DeclarativeChart uses its container ref, VegaDeclarativeChart merges a root ref
with the forwarded one. Web-light fallbacks preserve the pre-2b default-theme
behavior before mount/outside themed scopes. Tests: theme-object literals ->
themeClassName={webDarkThemeClassName}; snapshots regenerated (diffs are
provider markup + useId shifts only; 917 passed / 86 skipped).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ped-theming + deprecated-package probes (theming 2b)
Mechanical sweep of the component packages' cypress mounts, e2e helpers and the
react-portal unit test from theme={<shipped>Theme} to
themeClassName={<shipped>ThemeClassName} (38 cy files + helpers). NEW committed
browser probes: react-menu MenuThemeScoping.cy.tsx (2/2 — a theme class on any
subtree node themes it while siblings keep :root web light, and a portaled menu
opened inside a themed provider carries the class + resolves dark values on the
portal mount node) and deprecated react-infobutton InfoLabel.cy.tsx (+2 — the
still-Griffel package's tokens.* references resolve against the dark theme
class end to end; deprecated packages have no stories anywhere, so cypress is
the themed-rendering evidence). Motion preview demo + text migration doc ride
along.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…cts from @fluentui/tokens, apply them as CSS classes (theming 2b) theme-designer keeps building Theme objects (createLightTheme/createDarkTheme now imported from @fluentui/tokens) and previews/exports them as generated custom-property theme classes via a local applyThemeAsClass util (canonical names derived from the tokens.* var() strings); export snippets emit the new consumer pattern. react-migration-v8-v9: createV9Theme/createV8Theme stay object-level converters importing from @fluentui/tokens (documented as build-time/tooling input); ThemeShim stories preview via a generated theme class; button-shim stories swept to themeClassName constants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… 2b) getStoryVariant maps DARK_MODE / HIGH_CONTRAST / RTL onto the shipped theme class constants instead of theme objects, in both the vr-tests copy and the published tools/visual-regression-utilities mirror. The mirror has zero in-repo consumers and is therefore NOT covered by any of the 76 VR sets -- its gate is its unit spec (3/3); recorded in the report's VR-reach note so the pixel evidence is not overclaimed. Also widens the capture staleness guard, which walked only packages/react-components and so could never have caught a stale getStoryVariant. It now also walks packages/charts (19 stories/Charts/* files import @fluentui/react-charts), apps/vr-tests-react-components/src and tools/visual-regression-utilities/src. ChartAnnotationLayer's fontSize 18 -> '18px' is a non-theming edit that arrived inside this diff. Kept because it is part of the tree the 76/76 was captured from and is pixel-neutral (React serializes numeric fontSize to px); called out in the report rather than silently dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
… theme prop, nonce and runtime style tag (theming 2b)
Sweeps the docsite to the shipped contract: import styles.css once, web light
is the :root default, and a theme is applied with themeClassName={xThemeClassName}
or by putting a theme class on any DOM node. Theming.mdx is rewritten around
scoped theming, portal inheritance, the 7 constants + themeClassNames map +
ThemeClassName type, and custom themes as classes of custom-property
declarations in canonical kebab names. It also gains an explicit before/after
migration callout -- theme={webLightTheme} -> themeClassName={webLightThemeClassName}
-- and the import line the nested-provider example was missing.
The createCSSRuleFromTheme section is replaced by Utilities/Theme/ThemeClasses
(default, custom, and a live theme-switching story plus a best-practices doc).
ThemeShim reads theme objects from @fluentui/tokens, which the app now declares
as a dependency ("*", matching all 16 of its other @FluentUI deps in this
private app) -- that clears 4 import/no-extraneous-dependencies errors; the 9
that remain were proven pre-existing by linting the pre-2b file content.
Closeout fixes folded in:
- AdvancedConfiguration.mdx documented the REMOVED nonce prop and runtime
<style> tag and actively told consumers to migrate TO the nonce prop. The CSP
section is rewritten (component CSS is a static asset covered by style-src
source lists; the provider creates no style element), as is "Configuring
rendering", which claimed the provider writes custom properties into the
child document.
- QuickStart.mdx and Migration/GettingStarted.mdx were half-applied: code
fences swapped, prose left asserting that FluentProvider owns the token
values / that a theme object sets the CSS variables. Prose now matches.
Verified against the SSR run: 0 <style> elements, 0 nonce attributes,
theme = fui-theme-teams-light. public-docsite-v9:build-storybook exits 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ClassName (theming 2b) Mechanical prop/import swap to the class constants across the 8 perf scenarios, the react-components-vite starter template and the recipes Example template. Note on FluentProviderWithTheme: the scenario is now effectively vestigial. Its forced-reflow harness existed to measure the cost of the provider's theme injection, and there is no injection left -- 20 nested providers now differ from the sibling FluentProvider scenario only by count and a static class string. Its perf baseline will therefore move sharply for a reason unrelated to any component; keeping the scenario for continuity, flagged so the shift is not misread as a component regression. The dedicated before/after benchmark measured provider mount at 9.40 ms -> 0.30 ms median per 40-provider window (-96.8%, n=186/leg), with liveness asserted at 40 vs 0 injected style tags. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…VarValue deps (theming 2b) Phase 2b moved useIsDarkTheme from a ThemeContext_unstable read to useCssVarValue, and dropped reactivity in the process: useCssVarValue is read-once-per-(element, variable) by contract and module-memoized in a WeakMap, so with no deps a stable chart container leaves DeclarativeChart and VegaDeclarativeChart pinned to whatever theme was active at first mount. Fix: pass the resolved theme class from useThemeClassName_unstable as deps on both reads. That restores the pre-2b envelope exactly -- provider theme changes re-read; a class swapped on a bare ancestor element was never tracked pre-2b (it read provider context) and still is not. The hook's JSDoc states that boundary rather than overclaiming it. Not useFluentProviderThemeClassName_unstable: it lives in @fluentui/react-provider, which react-charts does not depend on; useThemeClassName_unstable is already a dependency and carries the same string (state.themeClassName joins root.className and is published by useFluentProviderContextValues_unstable). New suite VegaDeclarativeChartHooks.test.tsx (5 tests) -- proven to bite: with deps removed the 2 reactivity tests fail; restored, 5/5 pass. Package suite 922 passed / 86 skipped, no snapshot churn. No VR set renders a consumer of this hook: it is imported only by DeclarativeChart and VegaDeclarativeChart, neither of which has a story under apps/vr-tests-react-components/src, and only barrel files re-export them -- so the unit suite is its gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…nd theme utils on the class-based API (theming 2b)
- react-storybook-addon: withFluentProvider maps theme ids to the shipped
class constants; the docs container and example container follow.
- react-storybook-addon-export-to-sandbox: generated sandboxes emit
themeClassName, so exported examples compile against the new surface
(54/54).
- scripts/test-ssr: the generated entry points render
themeClassName={teamsLightThemeClassName}. Verified end to end --
ssr-tests-v9:test-ssr exits 0 with 0 <style> elements and 0 nonce
attributes in the output.
- storybook-llms-extractor: the golden fixture and its inline snapshot were
half-applied (code samples swapped, prose left saying "styled using CSS in
JS ... a style renderer which inserts CSS into DOM ... pass theme as a
prop"). Fixed -- this fixture is the contract for generated llms.txt, so
the stale model would have shipped straight to LLM consumers (9/9, 6
snapshots).
- theme-designer / react-migration-v8-v9 shims: applyThemeAsClass builds
theme objects from @fluentui/tokens and applies them as generated
custom-property classes. The theme-designer copy injected into the global
document while its byte-identical twin correctly used
useFluent().targetDocument -- fixed (child-window/iframe hosts are exactly
why that plumbing exists). Both copies now carry a DUPLICATE
cross-reference and note that a theme key with no matching tokens entry is
silently dropped.
- react-theme-sass, eslint-plugin README: docs/tests to the new names.
- workspace-plugin generator spec: themeClassName in the scaffolded output.
This suite fails on Windows for pre-existing reasons (EBUSY temp locks,
POSIX-vs-Windows path assertions); the pre-2b content of this spec fails
snapshots 3, 9, 11 and 12 while the 2b content fails only 3 and 9.
eslint-plugin no-restricted-imports fixtures deliberately keep the old
symbol names -- they are arbitrary RuleTester string literals, never
type-checked, and the rule is config-driven.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…ajor gates (theming 2b) Change files for every package Phase 2b touches with non-ignored files, plus the manifest gate lifts the entries need to validate. Missing entries added: - @fluentui/tokens (prerelease) -- the package that gained the ENTIRE new public surface: the 7 theme class-name constants, themeClassNames and ThemeClassName, plus the committed theme-values snapshot. Shipping without an entry meant no version bump and no changelog for the new API. - @fluentui/react-theme-sass (prerelease). Both manifests disallow major/minor/patch, so prerelease is the only writable type -- matching the existing react-tailwind-theme entry. Gate lifts (one deleted "major" line each, precedent commit 9b59224, which did the same for 56 manifests): react-theme, react-shared-contexts, react-portal-compat. Their new major entries would otherwise be rejected by beachball's validator, masked today only because the missing-entry error fires first. @fluentui/react-storybook-addon flipped major -> minor: it is 0.7.1, and the branch's stated 0.x convention (eae2756) keeps 0.x packages on minor. That also removes its need for a gate lift. Changelog-fidelity entries added for react-storybook-addon-export-to-sandbox (the sandbox scaffold change) and react-tailwind-theme (the 2b artifact delta -- byte figures deliberately omitted, see the report's correction note: the reported tokens.css baseline was wrong and dist/styles.css is not committed at the base ref, so neither figure is reproducible from git). Simulating beachball's validator over all 223 entries now reports 0 disallowed types. @fluentui/theme-designer is private, so its entry is warned-and-discarded before validation (223 -> 220 entry delta) -- kept for the human record, matching the pre-existing precedent entry. NOT fixed here: 18 other packages beachball flags have zero Phase-2b-attributable non-ignored files. That residual is pre-existing branch debt and needs its own decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…nsumer migration map (theming 2b)
Closes the record for the phase that removes the JS theming path.
reports/theming-css-native.md -- the Phase 2b section now carries the
shipped artifact (7 theme classes, their contents and sizes, the deliberate
spacing/stroke and zIndex exclusions), the themeClassName prop shape and its
resolution order, the provider surface delta (runtime tag, createCSSRuleFromTheme,
useFluentProviderThemeStyleTag, serverStyleProps, the nonce prop and
StyleNonceContext, ThemeContext_unstable -- all removed), the scoped-theming
and portal contract, the useCssVarValue memo/deps semantics, the runtime
value-reader inventory, and the full gate evidence. Added in this commit:
the re-gate table taken after the closeout fixes (type-check 0, lint 0, the
per-package jest totals, SSR exit 0 with 0 style tags and 0 nonce attributes,
docsite storybook exit 0, perf recomputed from the stored 186+186 samples at
9.40 -> 0.30 ms median, -96.8%), the two failures proven pre-existing by
running pre-2b file content, the beachball state and its 18-package
pre-existing residual, and a "deliberately deferred" list.
Honesty corrections recorded rather than quietly fixed: the artifact byte
figures were wrong (tokens.css pre-2b measures 41,511 bytes, not 20,842;
dist/styles.css is not committed at the base ref so its baseline is not
reproducible; local gzip measures 15,714, not 14,603), and the VR-reach note
states precisely what the 76/76 does NOT cover -- the published
visual-regression-utilities mirror (no in-repo consumers) and the react-charts
hook (no VR story renders a consumer).
NEW reports/theme-api-migration-map.{md,json} -- the committed consumer
find/replace source for this break: 7 theme -> class rows, the prop rename,
the relocated-to-@fluentui/tokens list and the removed-with-no-replacement
table. This is the Phase-2b counterpart to token-rename-map, which Phase 2a
shipped and 2b was missing.
DECISIONS.md D28 records the settled contract and the D20.1 retirement (the
nonce existed solely for the theme style element; with no style element
there is nothing to nonce -- component CSS is a static asset covered by CSP
style-src source lists).
Two maintained in-repo docs still taught the removed surface and are fixed:
docs/react-v9/contributing/patterns/extending-tokens.md (imported
themeToTokensObject, webLightTheme and Theme from the umbrella and used the
theme prop) and docs/architecture/design-tokens.md, the unswept twin of the
copilot.instructions.md block. Both of those also asserted pre-2a
vocabulary -- tokens.colorNeutralForeground1 === 'var(--colorNeutralForeground1)'
-- which is false: the value is 'var(--color-neutral-foreground-1)', and
themes.css declares 0 camelCase names against 7 kebab-case.
Historical RFCs under docs/react-v9/contributing/rfcs/** are point-in-time
design records and stay untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…aminated The 2026-08-07 restatement re-ran metrics/capture.sh unmodified on 7cc30e3. Its outputs are the provenance for every figure republished in FINAL_REPORT.md 2.0 and in the PR body, so the leg belongs in the tree alongside baseline/ and phase4/ rather than living only on the machine that measured it. 12 of the 15 captured files are committed. The other three are the build-vnext.log, monosize.log and storybook-build.log captures, which migration/griffel-to-tailwind/.gitignore excludes from every leg. A README in the leg directory marks it superseded before anyone cites it. At this commit the root package.json resolutions field redirects @fluentui/react-icons to the LOCAL-ONLY fork tarball introduced by 90d1096 / b0248a5, and the installed package self-reports version 2.0.334 with a styles.css export and tslib as its only dependency, i.e. the headless build rather than published Griffel-based 2.x. Anything measured through a resolved dependency graph therefore does not reproduce on the tree a maintainer clones, and the README says which captures that covers and which measure only this repo's own output. The json captures are prettier-formatted to satisfy the pre-commit hook; parsed values were compared before and after and are identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
…-run today The reports carried figures measured at Phase 4 of the original migration. Everything since -- the Griffel-zero campaign, the icons fork, the @Property remedy, Prettier 3, and theming phases 1 / 2a / 2b -- landed after those numbers were taken, and a prior gate pass found several that no longer reproduce at all. Every figure below was re-run against 7cc30e3 and its capture is committed at metrics/head-7cc30e35/. FINAL_REPORT.md gains a 2.0 "what is publishable today" section that sorts each metric into publishable, blocked, needs-reframing, or not-re-measured, with the reason attached to each. Four survive: the Griffel AOT recount (62 packages / 277 files -> 3 / 6, all under deprecated/), dead *.styles.raw.js (700,829 B -> 0, with 12 files / 44,304 B remaining on disk and none outside deprecated/), shipped lib/ JS (-15.57% like-for-like, the baseline restricted to HEAD's 84-package set), and the react-button tarball (-34.14% packed, -41.18% unpacked, 185 -> 176 files). Build time is withheld rather than restated. Five cold identical-scope runs on one unchanged tree measured 167/163/161/158/156 s in run order, a monotone warming trend spanning 6.8% of the median, against a single-observation baseline. No percentage in that range is defensible, so none is given. Bundle size is withheld because every monosize figure on this branch is measured through the LOCAL-ONLY icons fork. The VR storybook figure is disclosed as confounded twice over, not just once. It was already noted that the storybook gained 24 story files (137 -> 161). Newly recorded: it is a bundled artifact, 105 of its 505 files reference the icons package, and the icons override landed 2026-07-31 -- after the baseline (2026-07-27) and Phase-4 (2026-07-30) legs and before this one. The baseline bundled published Griffel-based icons and this leg bundled the headless fork. The direction and size of that component are not measured and deliberately not estimated; +12.95% is stated as the measured difference between two artifacts that differ in three ways. Unreproducible figures are removed rather than adjusted: - theming-css-native.md: the unsourceable 20,842 B for css/tokens.css pre-2b is replaced with the 41,511 B that ref 4e90ba2 actually measures; both dist/styles.css baselines (1,410 and 2,810 B) are removed outright because dist is not committed at that ref, so no baseline for it can be reproduced; the 14,603 B gzip figure is reinstated with its compression level annotated, having been retracted for disagreeing with a measurement taken at a different level. The correction block is rewritten as a binding "measurement conventions" section so the same class of mismatch cannot recur. - DECISIONS.md: D10's AOT target of "-> 0" is corrected to the 3 packages / 6 files that were actually reached; D11's nonce and react-icons bullets get retirement markers pointing at D28 and D27; the two 2,810 B figures go. - icons-integration-1.md: the 2,559 B figure is removed. It measured a gitignored artifact produced by a dependency that will not exist in the reviewed tree. A provenance caveat replaces it. - GRIFFEL_ZERO_CLOSURE.md: the charts-jest 912/86/998 cells are marked stage-local so they cannot be lifted into a PR body as current status. perf-property-remedy.md records a re-verification dated today: across the 63 shipped dist/styles.css artifacts, 2 @Property rules survive. Both are Tailwind-internal and confined to @fluentui/react-storybook-addon, a devDependency of one package and a runtime dependency of none, so the consumer-facing claim holds. Because the mechanism is document-wide, any perf measurement taken inside Storybook has a non-empty registry, and that is now stated where the measurements are. theme-api-migration-map.md splits the section 5 fall-through rule into light-derived and dark-derived cases, which behave differently, and adds a full-theme-class generator that was verified to run: it emits exactly 433 declarations, matching every shipped .fui-theme-* class. FINAL_REPORT.md 6.3 also records the open items this pass could not close without touching component source or release metadata, including the Theme Designer createThemeClassRule defect, which emits 26 literal spacing and stroke tokens that sever the --spacing and --base-scale knobs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
The migration removed the JS theming path, renamed all 467 token CSS variables and made the theme package a required install, but the docsite still taught the removed surface. Two of the breaks fail silently -- a var() on an undefined custom property drops the declaration with no error, warning or log, and skipping the theme stylesheet renders components with collapsed spacing, no radii and no shadows -- so a consumer who reads nothing gets no signal at all. This gives them somewhere to land. NEW Concepts/Migration/FromV9/UpgradeGuide.mdx is the single entry point: seven breaking changes with before/after for each, ordered so the two silent ones come first. NEW Concepts/Migration/FromV9/TokenRename.mdx is generated from the committed token-rename-map.json rather than written by hand: the failure mode, a summary of the 11 Tailwind namespaces the names now align to, a find/replace script, and the full 467-row old -> new table. Corrections to pages that taught the old contract: - Theming.mdx: the light-derived vs dark-derived fall-through hazard (the two behave differently and the difference is silent), an end-to-end custom brand-ramp recipe, runtime theme switching, and useCssVarValue as the replacement for the removed ThemeContext_unstable. - StylingComponents.mdx: "still exported / supported escape hatch" was false -- replaced with the removal and the two migration paths out of it. The FluentProvider custom-properties claim is corrected. - SSR.mdx and Remix.mdx: these told readers to use a deprecated makeStyles re-export that no longer exists; they now point at a direct @griffel/react dependency. - Portals.mdx: new "theming portals" section, since portals inherit a theme class rather than a provider context. - QuickStart.mdx: a "loading stylesheets" section covering the three delivery paths, including the umbrella CJS gap. - UnprocessedStyles.mdx: the charts and icons rows still said those packages ship Griffel raw modules. Both now ship plain CSS. - AdvancedStylingTechniques.mdx: the custom style hook example did not work. It discarded the value the hooks return, referenced an undefined symbol in the hook map, and had an unbalanced JSX tag. Rewritten to the returning shape. - Migration/GettingStarted.mdx: cross-link to the new guide. - react-components README: the install line omitted the theme package the component CSS compiles against, which is break 0 of the upgrade guide. Documentation only; no component source is touched and no rendered output changes, so the visual-regression baselines are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrate react-components styling from Griffel to Tailwind v4 + CSS Modules
This PR replaces the runtime CSS-in-JS styling system (Griffel
makeStyles/mergeClasses) across the entire non-deprecated v9 surface — every react-components package, the charts package, the test/VR/SSR/perf harness, and the stories and docs pages — with build-time CSS Modules + Tailwind v4 tokens under a cascade-layer contract (@layer fui.theme, fui.base, fui.components(.l1–l5), fui.utilities). The repo ends Griffel-zero outsidedeprecated/packages: the Griffel AOT pass runs in 3 packages / 6 files, all underpackages/react-components/deprecated/, and no*.styles.raw.jsis emitted anywhere else. Per-stage counts are inGRIFFEL_ZERO_CLOSURE.md.Why
dist/styles.csscompiled at build time; no style computation or injection at runtime, and no Griffel AOT compilation step.metrics/capture.sh, same machine, cold, clean tree (full data and provenance:migration/griffel-to-tailwind/reports/FINAL_REPORT.md§2.0):packages/react-components/deprecated/(react-alert 1, react-infobutton 2, react-virtualizer 3)*.styles.raw.jsshipped in every tarball: 700,829 B → 0 across non-deprecated react-components (12 files / 44,304 B remain on disk, all underdeprecated/)lib/JS across react-components: −15.57% (like-for-like, 4,556,347 → 3,846,978 B; baseline restricted to HEAD's 84-package set, sincereact-conformance-griffelleft it fordeprecated/)@fluentui/react-buttonnpm tarball: −34.14% packed (124,354 → 81,896 B), −41.18% unpacked (1,050,635 → 617,951 B), 185 → 176 filesFINAL_REPORT.md§2.7 — because doing so honestly requires the post-revert re-capture.Developer experience
Beyond the measurements, the authoring model changes in ways that matter day-to-day (expanded rationale: Less variant nesting in Tailwind with compound and stacked syntax):
*.module.css, organized by slot and state, instead of being distributed across atomic utility classes. That also bounds the blast radius of change: editing a component's rule affects that component — whereas editing a shared atomic/utility class (in code or live in DevTools) changes every element on the page that happens to use it..tsx, appearance in.module.css. Reviews get smaller and more focused; styling changes stop churning component logic files.fuicm-<component>-<slot>-<hash>), so component structure is identifiable by reading the markup in DevTools — versus a hashed atomic class soup where nothing in the element tells you what it is.@variant hover, focus-visible { … },@variant dark:hover { … }) expresses "these states share this appearance" once, instead of duplicated blocks that silently drift apart — the source reads the way you'd describe the design out loud.fui.theme→fui.base→fui.components.l1–l5→fui.utilities): a consumer override wins by layer, not by specificity games, source-order luck, or!important.webDarkThemeClassNameon<FluentProvider>, on a bare<aside>, or on<html>— no provider is required for a scoped theme, portals and nested providers inherit automatically, and the default (web light) resolves at:rootwith no wrapper at all. Custom themes are authored the same way, which means they are inspectable and overridable in DevTools like any other stylesheet, and switching themes is aclassNameswap instead of rebuilding and re-injecting a rule set.reports/perf-eval.mdandreports/perf-consumer-dynamic-class.md, each dated to the commit pair it traced.Runtime-performance deep dive (state-toggle investigation)
An observed state-toggle regression was investigated to root cause across eight controlled experiments (
migration/griffel-to-tailwind/reports/perf-*.md): selector policy, selector ordering, attribute-vs-class form, named groups,will-change, transition tightening,@layerstructure, and value literalization each measured null. The mechanism the investigation landed on: Blink charges a fixed per-started-transition cost whenever the document's custom-property registry is non-empty — any@propertyrule, related to the transition or not, reproduced by injecting a single dummy registration into the unmigrated build, which slowed it equally. The migrated theme had registered a handful of focus-ring knob properties; those registrations are now removed, with the same nesting-isolation guarantee reproduced at the cascade level. Verified on this branch: across all 63dist/styles.cssartifacts underpackages/, the theme package and every component package emit zero@propertyrules — the only two that remain are Tailwind-internal and confined to a dev-only storybook addon that no shipped package depends on. The remedy brings Button toggle cost back to Griffel parity and recovers most of Switch's; the remainder is custom-property resolution during transition-triggered recalc, the accepted cost of a fully var-driven theme.The per-cell timings, the µs-per-transition figure and the recovery percentages live in
reports/perf-mechanism-diagnostic.md,reports/perf-property-remedy.mdandreports/perf-layers-vs-literals.md, each stamped with the commit pair it traced. They are cited rather than restated here because they have not been re-run against the current HEAD, which has since had theming Phase 2b remove provider rule-building entirely. The diagnostic recommends filing the underlying behavior as a Chromium optimization gap; it affects any page that registers custom properties, regardless of styling system.Validation
Pixel identity was the non-negotiable gate throughout. Every conversion batch was captured against pre-change baselines via StoryWright and diffed at zero tolerance, with per-batch gates plus full-suite sweeps at phase boundaries. Where states lacked coverage, VR stories were added and baselined before converting (focus states, icon filled/regular swaps across hover/open/pressed/disabled, chart popovers, RTL/dark/high-contrast) — the VR story count under
apps/vr-tests-react-components/srcwent from 137 files at the baseline commit to 161 today. Behavioral gates: full jest suites at parity, the SSR render+hydration suite, and CDP computed-style/matched-rules equivalence probes for cascade-sensitive changes.Per-gate set and screenshot counts are deliberately not restated here: each was recorded against the tree that existed when it ran, and quoting one as a current status would misdate it. Every batch, decision, gate result and adjudication is in-repo with its own date under
migration/griffel-to-tailwind/— seeDECISIONS.md,GRIFFEL_ZERO_CLOSURE.md,theming-css-native.md(the most recent full-VR sweep), the per-batch reports, and the post-campaign consistency audits.Breaking changes (major-version)
Consumer-facing walkthrough of all of the below, with before/after for each:
Concepts/Migration/Upgrading from v9on the docsite, plus the full 467-row token map at.../FromV9/TokenRename.mdx.@fluentui/react-tailwind-thememust be installed and its stylesheet imported once per document. It carries the cascade@layerorder, the token values (Web Light at:root, :host), the spacing/stroke scales and the shipped theme classes — everything the component CSS references. This is the break most likely to be missed, because skipping it does not throw: components render with collapsed spacing, no radii and no shadows. Two of the breaks in this list fail silently; this is one, the token rename (item 7) is the other.fui-Component/fui-Component__slotand thexClassNamesslot maps). The supported styling contract is: per-slotclassNameprops; one public identity marker per component (group/fui-<component>, usable viafuiSelector()from react-utilities and as a Tailwind group target);data-*state attributes; and the documented layer system for cascade-safe overrides. This is a type change, not a deprecation: 215*ClassNamesconstants typedSlotClassNames<…Slots>onmasterare 193 typed{ root: string }here, so every non-root slot reference is a compile error. The one step the type system cannot force is`.${x.root}`→fuiSelector(x.root), since string building type-checks fine.@fluentui/react-components(12 runtime symbols + 3 types) along with its@griffel/reactdependency.@fluentui/react-conformance-griffelretired (moved todeprecated/, per repo precedent).TextDirectionProvider— direction flows viadir+ CSS logical properties, so RTL now follows the DOM rather than React context. (That was an internal render detail, not a published export; it appears in noapi.mdatmaster.) The interim first-classnonceprop was itself removed by item 7 — the provider no longer creates any style element, so there is nothing to sign; cover the static.cssassets with a CSPstyle-srcsource list.@fluentui/react-iconsv3 (headless) required — see Merge order.--colorNeutralBackground1→--color-neutral-background-1,--spacingHorizontalM→--spacing-horizontal-m,--strokeWidthThin→--stroke-width-thin, …; complete old→new map committed atmigration/griffel-to-tailwind/reports/token-rename-map.jsonand published as a docsite page with a find/replace script) — the old camelCase variables no longer exist in any shipped CSS. This one fails silently: avar()on an undefined custom property drops the declaration with no error, warning or log. Thetokensobject is unaffected — same JS key names, updated values — so only hand-written CSS needs rewriting. Themes ship as static CSS classes in@fluentui/react-tailwind-theme(.fui-theme-web-dark, …; web-light values are the:rootdefaults), applied via FluentProvider's newthemeClassNameprop (typed constants:webLightThemeClassName, …) or directly on any DOM node for scoped theming; portals and nested providers inherit automatically. REMOVED: FluentProvider'sthemeprop and runtime theme<style>tag, itsnonceprop,createCSSRuleFromTheme,useFluentProviderThemeStyleTag,ThemeContext_unstable, and the umbrella/react-theme exports of the theme objects,create*Themefactories,themeToTokensObjectandTheme/PartialTheme/BrandVariantstypes (theme objects remain in@fluentui/tokensas build-time tooling input). Custom themes are CSS classes containing only custom-property declarations; runtime value reads go throughuseCssVarValue(from@fluentui/react-utilities, which the umbrella does not re-export). Provider mount no longer builds or injects a rule set at all — the work is gone rather than reduced, though this PR quotes no re-measured mount timing for it.Merge order / dependencies — IMPORTANT
This is PR 2 of 2 and must not merge first:
@fluentui/react-icons3.0.0 (headless default + requiredstyles.css). This PR's icon integration (the icons stylesheet imported atlayer(fui.components.l1), layered icon-state selectors, and retirement of the Griffel jest snapshot serializer) was validated against exactly that code.LOCAL-ONLY(revert-before-PR)commits (90d1096404,b0248a57f1) that point the workspace at a packed tarball of the icons fork for development. Before this PR is marked ready they are reverted — but the revert alone is not enough, and the remaining steps are load-bearing:package.jsonthere are 42@fluentui/react-iconsrange declarations and zero declare^3.0.0(36 ×^2.0.245, plus^2.0.306×2,^2.0.311,^2.0.239,^2.0.237, and the onefile:resolution). Reverting without bumping resolves the workspace to published 2.x, which is the Griffel-based build this PR's icon integration assumes is gone.yarn install, then re-run the icon-integration VR gate.bash migration/griffel-to-tailwind/metrics/capture.sh <leg>) and publish the bundle numbers from that leg. Every monosize figure currently on the branch is measured against the fork tarball, so none is quoted in this PR body — seeFINAL_REPORT.md§2.0 for what is and is not publishable and why.🤖 Generated with Claude Code
https://claude.ai/code/session_01XB8ax95bxM4N23bUkSm455