Skip to content

fix(react-provider): validate serialized theme tokens - #36713

Open
Paul Mardling (PaulGMardling) wants to merge 5 commits into
microsoft:masterfrom
PaulGMardling:fix/theme-css-hardening
Open

fix(react-provider): validate serialized theme tokens#36713
Paul Mardling (PaulGMardling) wants to merge 5 commits into
microsoft:masterfrom
PaulGMardling:fix/theme-css-hardening

Conversation

@PaulGMardling

@PaulGMardling Paul Mardling (PaulGMardling) commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Previous Behavior

createCSSRuleFromTheme serialized theme entries without verifying that each token name and value remained within one CSS custom-property declaration. A malformed entry could invalidate declarations that followed it, and the theming documentation did not describe the trust boundary for dynamic theme data.

New Behavior

createCSSRuleFromTheme now accepts CSS escape-aware custom-property names and structurally contains each token value before serialization. Valid CSS syntax remains unchanged, including colors, font-family fallbacks, quoted strings, comments, nested functions, gradients, calculations, case-insensitive escaped URL forms, hash and at-keyword token contexts, other escapes, and multiline whitespace. Angle-bracket and delimiter processing consume input linearly while preserving CSS escape parity and mixed-block recovery. Malformed lexical contexts are repaired at the token boundary so later declarations remain independently parseable; unsupported token names are omitted with a development-only warning that identifies the name without including its value.

Regression coverage exercises exported themes, escaped custom-token names, compatibility syntax, long backslash and delimiter runs, malformed-entry isolation, client stylesheet insertion, and server-rendered style output. The theming documentation clarifies that applications must validate dynamic data against an application-specific schema before constructing a theme.

Validation

  • yarn nx run react-provider:test (126 tests, 14 snapshots)
  • yarn nx run react-provider:lint
  • yarn nx run react-provider:type-check
  • yarn nx run react-provider:format:check
  • yarn nx run react-provider:build
  • yarn beachball check
  • FluentProvider bundle comparison: 22.083 kB minified / 8.491 kB gzip, +1.997 kB / +790 B against master; below the repository threshold

Related Issue(s)

  • None.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

Pull request demo site: URL

@@ -112,6 +112,10 @@ export const customDarkTheme = createDarkTheme(customBrandRamp);

@github-actions github-actions Bot Sep 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Menu Converged - submenuIndicator slotted content 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 404 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default.submenus open.chromium.png 413 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 613 Changed
vr-tests-react-components/Positioning.Positioning end.chromium.png 622 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 50 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 2 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 93 Changed
vr-tests-react-components/TagPicker 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled.disabled input hover.chromium.png 677 Changed

