From a26463d4ad9bb6460c1b4a633be3e6d00ad2d7ef Mon Sep 17 00:00:00 2001 From: Yevheniia Sorokina Date: Mon, 28 Sep 2026 11:12:03 +0100 Subject: [PATCH 1/2] fix(feature-list): render a framework without an API reference as a non-link A framework cell with no apiUrl rendered an with no href: withCurrentDocsPath returns undefined, React omits the attribute, and the chip stayed a link that is not keyboard-focusable, navigates nowhere and still shows the link hover. 16 cells in features.json reach that branch. It now renders a with a dashed border, a dimmed colour token that keeps AA contrast (the old opacity rule composited to 3.19:1), and a visually hidden "(not available)" so screen readers get the same signal. The framework column's container, which was display: none, gets a minimal wrapping layout so the chips are legible. Latent today: all 50 FeatureList usages pass displayMode="compact", which skips this column. Split out of #418, which is being closed. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/components/FeatureList/index.tsx | 47 ++++++++++++++++---- src/components/FeatureList/styles.module.css | 45 +++++++++++++++++-- 2 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/components/FeatureList/index.tsx b/src/components/FeatureList/index.tsx index 570cf628df..5185879a27 100644 --- a/src/components/FeatureList/index.tsx +++ b/src/components/FeatureList/index.tsx @@ -200,15 +200,44 @@ const FeatureList: React.FC = ({ {Object.entries(feature.frameworks) .filter(([, info]) => info.version !== 'n/a') .map(([frameworkName, frameworkInfo]) => ( - - {frameworkName} v{frameworkInfo.version} - + // The ELEMENT changes, not just the href. Guarding the + // href alone rendered the same DOM - withCurrentDocsPath + // returns undefined for undefined and React omits the + // attribute - so the chip stayed an with no href: + // not keyboard-focusable, navigating nowhere, still + // showing the link hover. 17 cells in features.json + // carry no apiUrl, of which 16 reach this branch - the + // 17th is Web / 7-Segment Display, filtered out earlier + // by version === "n/a". + // Latent only while every usage is compact mode, which + // skips this column. Same shape as the feature-name cell + // above, which swaps for

. + frameworkInfo.apiUrl ? ( + + {frameworkName} v{frameworkInfo.version} + + ) : ( + + {frameworkName} v{frameworkInfo.version} + {/* The dashed border and the dimmed token say "unavailable" + to someone who can see them. Without this the chip + announces exactly what an available one announces - + "iOS v1.2.3" - so a screen-reader user gets no signal + that this one is different and does not link anywhere. */} + (not available) + + ) ))} diff --git a/src/components/FeatureList/styles.module.css b/src/components/FeatureList/styles.module.css index 7e41c35f1b..1c29f473db 100644 --- a/src/components/FeatureList/styles.module.css +++ b/src/components/FeatureList/styles.module.css @@ -87,8 +87,18 @@ } +/* The chips carry padding and a border but no margin, and React renders the + .map() output with no whitespace between siblings, so without a container + layout they sit flush against one another. This rule was `display: none` + from the file's first commit - the column has never rendered, because all 50 + FeatureList usages pass displayMode="compact", which takes a different path. + Hiding the content while the component still emits its + Available Frameworks header was incoherent; this is the smallest + layout that makes the column legible if that view is ever used. */ .frameworkList { - display: none; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; } .frameworkItem { @@ -112,14 +122,41 @@ text-decoration: none; } +/* Reachable by a screen reader, absent from the visual layout. Not + `display: none` or `visibility: hidden`, which remove it from the + accessibility tree as well and would defeat the point. */ +.visuallyHidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* No API reference for this framework yet. Dimmed through the border and a + colour token rather than `opacity`, which composited the label to 3.19:1 in + light mode - below AA - while looking merely faint. Measured against the + BUILT css rather than assumed token values: 5.29:1 light, 12.26:1 dark. + `border-style: dashed` carries the state without relying on colour. */ .frameworkItemUnavailable { - opacity: 0.5; - cursor: not-allowed; + color: var(--ifm-color-emphasis-700); + border-style: dashed; + cursor: default; } +/* Hold the dimmed colour on hover. This rule exists to cancel + .frameworkItem:hover, but it was left over from the opacity version and + restored --ifm-color-content - the full-contrast colour - so hovering an + unavailable chip made it look available, with only the dashed border + still carrying the state. The span is not interactive; cursor: default + already says so. */ .frameworkItemUnavailable:hover { background: var(--ifm-color-emphasis-100); - color: var(--ifm-color-content); + color: var(--ifm-color-emphasis-700); border-color: var(--ifm-color-emphasis-200); } From 2a7c9fb28b4dce7e0d9c85d7a6cdf24ea8dc32cc Mon Sep 17 00:00:00 2001 From: Yevheniia Sorokina Date: Fri, 9 Oct 2026 12:49:30 +0100 Subject: [PATCH 2/2] Describe a chip without an API reference accurately to screen readers - The hidden text says "(no API reference)" instead of "(not available)": every chip in this column is a framework that supports the feature. - Drop aria-disabled: a plain span is not a control, and ARIA 1.2 does not allow the attribute on generic elements. - Shorten the title to "No API reference" so screen readers don't announce the name and version twice. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/components/FeatureList/index.tsx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/FeatureList/index.tsx b/src/components/FeatureList/index.tsx index 5185879a27..ca07601f53 100644 --- a/src/components/FeatureList/index.tsx +++ b/src/components/FeatureList/index.tsx @@ -226,16 +226,21 @@ const FeatureList: React.FC = ({ is not a control, and ARIA 1.2 does not allow the + attribute on generic elements. */ + title="No API reference" > {frameworkName} v{frameworkInfo.version} - {/* The dashed border and the dimmed token say "unavailable" - to someone who can see them. Without this the chip - announces exactly what an available one announces - - "iOS v1.2.3" - so a screen-reader user gets no signal - that this one is different and does not link anywhere. */} - (not available) + {/* The dashed border and the dimmed token show sighted readers + that this chip has no link. Without this text the chip + announces exactly what a linked one announces - "iOS v1.2.3". + It says "no API reference", not "not available": every chip in + this column is a framework that DOES support the feature, so + "not available" would read as "this feature isn't on iOS". */} + (no API reference) ) ))}