Skip to content

Design: rework Flex tokens, add Fluent themes, and move Fluent 2 token JSON behind design/deprecated - #4429

Merged
Jason Morse (JasonVMo) merged 19 commits into
mainfrom
user/jasonvmo/design-rework-legacy-removal
Nov 2, 2026
Merged

Jason Morse (JasonVMo) merged 19 commits into
mainfrom
user/jasonvmo/design-rework-legacy-removal

Conversation

@JasonVMo

Copy link
Copy Markdown
Contributor

Platforms Impacted

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

Description of changes

Reworks the @fluentui-react-native/design theming contract and moves every module that loads the Fluent 2 token JSON
behind a new @fluentui-react-native/design/deprecated entry point. Related to #4263 and #4276 (partial work, so no
closing keyword).

This branch is 18 commits that have not landed on main. Reviewing by commit is easiest: the first 15 are the design
refactor, token codegen, and theme entry points, and the last three are the Fluent appearance sets, the deprecated
entry point, and a documentation fix.

Flex tokens on the x3 defaults (design, components, storybook-desktop-runtime)

  • Every Flex semantic color is a resolved InteractiveColor with rest, hover, and pressed; omitted states derive
    from rest. Appearances accept a colorTone (cool or warm). Defaults follow x3-design/fluent-design@206e890b,
    and shadows become layered, scheme-aware arrays.
  • Package sources move under src/flex, src/fluent, src/types, and src/test; export keys and conditions are
    unchanged.
  • Components read colors through the envelope, so default-theme rendering changes (achromatic brand, translucent
    neutral backgrounds, larger corner radii).
  • The Storybook chrome and canvas always use Flex; a Theme, Scheme, Contrast, and Tone selector applies only to story
    content.

Fluent themes and generated tokens (design, default-theme)

  • The Fluent token codegen emits light, dark, lightElevated, darkElevated, hcLight, and hcDark sets for
    every platform, with scheme and high-contrast fallbacks.
  • New design/themes entry point: fluentLightTheme, fluentDarkTheme, fluentHcTheme, fluentTheme, flexTheme,
    and flexWarmTheme. Elevated requests still resolve by color scheme.
  • default-theme uses design's Fluent themes instead of building tokens locally (macOS previously used the Windows
    set). createAliasTokens is removed.

Deprecated entry point (design, theming packages, shims, tester)

  • src/deprecated, exposed as @fluentui-react-native/design/deprecated, holds the token loaders, global and
    high-contrast alias tokens, and mapPipelineToTheme, mapFontPipelineToTheme, and mapPipelineToShadow. Only tests
    and other deprecated code may import it (R-DESIGN-003). The design import-boundary test enforces it and bundle-size
    scenarios guard the root, legacy-theme, and Theme-from-Flex graphs.
  • Removed with no in-repository consumers: getDefaultLegacyTheme, the stockWebPalette modules, and the legacy
    default colors and alias tokens, with the ./tokens/legacy and ./theming/compat/defaults exports. theming no
    longer exports the pipeline mappers.
  • themeFromFlexTokens now starts from the generated Fluent theme for the appearance, so the theming entry point no
    longer bundles the token JSON.
  • android-theme, apple-theme, win32-theme, the theme-tokens and theming-utils shims, and tester-core import
    from design/deprecated with unchanged values and snapshots.
  • Retiring the entry point is tracked in design's references/remaining-work.md.

Breaking changes (each has a changeset)

  • design (minor): Flex colors are InteractiveColor envelopes and shadows are layer arrays; color-lib
    interaction functions drop their token parameter; the tokens/legacy and theming/compat/defaults entry points,
    getDefaultLegacyTheme, and the theming mapper exports are removed in favor of design/deprecated.
  • default-theme (minor): createAliasTokens is removed; read alias colors from theme.colors.
  • components (minor): default Flex rendering changes as above.

Verification

Automated, with exact commands and results:

  • yarn clean-lage-cache then yarn lage buildci at 46305a228: passed. 176 tasks succeeded, 0 failed, 0 skipped,
    covering lint-repo, check-publishing, check-agent-instructions, check-package-contents, the root build, and every
    package's test and lint. No downstream snapshot changed.
  • yarn oxfmt --check, yarn check-agent-instructions, and yarn change:check at c33e026c6: all passed.
  • In packages/agentic/design: yarn lint, yarn test --ci (41 suites, 527 tests), and yarn check:mappings:
    passed. A build after deleting lib and the build info also passed, and codegen left the generated token modules
    unchanged.
  • yarn bundle-size at the updated baseline: passed.
  • yarn lint in android-theme, apple-theme, win32-theme, theme-tokens, theming-utils, and tester-core: passed.
  • FURN_RN_PLATFORM=<ios|android|windows|win32> yarn test --ci in design: failed with 33, 33, 13, and 14 tests.
    Design Jest targets macOS, and the parent commit ca9f3f79e fails 33, 33, 14, and 14 with the same test names
    after renames (macOS-recorded snapshots and macOS-only expectations). I did not compare against main.

