Skip to content

Design: require a theme for Flex theme consumers - #4433

Closed
Jason Morse (JasonVMo) wants to merge 1 commit into
mainfrom
user/jasonvmo/require-theme
Closed

Jason Morse (JasonVMo) wants to merge 1 commit into
mainfrom
user/jasonvmo/require-theme

Conversation

@JasonVMo

Copy link
Copy Markdown
Contributor

Platforms Impacted

  • iOS
  • macOS
  • win32 (Office)
  • windows
  • android

Description of changes

Flex components now require a theme. useThemeState throws when no theme information is found instead of falling back to a default theme state, and the default theme state is removed.

packages/agentic/design

  • src/flex/useThemeState.ts: a shared useRequiredThemeBoundary makes useThemeState and useThemeAppearance throw when there is no ThemeProvider, ThemedRoot, or legacy ThemeContext provider above the caller. The message points to ThemedRoot/ThemeProvider and to the test helpers. useThemeAppearance throws too, for consistency; its default appearance state is removed.
  • src/flex/themeState.ts, src/theming/appearance.ts: remove defaultThemeState and defaultThemeAppearanceState.
  • src/test/TestThemeProvider.tsx (new): TestThemeProvider and createThemeWrapper for tests that render Flex consumers. Defaults to one theme reference per mounted provider and a pinned light/standard/base appearance source.
  • package.json: new ./testing/theme entry point. It is separate from ./testing, which must stay free of react-native because @fluentui-react-native/test-tools re-exports mockTheme from it and packages on the react Jest platform (for example @uifabricshared/theming-ramp) cannot import react-native.
  • src/test/compatibility/*: entry-point list and snapshot include ./testing/theme; import-boundary tests forbid production imports of testing and testing/*.
  • src/flex/useThemeState.test.ts, src/test/TestThemeProvider.test.tsx: wrap provider-less tests, add throw coverage and helper tests.

apps/bundle-size

  • design-theme-state-legacy-source no longer retains the x3 defaults or the interaction algorithm (only defaultThemeState pulled them in), so its output patterns now forbid them. baseline.json is refreshed.
Scenario Change (raw)
components-button about -16.8 KB
components-catalog about -16.8 KB
design-theme-state-legacy-source -16.77 KB
design-flex-theme, design-flex-warm-theme -1.24 KB

Docs and release

  • Updated design/references/{theming,testing,entry-points,contributing}.md, design/AGENTS.md, and components/references/tests-and-stories.md.
  • Added .changeset/design-require-theme.md (design and components, minor).

Breaking behavior: anything that renders a Flex consumer outside a ThemedRoot, ThemeProvider, or legacy ThemeContext provider now throws. Components tests already render through ThemedRoot (renderWithTheme), and the Storybook runtime wraps stories in a theme, so no component changes were needed.

Verification

Run from the repository root:

  • yarn clean-lage-cache then yarn lage buildci: passed (176 tasks, 0 failed)
  • yarn lage test-links --verbose --grouped: passed (89 tasks, 0 failed)
  • yarn change:check: passed
  • yarn bundle-size with the refreshed baseline: passed

No manual device testing; this is a JS-only change covered by Jest.

Before After
useThemeState without a provider returned the default theme state useThemeState without a provider throws and names ThemedRoot, ThemeProvider, and the test helpers

Pull request checklist

This PR has considered (when applicable):

  • Automated Tests
  • Documentation and examples
  • Keyboard Accessibility
  • Voiceover
  • Internationalization and Right-to-left Layouts

useThemeState and useThemeAppearance now throw when no ThemeProvider,
ThemedRoot, or legacy ThemeContext provider is above the caller. The
default theme state and default appearance state are removed.

Add design/testing/theme with TestThemeProvider and createThemeWrapper
so tests can render Flex consumers. It is a separate entry point because
design/testing must stay free of react-native.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Bundle size report

Tree-shaken, minified production esbuild bundles with React and React Native runtimes externalized.

Scenario Modules-Mac (Δ) Modules-Win (Δ) Size-Mac (Δ) Size-Win (Δ)
components-button 70 (-8) 71 (-8) 56.82k (-16.81k) 56.74k (-16.81k)
components-catalog 219 (-8) 220 (-8) 212.44k (-16.87k) 212.37k (-16.87k)
design-theme-state 47 (+0) 48 (+0) 53.43k (+156b) 53.35k (+156b)
design-themed-root 51 (+0) 52 (+0) 55.41k (+152b) 55.34k (+156b)
design-color-lib 6 (+0) 6 (+0) 6.27k (+0b) 6.27k (+0b)
design-theme-state-legacy-source 40 (-8) 41 (-8) 37.03k (-16.77k) 36.96k (-16.77k)
design-legacy-theme 42 (+0) 42 (+0) 39.10k (-11b) 42.90k (-11b)
design-legacy-theme-from-flex 54 (+0) 54 (+0) 59.86k (-12b) 63.67k (-14b)
design-fluent-light-theme 7 (+0) 7 (+0) 18.74k (+0b) 22.59k (+0b)
design-fluent-theme 35 (+0) 35 (+0) 27.44k (-12b) 31.24k (-12b)
design-fluent-hc-theme 32 (+0) 32 (+0) 27.33k (-12b) 31.12k (-12b)
design-flex-theme 33 (+0) 33 (+0) 22.13k (-1.24k) 22.11k (-1.24k)
design-flex-warm-theme 33 (+0) 33 (+0) 22.19k (-1.24k) 22.18k (-1.24k)

The job is advisory: size changes are reported but do not fail the pull request. Bundle or analysis errors still fail.


View bundle-size workflow run

| -------------------- | ---------------------------------------------------------------- | ------------------ |
| `useThemeState` | `ThemeState`: `tokens`, resolved `appearance`, and `themeStyles` | Throws |
| `useThemeAppearance` | The requested and resolved appearance | Throws |
| `useTheme` | The legacy `Theme` (from the `theming` subpath) | `undefined` |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe with v1 we usually encouraged useFluentTheme usage in the components because it has a fallback theme built in, and thus is more stable for consumers. Are you ok with not having that fallback behavior?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm, good catch, I had forgotten we used the fallback for v1. The tradeoff is in bundle size if you are overriding the theme but it does make things more foolproof.

Thinking about it I will rework this one as:

  • switch the current default flex token theme to use the new generated flex theme source
  • build the default theme on demand one time, using the current system Appearance dark|light setting to guide which set of defaults to use.

@JasonVMo
Jason Morse (JasonVMo) deleted the user/jasonvmo/require-theme branch November 5, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants