Skip to content

Commit 4157768

Browse files
authored
Add appearance-aware Flex theme sources (#4295)
* appearance updates part one * remove win32 variants and update report
1 parent 59b6003 commit 4157768

101 files changed

Lines changed: 5067 additions & 676 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/fuzzy-themes-bridge.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@fluentui-react-native/design": minor
3+
"@fluentui-react-native/components": patch
4+
"@fluentui-react-native/default-theme": patch
5+
"@fluentui-react-native/android-theme": patch
6+
"@fluentui-react-native/apple-theme": patch
7+
"@fluentui-react-native/win32-theme": patch
8+
---
9+
10+
Add appearance-aware Flex theme sources with stable token identity and lazy compatibility bridges for legacy themes.

apps/bundle-size/baseline.json

Lines changed: 118 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -15,88 +15,90 @@
1515
{
1616
"platform": "macos",
1717
"scenario": "components-button",
18-
"rawBytes": 868023,
19-
"gzipBytes": 248334,
20-
"moduleCount": 550,
21-
"metroModuleCount": 546,
22-
"metafileInputCount": 753,
18+
"rawBytes": 871797,
19+
"gzipBytes": 249494,
20+
"moduleCount": 554,
21+
"metroModuleCount": 550,
22+
"metafileInputCount": 758,
2323
"workspaceModules": {
2424
"@fluentui-react-native/components": 10,
25-
"@fluentui-react-native/design": 17,
25+
"@fluentui-react-native/design": 21,
2626
"@fluentui-react-native/framework-base": 26
2727
},
2828
"workspaceBytes": {
29-
"@fluentui-react-native/components": 9480,
30-
"@fluentui-react-native/design": 26458,
29+
"@fluentui-react-native/components": 9482,
30+
"@fluentui-react-native/design": 30207,
3131
"@fluentui-react-native/framework-base": 11146
3232
}
3333
},
3434
{
3535
"platform": "macos",
3636
"scenario": "components-catalog",
37-
"rawBytes": 1090635,
38-
"gzipBytes": 301676,
39-
"moduleCount": 750,
40-
"metroModuleCount": 745,
41-
"metafileInputCount": 753,
37+
"rawBytes": 1094254,
38+
"gzipBytes": 302948,
39+
"moduleCount": 754,
40+
"metroModuleCount": 749,
41+
"metafileInputCount": 758,
4242
"workspaceModules": {
4343
"@fluentui-react-native/components": 98,
44-
"@fluentui-react-native/design": 17,
44+
"@fluentui-react-native/design": 21,
4545
"@fluentui-react-native/framework-base": 26
4646
},
4747
"workspaceBytes": {
48-
"@fluentui-react-native/components": 122538,
49-
"@fluentui-react-native/design": 26642,
48+
"@fluentui-react-native/components": 122383,
49+
"@fluentui-react-native/design": 30391,
5050
"@fluentui-react-native/framework-base": 12813
5151
}
5252
},
5353
{
54-
"platform": "win32",
55-
"scenario": "shell",
56-
"rawBytes": 825849,
57-
"gzipBytes": 236958,
58-
"moduleCount": 497,
59-
"metroModuleCount": 495,
60-
"metafileInputCount": 497,
61-
"workspaceModules": {},
62-
"workspaceBytes": {}
54+
"platform": "macos",
55+
"scenario": "design-theme-state",
56+
"rawBytes": 854603,
57+
"gzipBytes": 243954,
58+
"moduleCount": 541,
59+
"metroModuleCount": 535,
60+
"metafileInputCount": 542,
61+
"workspaceModules": {
62+
"@fluentui-react-native/design": 18,
63+
"@fluentui-react-native/framework-base": 26
64+
},
65+
"workspaceBytes": {
66+
"@fluentui-react-native/design": 30289,
67+
"@fluentui-react-native/framework-base": 3413
68+
}
6369
},
6470
{
65-
"platform": "win32",
66-
"scenario": "components-button",
67-
"rawBytes": 873381,
68-
"gzipBytes": 248809,
69-
"moduleCount": 551,
70-
"metroModuleCount": 547,
71-
"metafileInputCount": 754,
71+
"platform": "macos",
72+
"scenario": "design-theme-state-legacy-source",
73+
"rawBytes": 856701,
74+
"gzipBytes": 244382,
75+
"moduleCount": 543,
76+
"metroModuleCount": 537,
77+
"metafileInputCount": 545,
7278
"workspaceModules": {
73-
"@fluentui-react-native/components": 10,
74-
"@fluentui-react-native/design": 18,
79+
"@fluentui-react-native/design": 20,
7580
"@fluentui-react-native/framework-base": 26
7681
},
7782
"workspaceBytes": {
78-
"@fluentui-react-native/components": 9482,
79-
"@fluentui-react-native/design": 26495,
80-
"@fluentui-react-native/framework-base": 11146
83+
"@fluentui-react-native/design": 32361,
84+
"@fluentui-react-native/framework-base": 3413
8185
}
8286
},
8387
{
84-
"platform": "win32",
85-
"scenario": "components-catalog",
86-
"rawBytes": 1096000,
87-
"gzipBytes": 302161,
88-
"moduleCount": 751,
89-
"metroModuleCount": 746,
90-
"metafileInputCount": 754,
88+
"platform": "macos",
89+
"scenario": "design-legacy-theme-from-flex",
90+
"rawBytes": 923158,
91+
"gzipBytes": 257703,
92+
"moduleCount": 563,
93+
"metroModuleCount": 558,
94+
"metafileInputCount": 566,
9195
"workspaceModules": {
92-
"@fluentui-react-native/components": 98,
93-
"@fluentui-react-native/design": 18,
96+
"@fluentui-react-native/design": 32,
9497
"@fluentui-react-native/framework-base": 26
9598
},
9699
"workspaceBytes": {
97-
"@fluentui-react-native/components": 122554,
98-
"@fluentui-react-native/design": 26679,
99-
"@fluentui-react-native/framework-base": 12813
100+
"@fluentui-react-native/design": 61746,
101+
"@fluentui-react-native/framework-base": 4291
100102
}
101103
},
102104
{
@@ -113,40 +115,91 @@
113115
{
114116
"platform": "windows",
115117
"scenario": "components-button",
116-
"rawBytes": 877232,
117-
"gzipBytes": 249752,
118-
"moduleCount": 556,
119-
"metroModuleCount": 552,
120-
"metafileInputCount": 759,
118+
"rawBytes": 880864,
119+
"gzipBytes": 250891,
120+
"moduleCount": 560,
121+
"metroModuleCount": 556,
122+
"metafileInputCount": 764,
121123
"workspaceModules": {
122124
"@fluentui-react-native/components": 10,
123-
"@fluentui-react-native/design": 18,
125+
"@fluentui-react-native/design": 22,
124126
"@fluentui-react-native/framework-base": 26
125127
},
126128
"workspaceBytes": {
127-
"@fluentui-react-native/components": 9478,
128-
"@fluentui-react-native/design": 26530,
129+
"@fluentui-react-native/components": 9480,
130+
"@fluentui-react-native/design": 30142,
129131
"@fluentui-react-native/framework-base": 11146
130132
}
131133
},
132134
{
133135
"platform": "windows",
134136
"scenario": "components-catalog",
135-
"rawBytes": 1099607,
136-
"gzipBytes": 303049,
137-
"moduleCount": 756,
138-
"metroModuleCount": 751,
139-
"metafileInputCount": 759,
137+
"rawBytes": 1103086,
138+
"gzipBytes": 304267,
139+
"moduleCount": 760,
140+
"metroModuleCount": 755,
141+
"metafileInputCount": 764,
140142
"workspaceModules": {
141143
"@fluentui-react-native/components": 98,
142-
"@fluentui-react-native/design": 18,
144+
"@fluentui-react-native/design": 22,
143145
"@fluentui-react-native/framework-base": 26
144146
},
145147
"workspaceBytes": {
146-
"@fluentui-react-native/components": 122501,
147-
"@fluentui-react-native/design": 26714,
148+
"@fluentui-react-native/components": 122346,
149+
"@fluentui-react-native/design": 30326,
148150
"@fluentui-react-native/framework-base": 12813
149151
}
152+
},
153+
{
154+
"platform": "windows",
155+
"scenario": "design-theme-state",
156+
"rawBytes": 863667,
157+
"gzipBytes": 245363,
158+
"moduleCount": 547,
159+
"metroModuleCount": 541,
160+
"metafileInputCount": 548,
161+
"workspaceModules": {
162+
"@fluentui-react-native/design": 19,
163+
"@fluentui-react-native/framework-base": 26
164+
},
165+
"workspaceBytes": {
166+
"@fluentui-react-native/design": 30222,
167+
"@fluentui-react-native/framework-base": 3413
168+
}
169+
},
170+
{
171+
"platform": "windows",
172+
"scenario": "design-theme-state-legacy-source",
173+
"rawBytes": 865767,
174+
"gzipBytes": 245825,
175+
"moduleCount": 549,
176+
"metroModuleCount": 543,
177+
"metafileInputCount": 551,
178+
"workspaceModules": {
179+
"@fluentui-react-native/design": 21,
180+
"@fluentui-react-native/framework-base": 26
181+
},
182+
"workspaceBytes": {
183+
"@fluentui-react-native/design": 32294,
184+
"@fluentui-react-native/framework-base": 3413
185+
}
186+
},
187+
{
188+
"platform": "windows",
189+
"scenario": "design-legacy-theme-from-flex",
190+
"rawBytes": 932037,
191+
"gzipBytes": 259016,
192+
"moduleCount": 569,
193+
"metroModuleCount": 564,
194+
"metafileInputCount": 572,
195+
"workspaceModules": {
196+
"@fluentui-react-native/design": 33,
197+
"@fluentui-react-native/framework-base": 26
198+
},
199+
"workspaceBytes": {
200+
"@fluentui-react-native/design": 61490,
201+
"@fluentui-react-native/framework-base": 4291
202+
}
150203
}
151204
]
152205
}

