Skip to content

Commit 7372841

Browse files
JasonVMoJason MorseCopilot
authored
Theming: high-contrast token consolidation (#4285)
* fix bad references in lockfile * consolidate theming utils * move dead packages to shim directory * add changeset * fix formatting * remove old references * update dependency profiles * add changeset for additional packages * add high contrast helpers --------- Co-authored-by: Jason Morse <jasonmorse@Jasons-M1-Max.local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3c9fa0d commit 7372841

14 files changed

Lines changed: 83 additions & 69 deletions

File tree

.changeset/kind-colors-share.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@fluentui-react-native/design": patch
3+
"@fluentui-react-native/theme-tokens": patch
4+
---
5+
6+
Share high-contrast alias token processing across Windows platforms from the design package and expose it through the theme-tokens compatibility shim.

.changeset/warm-themes-reuse.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@fluentui-react-native/win32-theme": patch
3+
---
4+
5+
Reuse high-contrast alias tokens from `@fluentui-react-native/design/tokens/legacy`.

apps/bundle-size/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@rnx-kit/metro-config": "catalog:",
3535
"@rnx-kit/metro-resolver-symlinks": "catalog:",
3636
"@rnx-kit/metro-serializer-esbuild": "catalog:",
37+
"@rnx-kit/tools-filesystem": "^0.2.0",
3738
"metro": "^0.83.1",
3839
"oxc-resolver": "catalog:"
3940
},

apps/bundle-size/scripts/measure.mjs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { readJSONFileSync, writeJSONFileSync } from '@rnx-kit/tools-filesystem';
12
import { spawnSync } from 'node:child_process';
23
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
34
import { mkdir } from 'node:fs/promises';
@@ -7,7 +8,7 @@ import { gzipSync } from 'node:zlib';
78

89
const workspaceRoot = dirname(dirname(fileURLToPath(import.meta.url)));
910
const repositoryRoot = dirname(dirname(workspaceRoot));
10-
const yarnVersion = JSON.parse(readFileSync(join(repositoryRoot, 'package.json'), 'utf8')).packageManager.split('@')[1];
11+
const yarnVersion = readJSONFileSync(join(repositoryRoot, 'package.json')).packageManager.split('@')[1];
1112
const yarnPath = join(repositoryRoot, '.yarn', 'releases', `yarn-${yarnVersion}.cjs`);
1213
const configPath = join(workspaceRoot, 'scenarios.json');
1314
const defaultBaselinePath = join(workspaceRoot, 'baseline.json');
@@ -68,7 +69,7 @@ function getWorkspacePackage(source) {
6869
while (directory.startsWith(packagesRoot)) {
6970
const manifestPath = join(directory, 'package.json');
7071
if (existsSync(manifestPath)) {
71-
return JSON.parse(readFileSync(manifestPath, 'utf8')).name;
72+
return readJSONFileSync(manifestPath).name;
7273
}
7374
directory = dirname(directory);
7475
}
@@ -153,8 +154,8 @@ function runBundle(platform, scenario, resetCache) {
153154
}
154155

155156
const bundle = readFileSync(bundlePath);
156-
const sourceMap = JSON.parse(readFileSync(sourceMapPath, 'utf8'));
157-
const metafile = JSON.parse(readFileSync(metafilePath, 'utf8'));
157+
const sourceMap = readJSONFileSync(sourceMapPath);
158+
const metafile = readJSONFileSync(metafilePath);
158159
const contributions = getWorkspaceContributions(metafile);
159160

160161
return {
@@ -263,7 +264,7 @@ const {
263264
if (updateBaseline && selectedPlatforms) {
264265
throw new Error('Baseline updates must include every configured platform; omit --platform');
265266
}
266-
const selectedConfig = JSON.parse(readFileSync(selectedConfigPath, 'utf8'));
267+
const selectedConfig = readJSONFileSync(selectedConfigPath);
267268
const platforms = selectedPlatforms ?? selectedConfig.platforms;
268269

269270
await mkdir(entryRoot, { recursive: true });
@@ -284,12 +285,10 @@ const currentBaseline = {
284285
results: measurements.map(baselineResult),
285286
};
286287
if (updateBaseline) {
287-
writeFileSync(selectedBaselinePath, `${JSON.stringify(currentBaseline, null, 2)}\n`);
288+
writeJSONFileSync(selectedBaselinePath, currentBaseline);
288289
}
289290

290-
const baseline = existsSync(selectedBaselinePath)
291-
? JSON.parse(readFileSync(selectedBaselinePath, 'utf8'))
292-
: { schemaVersion: 1, results: [] };
291+
const baseline = existsSync(selectedBaselinePath) ? readJSONFileSync(selectedBaselinePath) : { schemaVersion: 1, results: [] };
293292
if (baseline.schemaVersion !== 1) {
294293
throw new Error(`Unsupported baseline schema version: ${baseline.schemaVersion}`);
295294
}
@@ -315,7 +314,7 @@ const report = {
315314
};
316315
const reportPath = join(outputRoot, 'results.json');
317316
const markdownReportPath = join(outputRoot, 'report.md');
318-
writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
317+
writeJSONFileSync(reportPath, report);
319318
writeFileSync(markdownReportPath, createMarkdownReport(results));
320319

321320
console.table(results);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { processAliasTokens, transformWin32PlatformColorName, transformWindowsPlatformColorName } from '../processAliasTokens';
2+
3+
jest.mock('react-native', () => ({
4+
PlatformColor: (color: string) => `PlatformColor('${color}')`,
5+
}));
6+
7+
const createAliasTokens = () => ({
8+
colors: {
9+
buttonFace: 'PlatformColor(ButtonFace)',
10+
},
11+
});
12+
13+
it('maps Windows platform colors to SystemColor names', () => {
14+
expect(processAliasTokens(createAliasTokens(), transformWindowsPlatformColorName)).toEqual({
15+
colors: {
16+
buttonFace: "PlatformColor('SystemColorButtonFaceColor')",
17+
},
18+
});
19+
});
20+
21+
it('preserves raw Win32 platform color names', () => {
22+
expect(processAliasTokens(createAliasTokens(), transformWin32PlatformColorName)).toEqual({
23+
colors: {
24+
buttonFace: "PlatformColor('ButtonFace')",
25+
},
26+
});
27+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { PlatformColor } from 'react-native';
2+
3+
type AliasTokens = Record<string, Record<string, unknown>>;
4+
type PlatformColorNameTransform = (color: string) => string;
5+
6+
export const transformWindowsPlatformColorName: PlatformColorNameTransform = (color) => `SystemColor${color}Color`;
7+
export const transformWin32PlatformColorName: PlatformColorNameTransform = (color) => color;
8+
9+
export function processAliasTokens<T extends AliasTokens>(aliasTokens: T, transformColorName: PlatformColorNameTransform): T {
10+
// The imported token JSON is intentionally mutated once when its module loads.
11+
for (const key in aliasTokens) {
12+
const tokenGroup: Record<string, unknown> = aliasTokens[key];
13+
for (const innerKey in tokenGroup) {
14+
const entry = tokenGroup[innerKey];
15+
if (typeof entry === 'string' && entry.includes('PlatformColor')) {
16+
const color = transformColorName(entry.substring(14, entry.length - 1));
17+
// eslint-disable-next-line @react-native/platform-colors
18+
tokenGroup[innerKey] = PlatformColor(color);
19+
}
20+
}
21+
}
22+
23+
return aliasTokens;
24+
}
Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
1-
import { PlatformColor } from 'react-native';
2-
31
import aliasTokens from '@fluentui-react-native/design-tokens-win32/hc/tokens-aliases.json';
42

5-
export const hcAliasTokens = processAliasTokens(aliasTokens);
6-
7-
function processAliasTokens(aliasTokens: any) {
8-
for (const key in aliasTokens) {
9-
for (const innerKey in aliasTokens[key]) {
10-
const entry = aliasTokens[key][innerKey];
11-
if (typeof entry === 'string' && entry.includes('PlatformColor')) {
12-
const color = 'SystemColor' + entry.substring(14, entry.length - 1) + 'Color';
13-
// eslint-disable-next-line @react-native/platform-colors
14-
aliasTokens[key][innerKey] = PlatformColor(color);
15-
}
16-
}
17-
}
3+
import { processAliasTokens, transformWindowsPlatformColorName } from './processAliasTokens';
184

19-
return aliasTokens;
20-
}
5+
export const hcAliasTokens = processAliasTokens(aliasTokens, transformWindowsPlatformColorName);
Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
1-
import { PlatformColor } from 'react-native';
2-
31
import aliasTokens from '@fluentui-react-native/design-tokens-win32/hc/tokens-aliases.json';
42

5-
export const hcAliasTokens = processAliasTokens(aliasTokens);
6-
7-
function processAliasTokens(aliasTokens: any) {
8-
for (const key in aliasTokens) {
9-
for (const innerKey in aliasTokens[key]) {
10-
const entry = aliasTokens[key][innerKey];
11-
if (typeof entry === 'string' && entry.includes('PlatformColor')) {
12-
const color = entry.substring(14, entry.length - 1);
13-
// eslint-disable-next-line @react-native/platform-colors
14-
aliasTokens[key][innerKey] = PlatformColor(color);
15-
}
16-
}
17-
}
3+
import { processAliasTokens, transformWin32PlatformColorName } from './processAliasTokens';
184

19-
return aliasTokens;
20-
}
5+
export const hcAliasTokens = processAliasTokens(aliasTokens, transformWin32PlatformColorName);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default as globalTokens } from './tokens-global';
22
export { getAliasTokens, getShadowTokens } from './getTokens';
3+
export { hcAliasTokens } from './highContrast/tokens-alias';
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Theme-tokens
22

3-
> **Deprecated:** Import `globalTokens`, `getAliasTokens`, and `getShadowTokens`
4-
> from `@fluentui-react-native/design/tokens/legacy` instead.
3+
> **Deprecated:** Import `globalTokens`, `getAliasTokens`, `getShadowTokens`,
4+
> and `hcAliasTokens` from `@fluentui-react-native/design/tokens/legacy`
5+
> instead.
56
67
This package is a compatibility shim and will not receive new APIs. It preserves
78
the existing entry point while consumers migrate to the design package.

0 commit comments

Comments
 (0)