Skip to content

Commit 197c806

Browse files
committed
docs(public-docsite-v9-headless): add Browser support overview page
Adds an Overview/Browser support page with a data-driven support matrix (Baseline status + browser versions from web-features), feature->component mapping, and per-feature usage/fallback guidance including the focusgroup polyfill.
1 parent 58b8ed9 commit 197c806

17 files changed

Lines changed: 750 additions & 0 deletions

apps/public-docsite-v9-headless/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"postbuild-storybook": "yarn generate-llms-docs",
1010
"postbuild-storybook:docsite": "yarn postbuild-storybook",
1111
"generate-llms-docs": "yarn storybook-llms-extractor --distPath ./dist/storybook --summaryBaseUrl \"https://storybooks.fluentui.dev/headless/\" --summaryTitle \"Fluent UI React Headless Components\" --summaryDescription \"Fluent UI React headless components provide unstyled, accessible component primitives that can be styled with any CSS approach.\"",
12+
"generate-browser-support": "node -r ../../scripts/ts-node/src/register ../../scripts/web-features/src/generate.ts --output src/BrowserSupport/browser-support-data.generated.json --features popover,dialog,focusgroup,anchor-positioning=css.properties.anchor-name,anchor-name=anchor-positioning::css.properties.anchor-name,position-area=anchor-positioning::css.properties.position-area,position-try-fallbacks=anchor-positioning::css.properties.position-try-fallbacks,anchor-center=anchor-positioning::css.properties.place-self.anchor-center",
1213
"start": "yarn storybook:docs",
1314
"storybook": "storybook dev --port 3000",
1415
"storybook:docs": "yarn storybook --docs"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Meta } from '@storybook/addon-docs/blocks';
2+
3+
import { BrowserSupportMatrix } from './BrowserSupport/BrowserSupportMatrix';
4+
5+
<Meta title="Overview/Browser support" />
6+
7+
# Browser support
8+
9+
Fluent UI Headless components build their overlays on modern web-platform features — the
10+
**Popover API**, the native **`<dialog>` element**, and **CSS anchor positioning** — instead of
11+
JavaScript-managed portals and z-index stacks. This keeps overlays in the browser's top layer with
12+
native light-dismiss, focus handling, and accessibility.
13+
14+
These features are at different stages of [Baseline](https://web.dev/baseline) availability. Every
15+
component that uses them **detects support at runtime** (via `CSS.supports(...)`) and **degrades
16+
gracefully** where a feature is missing — falling back to `dialog.show()` or inline rendering and, in
17+
development, logging a warning that recommends a polyfill.
18+
19+
**Reading the matrix:**
20+
21+
- **Widely available** — Baseline for 2.5+ years; safe to rely on without a polyfill.
22+
- **Newly available** — Baseline in the current major browsers; a polyfill may be needed for older versions.
23+
- **Limited availability** — not yet Baseline; the JavaScript fallback keeps the component usable.
24+
25+
To support older browsers, supply a polyfill for the relevant feature where needed.
26+
27+
<BrowserSupportMatrix />
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import * as React from 'react';
2+
3+
import {
4+
browsers,
5+
browserLabel,
6+
features,
7+
featureLabel,
8+
MATRIX_ORDER,
9+
CONCEPT_ORDER,
10+
FEATURE_DETAILS,
11+
COMPONENT_FEATURES,
12+
MDN_LINKS,
13+
getAvailabilityLevel,
14+
getStatusLabel,
15+
generatedFrom,
16+
WEB_FEATURES_URL,
17+
type AvailabilityLevel,
18+
} from '.';
19+
import styles from './browserSupport.module.css';
20+
21+
const AVAILABILITY_TEXT: Record<AvailabilityLevel, string> = {
22+
widely: 'Widely available',
23+
newly: 'Newly available',
24+
limited: 'Limited availability',
25+
};
26+
27+
/** All tracked components, sorted, for the component → feature matrix rows. */
28+
const ALL_COMPONENTS = Object.keys(COMPONENT_FEATURES).sort();
29+
30+
/** Render `backtick`-delimited segments as inline `<code>`, linking known CSS properties to MDN. */
31+
function renderRichText(text: string): React.ReactNode {
32+
return text.split('`').map((part, index) => {
33+
if (index % 2 === 0) {
34+
return part;
35+
}
36+
const href = MDN_LINKS[part];
37+
if (href) {
38+
return (
39+
<a key={index} className={styles.codeLink} href={href} target="_blank" rel="noreferrer">
40+
<code className={styles.code}>{part}</code>
41+
</a>
42+
);
43+
}
44+
return (
45+
<code key={index} className={styles.code}>
46+
{part}
47+
</code>
48+
);
49+
});
50+
}
51+
52+
export const BrowserSupportMatrix = (): React.ReactNode => {
53+
return (
54+
<div className={styles.root}>
55+
<table className={styles.table}>
56+
<caption>Baseline status and minimum supporting browser versions</caption>
57+
<thead>
58+
<tr>
59+
<th scope="col">Feature</th>
60+
<th scope="col">Availability</th>
61+
{browsers.map(browser => (
62+
<th scope="col" key={browser}>
63+
{browserLabel(browser)}
64+
</th>
65+
))}
66+
</tr>
67+
</thead>
68+
<tbody>
69+
{MATRIX_ORDER.map(key => {
70+
const feature = features[key];
71+
const level = getAvailabilityLevel(feature);
72+
return (
73+
<tr key={key}>
74+
<th scope="row">{featureLabel(key)}</th>
75+
<td>
76+
<span className={`${styles.badge} ${styles[level]}`}>{AVAILABILITY_TEXT[level]}</span>
77+
<div className={styles.since}>{getStatusLabel(key)}</div>
78+
</td>
79+
{browsers.map(browser => {
80+
const version = feature.support[browser];
81+
return (
82+
<td key={browser} className={version ? styles.version : styles.unsupported}>
83+
{version ?? 'No'}
84+
</td>
85+
);
86+
})}
87+
</tr>
88+
);
89+
})}
90+
</tbody>
91+
</table>
92+
93+
<h2>How each feature is used</h2>
94+
<div className={styles.usage}>
95+
{CONCEPT_ORDER.map(key => {
96+
const details = FEATURE_DETAILS[key];
97+
return (
98+
<section className={styles.usageItem} key={key}>
99+
<h3 className={styles.usageTitle}>{featureLabel(key)}</h3>
100+
<div className={styles.usageText}>{renderRichText(details.usage)}</div>
101+
<div className={styles.fallback}>
102+
<span className={styles.fallbackLabel}>Fallback: </span>
103+
{renderRichText(details.fallback)}
104+
</div>
105+
<a className={styles.mdnLink} href={details.referenceUrl} target="_blank" rel="noreferrer">
106+
Reference ↗
107+
</a>
108+
</section>
109+
);
110+
})}
111+
</div>
112+
113+
<h2>Feature usage by component</h2>
114+
<table className={styles.table}>
115+
<thead>
116+
<tr>
117+
<th scope="col">Component</th>
118+
{CONCEPT_ORDER.map(key => (
119+
<th scope="col" key={key}>
120+
{featureLabel(key)}
121+
</th>
122+
))}
123+
</tr>
124+
</thead>
125+
<tbody>
126+
{ALL_COMPONENTS.map(component => (
127+
<tr key={component}>
128+
<th scope="row">{component}</th>
129+
{CONCEPT_ORDER.map(key => {
130+
const uses = COMPONENT_FEATURES[component].includes(key);
131+
return (
132+
<td key={key} className={styles.check} aria-label={uses ? 'Yes' : 'No'}>
133+
{uses ? '✓' : ''}
134+
</td>
135+
);
136+
})}
137+
</tr>
138+
))}
139+
</tbody>
140+
</table>
141+
142+
<div className={styles.provenance}>
143+
Generated with{' '}
144+
<a className={styles.provenanceLink} href={WEB_FEATURES_URL} target="_blank" rel="noreferrer">
145+
{generatedFrom}
146+
</a>
147+
</div>
148+
</div>
149+
);
150+
};
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"generatedFrom": "web-features@3.30.0",
3+
"browsers": [
4+
"chrome",
5+
"edge",
6+
"firefox",
7+
"safari"
8+
],
9+
"features": {
10+
"popover": {
11+
"key": "popover",
12+
"name": "Popover",
13+
"baseline": "low",
14+
"baselineLowDate": "2025-01-27",
15+
"baselineHighDate": null,
16+
"partial": false,
17+
"representativeBaseline": null,
18+
"representativeBaselineLowDate": null,
19+
"support": {
20+
"chrome": "116",
21+
"edge": "116",
22+
"firefox": "125",
23+
"safari": "17"
24+
}
25+
},
26+
"dialog": {
27+
"key": "dialog",
28+
"name": "<dialog>",
29+
"baseline": "high",
30+
"baselineLowDate": "2022-03-14",
31+
"baselineHighDate": "2024-09-14",
32+
"partial": false,
33+
"representativeBaseline": null,
34+
"representativeBaselineLowDate": null,
35+
"support": {
36+
"chrome": "37",
37+
"edge": "79",
38+
"firefox": "98",
39+
"safari": "15.4"
40+
}
41+
},
42+
"focusgroup": {
43+
"key": "focusgroup",
44+
"name": "focusgroup",
45+
"baseline": false,
46+
"baselineLowDate": null,
47+
"baselineHighDate": null,
48+
"partial": false,
49+
"representativeBaseline": null,
50+
"representativeBaselineLowDate": null,
51+
"support": {
52+
"chrome": null,
53+
"edge": null,
54+
"firefox": null,
55+
"safari": null
56+
}
57+
},
58+
"anchor-positioning": {
59+
"key": "anchor-positioning",
60+
"name": "Anchor positioning",
61+
"baseline": false,
62+
"baselineLowDate": null,
63+
"baselineHighDate": null,
64+
"partial": true,
65+
"representativeBaseline": "low",
66+
"representativeBaselineLowDate": "2026-01-13",
67+
"support": {
68+
"chrome": "125",
69+
"edge": "125",
70+
"firefox": "147",
71+
"safari": "26"
72+
}
73+
},
74+
"anchor-name": {
75+
"key": "anchor-name",
76+
"name": "anchor-name",
77+
"baseline": "low",
78+
"baselineLowDate": "2026-01-13",
79+
"baselineHighDate": null,
80+
"partial": false,
81+
"representativeBaseline": null,
82+
"representativeBaselineLowDate": null,
83+
"support": {
84+
"chrome": "125",
85+
"edge": "125",
86+
"firefox": "147",
87+
"safari": "26"
88+
}
89+
},
90+
"position-area": {
91+
"key": "position-area",
92+
"name": "position-area",
93+
"baseline": "low",
94+
"baselineLowDate": "2026-01-13",
95+
"baselineHighDate": null,
96+
"partial": false,
97+
"representativeBaseline": null,
98+
"representativeBaselineLowDate": null,
99+
"support": {
100+
"chrome": "129",
101+
"edge": "129",
102+
"firefox": "147",
103+
"safari": "26"
104+
}
105+
},
106+
"position-try-fallbacks": {
107+
"key": "position-try-fallbacks",
108+
"name": "position-try-fallbacks",
109+
"baseline": "low",
110+
"baselineLowDate": "2026-01-13",
111+
"baselineHighDate": null,
112+
"partial": false,
113+
"representativeBaseline": null,
114+
"representativeBaselineLowDate": null,
115+
"support": {
116+
"chrome": "128",
117+
"edge": "128",
118+
"firefox": "147",
119+
"safari": "26"
120+
}
121+
},
122+
"anchor-center": {
123+
"key": "anchor-center",
124+
"name": "anchor-center",
125+
"baseline": "low",
126+
"baselineLowDate": "2026-01-13",
127+
"baselineHighDate": null,
128+
"partial": false,
129+
"representativeBaseline": null,
130+
"representativeBaselineLowDate": null,
131+
"support": {
132+
"chrome": "125",
133+
"edge": "125",
134+
"firefox": "147",
135+
"safari": "26"
136+
}
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)