Not run: native app qualification on any platform, Storybook desktop tests, and manual testing on devices.

Bundle size, macOS raw bytes from apps/bundle-size/baseline.json on main and on this branch (Windows moves the same
way):

Before After
design-theme-state: 29.44 KB 52.02 KB (+22.58 KB raw, +7.57 KB gzip)
components-button: 49.44 KB 71.90 KB (+22.46 KB raw, +7.65 KB gzip)
design-legacy-theme-from-flex: 95.47 KB 58.47 KB (-37.01 KB raw, -3.69 KB gzip)
No Fluent or Flex theme scenarios design-fluent-theme 26.81 KB, design-flex-theme 22.82 KB

The root useThemeState growth is the interaction algorithm plus the generated x3 defaults. Every Fluent theme bundle
keeps nearly the whole generated token module because esbuild cannot drop unused spread-built objects; emitting
tree-shakable sets is a possible follow-up.

Pull request checklist

This PR has considered (when applicable):

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

Keyboard accessibility, VoiceOver, and RTL layouts are not applicable: this changes theme data and Storybook chrome
colors, not interactive behavior or layout.

Jason Morse (JasonVMo) and others added 18 commits October 31, 2026 16:03
Add export-map, runtime export, type inventory, legacy theme, adapter, native color, and Flex default fingerprint baselines that later phases must preserve.

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

Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
Move Flex-owned implementations under src/flex, legacy Fluent theme types,
references, token data, compat projection, and legacy styling under
src/fluent, shared type-only contracts under src/types, and test-only
fixtures and validation under src/test. Package export keys and conditions
are unchanged; only their targets moved.

- Unify ThemeState in src/types/themeState.types.ts and remove the
  duplicate RuntimeThemeState.
- Share guarded object-path traversal between mapObjectFromObject and
  themeFromFlexTokens while keeping their overwrite policies and errors.
- Add an import-boundary test with positive controls, and give every
  design bundle-size scenario a required-module control.
- Update generator paths, owning references, and docs links.

Compatibility baselines and snapshots are unchanged. Bundle module counts
are unchanged; Flex scenarios grow 122 B raw from the shared path helpers
and the legacy projection shrinks 238 B.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
… phase 3)

- Add ColorTone (cool|warm) to appearance requests and resolved appearances;
  tone resolves provider/root > source option > fallback > cool and is keyed
  separately only for warm. Raw legacy ThemeContext boundaries resolve cool.
- Make color-lib's interaction API token-free (colorTone, direction,
  compositing) and move interactive-token classification to src/flex/colors,
  exported from the root.
- FlexThemeReference derives omitted hover/pressed entries from rest for the
  appearance's scheme and tone; high contrast and unresolvable rest values use
  rest. The fill is eager per resolved appearance for now; phase 4 replaces it
  with lazy state envelopes.
- Move the bulk interaction-override helper to test fixtures.
- Guard ownership with import-boundary tests and add retained-output bundle
  assertions so legacy-only scenarios cannot retain the engine.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
…ctor phase 4)

Represent every Flex semantic color as one authored description and one
resolved InteractiveColor envelope with rest, hover, and pressed.

- Authored colors accept a value, a [light, dark] tuple, or flexColor options
  with explicit states and a standard, inverted, or fixed interaction type.
- Overrides replace each color atomically; color.hover/pressed maps are
  rejected with a migration error.
- Omitted interactive states derive lazily on first read and memoize per
  resolved color; defaults and the Theme adapter resolve explicitly.
- Recipes receive the authored description plus the resolved appearance;
  selectColorEntry reads appearance-specific entries.
- Migrate components and Storybook runtime reads to .rest/.hover/.pressed,
  revise component token-path contracts, and document the model and
  migration in the new design flex-colors reference.
- Narrow the legacy-source bundle scenario to allow the engine-free color
  envelope while still forbidding color-lib and the interaction policy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
Refresh the x3 pin to 206e890b and generate rest-first cool and warm Flex
defaults from the pinned sources. Interactive colors author a hover or
pressed state only where the runtime interaction policy cannot reproduce the
source; a coverage ledger records every destination, its state treatment,
and native adaptations. FlexThemeReference selects the default by colorTone.

Close the approved non-color gaps: layered, scheme-aware shadows with atomic
merging and selectShadowEntry, motion durations and easings, and
spacing.componentBase800. Legacy { ambient, key } Theme shadows project to
and from layers; the legacy projection keeps its Fluent fallbacks.

The forward Theme adapter keeps Fluent-native fallbacks in
flex/adapters/fluentFallbackTokens and stays engine-free, guarded by a new
import-boundary test. The default theme state now reaches the interaction
policy, so the legacy-source bundle scenario requires it instead of
forbidding it. Component tests read defaults from the testing entry point.