apps/bundle-size/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"dependencies": {
1515
"@fluentui-react-native/components": "workspace:*",
16+
"@fluentui-react-native/design": "workspace:*",
1617
"@office-iss/react-native-win32": "^0.81.0",
1718
"@types/react": "~19.1.4",
1819
"react": "19.1.4",

apps/bundle-size/scenarios.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"platforms": ["macos", "win32", "windows"],
2+
"platforms": ["macos", "windows"],
33
"scenarios": [
44
{
55
"name": "shell"
@@ -13,6 +13,32 @@
1313
"name": "components-catalog",
1414
"module": "@fluentui-react-native/components",
1515
"namespace": true
16+
},
17+
{
18+
"name": "design-theme-state",
19+
"module": "@fluentui-react-native/design",
20+
"exports": ["FlexThemeReference", "ThemeProvider", "useThemeState"],
21+
"forbiddenInputPatterns": ["/theming/compat/", "/tokens/legacy/"]
22+
},
23+
{
24+
"name": "design-theme-state-legacy-source",
25+
"imports": [
26+
{
27+
"module": "@fluentui-react-native/design",
28+
"exports": ["ThemeProvider", "useThemeState"]
29+
},
30+
{
31+
"module": "@fluentui-react-native/design/theming/legacy-source",
32+
"exports": ["ThemeReference"]
33+
}
34+
],
35+
"forbiddenInputPatterns": ["/theming/compat/", "/tokens/legacy/"]
36+
},
37+
{
38+
"name": "design-legacy-theme-from-flex",
39+
"module": "@fluentui-react-native/design/theming",
40+
"exports": ["FlexThemeReference", "ThemeProvider", "useTheme"],
41+
"requiredInputPatterns": ["/theming/compat/themeFromFlexTokens.", "/tokens/legacy/"]
1642
}
1743
]
1844
}