There were 2 duplicate changes discarded. Check the build logs for more information.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-components
react-components: Button, FluentProvider & webLightTheme
67.471 kB
19.465 kB
69.468 kB
20.312 kB
1.997 kB
847 B
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
227.136 kB
68.41 kB
229.133 kB
69.248 kB
1.997 kB
838 B
react-components
react-components: FluentProvider & webLightTheme
40.694 kB
13.549 kB
42.691 kB
14.347 kB
1.997 kB
798 B
react-components
react-components: entire library
1.283 MB
322.086 kB
1.285 MB
322.836 kB
2.001 kB
750 B
react-provider
FluentProvider
20.086 kB
7.701 kB
22.083 kB
8.491 kB
1.997 kB
790 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-avatar
Avatar
48.256 kB
15.276 kB
react-avatar
AvatarGroup
17.269 kB
6.89 kB
react-avatar
AvatarGroupItem
61.24 kB
19.139 kB
react-breadcrumb
@fluentui/react-breadcrumb - package
103.667 kB
29.079 kB
react-card
Card - All
94.104 kB
27.352 kB
react-card
Card
86.7 kB
25.469 kB
react-card
CardFooter
12.663 kB
5.028 kB
react-card
CardHeader
15.21 kB
5.877 kB
react-card
CardPreview
12.74 kB
5.142 kB
react-charts
AreaChart
406.902 kB
127.183 kB
react-charts
DeclarativeChart
758.446 kB
222.689 kB
react-charts
DonutChart
317.733 kB
97.977 kB
react-charts
FunnelChart
309.133 kB
94.775 kB
react-charts
GanttChart
390.032 kB
121.663 kB
react-charts
GaugeChart
317.083 kB
97.402 kB
react-charts
GroupedVerticalBarChart
398.115 kB
124.454 kB
react-charts
HeatMapChart
392.172 kB
122.702 kB
react-charts
HorizontalBarChart
297.409 kB
90.473 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
Legends
236.596 kB
71.053 kB
react-charts
LineChart
418.251 kB
130.314 kB
react-charts
PolarChart
345.588 kB
108.005 kB
react-charts
SankeyChart
213.797 kB
68.722 kB
react-charts
ScatterChart
397.575 kB
124.369 kB
react-charts
Sparkline
80.888 kB
26.864 kB
react-charts
VerticalBarChart
434.566 kB
129.339 kB
react-charts
VerticalStackedBarChart
404.13 kB
125.845 kB
react-checkbox
Checkbox
30.781 kB
10.914 kB
react-color-picker
ColorArea
31.032 kB
11.706 kB
react-color-picker
ColorPicker
16.049 kB
6.438 kB
react-color-picker
ColorSlider
26.093 kB
10.232 kB
react-combobox
Combobox (including child components)
139.318 kB
45.067 kB
react-combobox
Dropdown (including child components)
139.083 kB
44.818 kB
react-components
react-components: all base hooks
217.893 kB
68.338 kB
react-datepicker-compat
DatePicker Compat
217.447 kB
64.407 kB
react-dialog
Dialog (including children components)
90.984 kB
28.254 kB
react-field
Field
22.311 kB
8.312 kB
react-headless-components-preview
react-headless-components-preview: entire library
241.736 kB
68.14 kB
react-headless-components-preview
@fluentui/react-headless-components-preview/tag-picker
54.012 kB
17.756 kB
react-headless-components-preview
@fluentui/react-headless-components-preview/teaching-popover
36.073 kB
12.006 kB
react-input
Input
26.24 kB
8.643 kB
react-list
List
75.545 kB
23.408 kB
react-list
ListItem
99.06 kB
30.273 kB
react-menu
Menu (including children components)
160.788 kB
51.11 kB
react-menu
Menu (including selectable components)
163.954 kB
51.739 kB
react-message-bar
MessageBar (all components)
25.293 kB
8.946 kB
react-overflow
hooks only
10.203 kB
3.694 kB
react-persona
Persona
55.209 kB
17.22 kB
react-popover
Popover
126.6 kB
40.873 kB
react-portal
Portal
13.844 kB
5.392 kB
react-portal-compat
PortalCompatProvider
5.341 kB
2.146 kB
react-positioning
usePositioning
28.985 kB
10.198 kB
react-positioning
useSafeZoneArea
13.398 kB
5.365 kB
react-progress
ProgressBar
20.217 kB
7.836 kB
react-radio
Radio
28.166 kB
9.157 kB
react-radio
RadioGroup
13.931 kB
5.613 kB
react-select
Select
26.109 kB
9.403 kB
react-slider
Slider
33.405 kB
11.51 kB
react-spinbutton
SpinButton
33.661 kB
11.013 kB
react-swatch-picker
@fluentui/react-swatch-picker - package
93.186 kB
27.608 kB
react-switch
Switch
33.402 kB
10.566 kB
react-table
DataGrid
147.809 kB
43.977 kB
react-table
Table (Primitives only)
38.042 kB
12.69 kB
react-table
Table as DataGrid
119.68 kB
33.755 kB
react-table
Table (Selection only)
66.557 kB
18.957 kB
react-table
Table (Sort only)
65.22 kB
18.58 kB
react-tag-picker
@fluentui/react-tag-picker - package
174.763 kB
54.56 kB
react-tags
InteractionTag
13.629 kB
5.386 kB
react-tags
Tag
29.542 kB
9.338 kB
react-tags
TagGroup
70.806 kB
21.811 kB
react-teaching-popover
TeachingPopover
101.77 kB
32.204 kB
react-textarea
Textarea
24.606 kB
8.912 kB
react-timepicker-compat
TimePicker
142.037 kB
46.44 kB
react-toast
Toast (including Toaster)
91.488 kB
28.541 kB
react-tooltip
Tooltip
54.69 kB
19.443 kB
react-tree
FlatTree
136.693 kB
40.821 kB
react-tree
PersonaFlatTree
138.556 kB
41.361 kB
react-tree
PersonaTree
134.637 kB
40.143 kB
react-tree
Tree
132.78 kB
39.638 kB
🤖 This report was generated against a5b8341b24ed78137d2eeb4e7514b01071c6d471

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

1 participant