Bundle deltas against phase 4 (macOS, raw/gzip): theme-state +17.3k/+2.7k,
themed-root +17.3k/+2.7k, components-button +24.4k/+5.5k, catalog
+24.5k/+5.5k, legacy-source +24.4k/+5.4k, legacy-theme-from-flex +7.6k/+0.9k;
legacy-theme and color-lib are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
Add changesets for design (minor), components (minor), and
storybook-desktop-runtime (patch). Update the repository theming basics for the
x3 defaults and interactive color envelope, document the lossy shadow and motion
reverse projection, move the plan's open gates (fixed derivation, native layered
shadows and motion) into remaining-work.md, and remove the completed plan.

Refresh the bundle-size baselines. Against the Phase 1 measurement, Flex-state
scenarios grow about 28-29 KB raw / 7.1-7.3 KB gzip, legacy-theme-from-flex
+7.9 KB / +4.1 KB, and color-lib -62 B. The committed catalog baseline was
already stale before the refactor (155 KB committed vs 205 KB measured).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
Completes phase 6; the previous commit only removed the completed plan.

Add changesets for design (minor), components (minor), and
storybook-desktop-runtime (patch). Update the repository theming basics for the
x3 defaults and interactive color envelope, document the lossy shadow and motion
reverse projection, and move the plan's open gates (fixed derivation, native
layered shadows and motion) into remaining-work.md.

Refresh the bundle-size baselines. Against the Phase 1 measurement, Flex-state
scenarios grow about 28-29 KB raw / 7.1-7.3 KB gzip, legacy-theme-from-flex
+7.9 KB / +4.1 KB, and color-lib -62 B. The committed catalog baseline was
already stale before the refactor (155 KB committed vs 205 KB measured).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5030bce5-d7f7-4fb1-93de-68599b79269f
Tests added on main read Flex colors as flat strings and through the removed
color.hover and color.pressed maps. Read each InteractiveColor state instead,
drop a duplicate ProgressBar import from the merge, and refresh the bundle-size
baseline for the rebased tree.

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

- Generate light, dark, lightElevated, darkElevated, hcLight, and hcDark Fluent
  token sets for every platform. Elevated sets fall back to their base scheme,
  hcLight and hcDark fall back to each other, and missing high-contrast sets
  use light and dark. Windows high contrast uses the Win32 hc set as
  SystemColor brushes.
- Add fluentHcTheme, and have fluentTheme select the high-contrast theme for
  the color scheme under high contrast.
- Make default-theme use the design package's Fluent themes and remove its own
  token-building code, including createAliasTokens.
- Keep the Storybook chrome and canvas on Flex; the Theme selector applies only
  to story content.
- Update snapshots, the bundle-size baseline, docs, and changesets.

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

Move the Fluent 2 token loaders and pipeline mappers into src/deprecated,
exposed as @fluentui-react-native/design/deprecated. Only tests and other
deprecated code may import it (R-DESIGN-003, enforced by the design
import-boundary test).

Remove the legacy default theme, its stock web palettes, and the
tokens/legacy and theming/compat/defaults entry points, which had no
in-repository consumers. themeFromFlexTokens now uses the generated Fluent
theme for the appearance as its base, so the theming entry point no longer
bundles the token JSON.

Repoint the android, apple, and win32 themes, the theme-tokens and
theming-utils shims, and the tester at the deprecated entry point.

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

The legacy default theme is removed, so the x3 Flex defaults changeset and
the flex-colors reference no longer describe it as unchanged.

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

github-actions Bot commented Nov 2, 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 78 (+14) 79 (+14) 73.63k (+23.00k) 73.55k (+22.99k)
components-catalog 227 (+13) 228 (+13) 229.31k (+23.83k) 229.24k (+23.83k)
design-theme-state 47 (+13) 48 (+13) 53.27k (+23.12k) 53.20k (+23.12k)
design-themed-root 51 (+13) 52 (+13) 55.26k (+23.15k) 55.19k (+23.15k)
design-color-lib 6 (+0) 6 (+0) 6.27k (-62b) 6.27k (-62b)
design-theme-state-legacy-source 48 (+13) 49 (+13) 53.80k (+22.00k) 53.73k (+22.00k)
design-legacy-theme 42 (New) 42 (New) 39.11k (New) 42.91k (New)
design-legacy-theme-from-flex 54 (-4) 54 (-5) 59.87k (-37.89k) 63.69k (-33.95k)
design-fluent-light-theme 7 (New) 7 (New) 18.74k (New) 22.59k (New)
design-fluent-theme 35 (New) 35 (New) 27.46k (New) 31.25k (New)
design-fluent-hc-theme 32 (New) 32 (New) 27.34k (New) 31.13k (New)
design-flex-theme 33 (New) 33 (New) 23.37k (New) 23.36k (New)
design-flex-warm-theme 33 (New) 33 (New) 23.43k (New) 23.42k (New)

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

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

# Conflicts:
#	scripts/package.json
@JasonVMo
Jason Morse (JasonVMo) merged commit 06b458f into main Nov 2, 2026
18 checks passed
@JasonVMo
Jason Morse (JasonVMo) deleted the user/jasonvmo/design-rework-legacy-removal branch November 2, 2026 22:32
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.

4 participants