apps/bundle-size/scripts/measure.mjs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,25 @@ function parseArgs(args) {
4141

4242
function createEntry(scenario) {
4343
const lines = [];
44-
45-
if (scenario.module) {
46-
if (scenario.namespace) {
47-
lines.push(`import * as bundleSizeTarget from ${JSON.stringify(scenario.module)};`);
48-
lines.push('globalThis.__bundleSizeTarget = bundleSizeTarget;');
49-
} else if (scenario.exports?.length) {
50-
lines.push(`import { ${scenario.exports.join(', ')} } from ${JSON.stringify(scenario.module)};`);
51-
lines.push(`globalThis.__bundleSizeTarget = [${scenario.exports.join(', ')}];`);
44+
const targets = [];
45+
46+
const imports = scenario.imports ?? (scenario.module ? [scenario] : []);
47+
for (const [index, moduleImport] of imports.entries()) {
48+
if (moduleImport.namespace) {
49+
const target = `bundleSizeTarget${index}`;
50+
lines.push(`import * as ${target} from ${JSON.stringify(moduleImport.module)};`);
51+
targets.push(target);
52+
} else if (moduleImport.exports?.length) {
53+
const specifiers = moduleImport.exports.map((name) => `${name} as bundleSizeTarget${index}_${name}`);
54+
lines.push(`import { ${specifiers.join(', ')} } from ${JSON.stringify(moduleImport.module)};`);
55+
targets.push(...moduleImport.exports.map((name) => `bundleSizeTarget${index}_${name}`));
5256
} else {
53-
throw new Error(`Scenario "${scenario.name}" must set "namespace" or "exports"`);
57+
throw new Error(`Scenario "${scenario.name}" imports must set "namespace" or "exports"`);
5458
}
5559
}
60+
if (targets.length) {
61+
lines.push(`globalThis.__bundleSizeTarget = [${targets.join(', ')}];`);
62+
}
5663

5764
const bootstrapPath = relative(entryRoot, join(workspaceRoot, 'src', 'bootstrap.js')).replaceAll('\\', '/');
5865
lines.push(`import ${JSON.stringify(bootstrapPath.startsWith('.') ? bootstrapPath : `./${bootstrapPath}`)};`, '');
@@ -158,6 +165,18 @@ function runBundle(platform, scenario, resetCache) {
158165
const bundle = readFileSync(bundlePath);
159166
const sourceMap = readJSONFileSync(sourceMapPath);
160167
const metafile = readJSONFileSync(metafilePath);
168+
const inputPaths = Object.keys(metafile.inputs).map((source) => source.replaceAll('\\', '/'));
169+
for (const pattern of scenario.forbiddenInputPatterns ?? []) {
170+
const match = inputPaths.find((source) => source.includes(pattern));
171+
if (match) {
172+
throw new Error(`Scenario "${scenario.name}" unexpectedly includes "${match}" (forbidden by "${pattern}")`);
173+
}
174+
}
175+
for (const pattern of scenario.requiredInputPatterns ?? []) {
176+
if (!inputPaths.some((source) => source.includes(pattern))) {
177+
throw new Error(`Scenario "${scenario.name}" does not include a module matching required pattern "${pattern}"`);
178+
}
179+
}
161180
const contributions = getWorkspaceContributions(metafile);
162181

163182
return {

0 commit comments

Comments
 (0)