Summary
Generate alias, shadow, and Flex default values from the Fluent design token
packages instead of maintaining literal tables by hand. Extend the existing
codegen and shared-constant factoring so values are de-duplicated across both
platforms and appearances and remain tree-shakeable.
The repository already has the required prebuild entry point and
de-duplication machinery for global tokens; this task applies that model to the
remaining theme data.
Goal
Generate the default theme values from the Fluent design tokens instead of
hand-maintaining them, and de-duplicate values that are shared across themes so
each value is declared once and unused values can be dropped from a bundle.
Stage
Stages 2 and 3. Stage 2 replaces the hand-authored defaults with generated
ones; Stage 3 completes cross-appearance de-duplication and records the measured
bundle result.
Why it matters
- Observed. The Flex defaults are hand-authored.
defaultTokens.ts
is 473 lines and contains 249 color literals with only 80 distinct values,
because the color, color.hover, and color.pressed maps each restate the
full token list even when the value does not change between states.
- Observed. Only one appearance is generated at all.
codegen.cts reads
android/light, ios/light, macos/light, win32/colorful, and
windows/light
(scripts/codegen.cts),
while the installed token packages ship more variants: macOS provides light,
dark, hclight, and hcdark, and win32 provides colorful, darkgray,
hc, and black (Observed in the resolved
@fluentui-react-native/design-tokens-macos@0.53.0 and
@fluentui-react-native/design-tokens-win32@0.53.0 packages; the catalog pins
^0.53.0 in .yarnrc.yml).
- Observed. Non-generated theme values are loaded as whole JSON objects at
module scope. theme-tokens/src/getTokens.ts
imports the windows light and dark alias and shadow JSON files plus the win32
high-contrast shadow JSON, then selects between them at runtime. For macOS the
on-disk sizes are 6,088 bytes for light/tokens-aliases.json and 6,089 bytes
for dark/tokens-aliases.json, with byte-identical 22,681-byte
tokens-global.json files in both directories (Observed in the resolved
package).
- Inferred. Whole-object JSON imports cannot be tree-shaken per token, so
every consumer pays for every alias value of every bundled appearance. The
existing constant-per-token codegen output does not have that property, which
is why global.generated.ts states in its header that it "allows references
to direct constants which can be minified and statically analyzed and removed
if unused."
Observed current state
- Observed.
codegen.cts runs as the design package's prebuild script
(package.json) and calls
processPlatformJsonFiles and outputCodegenFile from
@fluentui-react-native/scripts.
- Observed.
scripts/src/codegen/json.ts
implements the de-duplication: it flattens nested JSON into prefixed constant
names, treats win32 as the default platform written to the un-suffixed file,
pulls constants common to all platforms into the common entry with
extractCommonFromPlatforms, and factors group-shared constants into
desktop, mobile, win, and apple subset files with
extractConstsUpstream, "so each value is declared once."
- Observed. The generated output is
src/tokens/global.generated.ts
(879 lines of export const color, size, radius, stroke, and font constants)
plus src/tokens/generated
containing global.platform.ts, four global.platform.<platform>.ts files,
and four global.subset.<group>.ts files that re-export shared values.
- Observed.
global.generated.ts is exposed publicly through the
./tokens/global export condition.
- Observed.
defaultTokens.ts mixes two styles: non-color groups reference
the generated constants (cornerRadius20, size40, fontLineHeight300),
while colors are inline literals. nonFluentFlexTokens holds the values that
have no Fluent source and is layered under defaultFlexTokens.
- Observed.
flex-token-map.yaml records, per Flex token, both the generic
source and the Fluent source (global.*, alias.*, shadow.* prefixes
against @fluentui-react-native/design-tokens-win32/colorful), and documents
the matching rules used for spacing, radius, stroke, font size, line height,
font weight, and font family
(flex-token-map.yaml).
It also states that nonFluentFlexTokens must be hand-synchronized with the
unmapped destinations.
Upstream de-duplication model (x3-design/fluent-design at d334acf)
- Observed. x3 solves the exact problem this task names, by layering rather
than by per-theme value tables. dev/web/flex-tokens/mappings.json holds 640
primitives (574 distinct values) shared by every mode, and 264 generics that
reference them by name.
- Observed. Of those 264 generics, 142 carry a single mode-invariant
primitive reference and 116 carry a primitive_light/primitive_dark pair;
6 are layered composites. No generic stores a literal color per mode, so a
value used by both light and dark exists once in the primitive table.
- Observed. Interaction values are the one place upstream does store a
per-mode literal pair: mappings.interaction has 118 entries, each with a
light and a dark hex, and they are emitted only into the opt-in
interaction-fallback*.css files rather than into the base theme.
- Inferred. A FURN generator that emits a shared primitive constant table
plus per-appearance reference tables reproduces the upstream structure and
satisfies the "shared across themes are not duplicated" requirement, and it
composes with the platform and platform-group factoring that
scripts/src/codegen/json.ts already performs.
Scope
- Extend the codegen to alias and shadow token JSON in addition to global token
JSON.
- Generate the Flex default token sets from the Fluent token packages using the
mapping recorded in flex-token-map.yaml, replacing the hand-authored literal
tables in defaultTokens.ts.
- Add appearance as a de-duplication axis so a value shared by light, dark, and
high contrast is declared once and referenced from each appearance file,
matching the existing platform and platform-group behavior.
- Keep emitting individual
export const bindings rather than object literals
wherever a value can be referenced directly, so unused values can be dropped.
- Preserve the documented provenance: generated files must remain traceable to
the mapping file and the source token package.
- Record a bundle-size baseline before the change and the resulting size after.
Out of scope
Deliverables
- Extended codegen in
packages/agentic/design/scripts/codegen.cts
and, where the logic is shared, in
scripts/src/codegen, covering alias and
shadow tokens and multiple appearances.
- Generated Flex default value modules replacing the hand-authored literal
tables in defaultTokens.ts.
- Appearance-aware de-duplication that emits shared values once.
- Tests for the codegen de-duplication behavior, including an appearance case.
- A recorded before-and-after size measurement for a representative bundle.
- Changesets for the design package and, if changed,
@fluentui-react-native/scripts.
Acceptance criteria
Dependencies and ordering
Risks and open decisions
- Open decision. Which appearances to generate. Observed: the installed
packages ship macOS light/dark/hclight/hcdark and win32
colorful/darkgray/hc/black, and AppearanceOptions in
Theme.types.ts
names light, dark, darkElevated, and highContrast; the two sets do not
correspond one to one.
- Open decision. Whether the generated defaults keep win32
colorful as the
default platform and appearance. Observed: json.ts hard-codes
defaultPlatform = 'win32', and AGENTS.md directs
using Win32, macOS, or Windows as the canonical cross-platform reference
rather than iOS.
- Open decision. Whether
theme-tokens should be re-pointed at the
generated constants as part of this task or left to
Package Consolidation. Its module-scope JSON
imports are the main remaining whole-object load.
- Risk. Generating from
flex-token-map.yaml requires the mapping to be
complete and current; the file itself documents that some destinations have no
Fluent equivalent (null entries, letterSpacing, serif font family) and that
synchronization is manual today.
- Risk. Aggressive de-duplication across appearances can produce re-export
chains that are correct at type level but confusing to read; the existing
subset files are the precedent to follow for readability.
- Risk. Upstream is alpha and pins are exact by instruction
(@x3-design/flex-tokens@0.9.0, @x3-design/flex-themes@0.8.0). If FURN
generates from upstream structure, the source commit should be recorded so
drift is detectable; see the
suggested addition on upstream drift.
- Risk. Bundle-size claims are unverifiable without a baseline. If no
repeatable measurement is established, the production criterion cannot be
closed.
Evidence and references
Summary
Generate alias, shadow, and Flex default values from the Fluent design token
packages instead of maintaining literal tables by hand. Extend the existing
codegen and shared-constant factoring so values are de-duplicated across both
platforms and appearances and remain tree-shakeable.
The repository already has the required prebuild entry point and
de-duplication machinery for global tokens; this task applies that model to the
remaining theme data.
Goal
Generate the default theme values from the Fluent design tokens instead of
hand-maintaining them, and de-duplicate values that are shared across themes so
each value is declared once and unused values can be dropped from a bundle.
Stage
Stages 2 and 3. Stage 2 replaces the hand-authored defaults with generated
ones; Stage 3 completes cross-appearance de-duplication and records the measured
bundle result.
Why it matters
defaultTokens.tsis 473 lines and contains 249 color literals with only 80 distinct values,
because the
color,color.hover, andcolor.pressedmaps each restate thefull token list even when the value does not change between states.
codegen.ctsreadsandroid/light,ios/light,macos/light,win32/colorful, andwindows/light(
scripts/codegen.cts),while the installed token packages ship more variants: macOS provides
light,dark,hclight, andhcdark, and win32 providescolorful,darkgray,hc, andblack(Observed in the resolved@fluentui-react-native/design-tokens-macos@0.53.0and@fluentui-react-native/design-tokens-win32@0.53.0packages; the catalog pins^0.53.0in.yarnrc.yml).module scope.
theme-tokens/src/getTokens.tsimports the windows light and dark alias and shadow JSON files plus the win32
high-contrast shadow JSON, then selects between them at runtime. For macOS the
on-disk sizes are 6,088 bytes for
light/tokens-aliases.jsonand 6,089 bytesfor
dark/tokens-aliases.json, with byte-identical 22,681-bytetokens-global.jsonfiles in both directories (Observed in the resolvedpackage).
every consumer pays for every alias value of every bundled appearance. The
existing constant-per-token codegen output does not have that property, which
is why
global.generated.tsstates in its header that it "allows referencesto direct constants which can be minified and statically analyzed and removed
if unused."
Observed current state
codegen.ctsruns as the design package'sprebuildscript(
package.json) and callsprocessPlatformJsonFilesandoutputCodegenFilefrom@fluentui-react-native/scripts.scripts/src/codegen/json.tsimplements the de-duplication: it flattens nested JSON into prefixed constant
names, treats
win32as the default platform written to the un-suffixed file,pulls constants common to all platforms into the common entry with
extractCommonFromPlatforms, and factors group-shared constants intodesktop,mobile,win, andapplesubset files withextractConstsUpstream, "so each value is declared once."src/tokens/global.generated.ts(879 lines of
export constcolor, size, radius, stroke, and font constants)plus
src/tokens/generatedcontaining
global.platform.ts, fourglobal.platform.<platform>.tsfiles,and four
global.subset.<group>.tsfiles that re-export shared values.global.generated.tsis exposed publicly through the./tokens/globalexport condition.defaultTokens.tsmixes two styles: non-color groups referencethe generated constants (
cornerRadius20,size40,fontLineHeight300),while colors are inline literals.
nonFluentFlexTokensholds the values thathave no Fluent source and is layered under
defaultFlexTokens.flex-token-map.yamlrecords, per Flex token, both the genericsource and the Fluent source (
global.*,alias.*,shadow.*prefixesagainst
@fluentui-react-native/design-tokens-win32/colorful), and documentsthe matching rules used for spacing, radius, stroke, font size, line height,
font weight, and font family
(
flex-token-map.yaml).It also states that
nonFluentFlexTokensmust be hand-synchronized with theunmapped destinations.
Upstream de-duplication model (x3-design/fluent-design at
d334acf)than by per-theme value tables.
dev/web/flex-tokens/mappings.jsonholds 640primitives (574 distinct values) shared by every mode, and 264 generics that
reference them by name.
primitivereference and 116 carry aprimitive_light/primitive_darkpair;6 are layered composites. No generic stores a literal color per mode, so a
value used by both light and dark exists once in the primitive table.
per-mode literal pair:
mappings.interactionhas 118 entries, each with alightand adarkhex, and they are emitted only into the opt-ininteraction-fallback*.cssfiles rather than into the base theme.plus per-appearance reference tables reproduces the upstream structure and
satisfies the "shared across themes are not duplicated" requirement, and it
composes with the platform and platform-group factoring that
scripts/src/codegen/json.tsalready performs.Scope
JSON.
mapping recorded in
flex-token-map.yaml, replacing the hand-authored literaltables in
defaultTokens.ts.high contrast is declared once and referenced from each appearance file,
matching the existing platform and platform-group behavior.
export constbindings rather than object literalswherever a value can be referenced directly, so unused values can be dropped.
the mapping file and the source token package.
Out of scope
System Appearance Handling.
Runtime Color Utilities.
FlexTokenstype shape; that is owned byDynamic Theme Building.
@fluentui-react-native/design-tokens-*versions.Deliverables
packages/agentic/design/scripts/codegen.ctsand, where the logic is shared, in
scripts/src/codegen, covering alias andshadow tokens and multiple appearances.
tables in
defaultTokens.ts.@fluentui-react-native/scripts.Acceptance criteria
yarn prebuildin the design package regenerates every default valuemodule, and re-running it produces no diff.
packages/agentic/design/src/tokens; values that genuinely have no Fluentsource are either generated from a checked-in source file or explicitly
listed with a recorded reason.
or referenced, verified by a codegen test.
drop when unused.
unchanged, verified by existing design and component tests.
after, using
yarn bundle:repoor another cited repeatable command.yarn build,yarn lage test, andyarn lage lintpass at therepository root, and changesets are present.
Dependencies and ordering
shape of the default token set and the Flex context type.
files land in their final submodule.
among the generated per-appearance sets.
the interaction states that have no authored source value.
Risks and open decisions
packages ship macOS
light/dark/hclight/hcdarkand win32colorful/darkgray/hc/black, andAppearanceOptionsinTheme.types.tsnames
light,dark,darkElevated, andhighContrast; the two sets do notcorrespond one to one.
colorfulas thedefault platform and appearance. Observed:
json.tshard-codesdefaultPlatform = 'win32', andAGENTS.mddirectsusing Win32, macOS, or Windows as the canonical cross-platform reference
rather than iOS.
theme-tokensshould be re-pointed at thegenerated constants as part of this task or left to
Package Consolidation. Its module-scope JSON
imports are the main remaining whole-object load.
flex-token-map.yamlrequires the mapping to becomplete and current; the file itself documents that some destinations have no
Fluent equivalent (
nullentries,letterSpacing, serif font family) and thatsynchronization is manual today.
chains that are correct at type level but confusing to read; the existing
subset files are the precedent to follow for readability.
(
@x3-design/flex-tokens@0.9.0,@x3-design/flex-themes@0.8.0). If FURNgenerates from upstream structure, the source commit should be recorded so
drift is detectable; see the
suggested addition on upstream drift.
repeatable measurement is established, the production criterion cannot be
closed.
Evidence and references
packages/agentic/design/scripts/codegen.cts: current codegen entry point and its single-variant inputs.scripts/src/codegen/json.ts: existing flatten and de-duplication implementation.packages/agentic/design/src/tokens/global.generated.tsandsrc/tokens/generated: current generated output shape.packages/agentic/design/src/tokens/defaultTokens.ts: the hand-authored tables to replace.packages/agentic/design/src/tokens/mappings/flex-token-map.yaml: Fluent sources and matching rules per Flex token.packages/theming/theme-tokens/src/getTokens.ts: whole-object JSON loading pattern..yarnrc.yml: catalog pin of thedesign-tokens-*packages at^0.53.0.e37b04b: PR Add @fluentui-react-native/design package with some basic codegen for token constants #4140, introduced the token codegen.03ba7ef: PR Add basic flex tokens shape and mapping information #4153, added the Flex token shape and mapping.dev/web/flex-tokens/mappings.json: primitives, generics withprimitiveorprimitive_light/primitive_dark, and the interaction table.dev/web/flex-tokens/README.md: the data-layer split and the statement that primitives are never exported as JS constants.