Skip to content

The focus concept emits :focus-within in Web Components and :focus-visible in React #754

Open

Description

@nathanacurtis

Problem

The same declared state concept produces a different CSS selector in React than in Web Components, and the documentation presents one of them as the contract.

A library declaring the focus concept gets:

  • React — :focus-visible, which is what the settings reference documents.
  • Web Components — :focus-within, and no :focus-visible anywhere.

Measured on one library's emitted output from a single run with one config: 41 occurrences of :focus-visible across the React stylesheets, 41 occurrences of :focus-within across the Web Components stylesheets, and zero of the other in each.

These are not equivalent. :focus-within matches while focus sits anywhere inside the element, including after a mouse click; :focus-visible matches only when the browser decides a focus ring is warranted. A library that declared focus and verified it in React gets visibly different behaviour on the Web Components side — a focus ring appearing on click where it should not — and nothing says so.

The concept table also offers focus-within as a separate concept, which is what someone would declare if they wanted that behaviour. So on the Web Components side, declaring either concept appears to produce the same result, which makes one of them unreachable.

Potential solution(s)

  • Make the Web Components emitter match the documented concept, so focus emits :focus-visible narrowed the way the React side narrows it, and focus-within stays the way to ask for :focus-within.
  • If the divergence is deliberate — because a shadow root changes what a focus selector can reach — document it on the states settings page as a per-platform difference, and say which concept to declare for which result on each platform. Right now the page reads as one contract for both.

Acceptance criteria

  • Declaring focus produces the same focus semantics in both emitted trees, or the difference is documented on the states settings page with the reason.
  • Declaring focus and declaring focus-within produce different output on the Web Components side, so both concepts are reachable.
  • The states settings page's selector table is true of every platform it covers, or names the platform each row applies to.

Case data

  • Fixture: Checkbox, Text field, Toggle — any component whose focus styling sits on a wrapper rather than the control
  • Workspace: atlassian, during a from-scratch onboarding run; surfaced while verifying that a configured states block reached the emitted CSS
  • Territory: react-from-specs, webcomponents-from-specs, docs
  • Impacted file(s): the focus-concept selector emission in webcomponents-from-specs, from-specs/src/states.ts if the narrowing is shared, site/src/content/docs/settings/states.md
  • Size: s to m

Related Docs

Notes

Found by verifying every row of a configured states block against both emitted trees rather than sampling one. Nine of eleven concepts emitted identically on both platforms; this was the only divergence.


Implementation details are tracked internally.

Activity

  1. added
    react-from-specsTransformer from specs into React code
    docsDocumentation that appears on specsplugin.com
    on Oct 13, 2026
  2. moved this from Ready to Backlog in Specson Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

docsDocumentation that appears on specsplugin.comreact-from-specsTransformer from specs into React codewebcomponents-from-specsTransformer from specs into web components code

Type

Fields

Priority

None yet

Projects

  • Status
    Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions