Skip to content

fix(styles): flatten *-led nested focus-visible rules - #381

Draft
zrmarley wants to merge 1 commit into
mainfrom
zmarley/fix-focus-override-nesting
Draft

zrmarley wants to merge 1 commit into
mainfrom
zmarley/fix-focus-override-nesting

Conversation

@zrmarley

@zrmarley zrmarley commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Every focused element, including inputs that opt out of the global focus ring with .focus-override, shows a focus ring. In composer text inputs it appears as a grey square ring inside the input. Menu surfaces whose ring is meant to be suppressed also show one.

Cause

Tailwind v4.3.3 drops the whole parent selector when a selector that starts with the universal selector * uses & nesting:

*:not(body):not(.focus-override) { &:focus-visible { @apply ring-2 ...; } }
/* compiles to */
:focus-visible { --tw-ring-shadow: ... }

It isn't specific to @apply or :focus-visible. *:not(.b) { &:hover { color: red } } also compiles to a bare :hover, while .c { &:focus-visible { ... } } compiles correctly. globals.css had three of these *-led nested rules: the base ring, the menu-surface suppression, and .link. All three compiled into one bare :focus-visible { ... } block, and the last rule wins, so every focused element got the 2px .link ring.

Fix

  • Flatten the three rules to equivalent single selectors (e.g. *:not(body):not(.focus-override):focus-visible). Specificity and cascade order stay the same, because & on a single compound parent desugars to an equivalent :is().
  • Add a test in globals.test.ts that rejects & nesting in globals.css (the @custom-variant definition is allowed) and asserts the three flattened selectors, so the nested form can't come back while the upstream bug stands.

Verification

  • Compiled dist/assets/index-*.css now contains :not(body):not(.focus-override):focus-visible, ...menubar-sub-trigger]):focus-visible and the .link selector, and no bare }:focus-visible{.
  • The new test fails against main's globals.css and passes with the fix.
  • pnpm vitest run src/shared/styles and just check pass.
  • Accessibility: all 7 .focus-override uses were checked; none loses a real focus indicator. Five are tabIndex={-1} panels, one is a dialog with its own focus-visible:outline-none, and one is the global composer textarea, which keeps its caret and group-focus-within styling.

Screenshots

Synthetic renders in WebKit with the real globals.css and Desktop Agent composer component; no real user data.

Before (reproduction): the composer input picks up the global ring and shows a grey box inside the text field.
![Before: grey focus box inside the composer input](/Users/zmarley/goose artifacts/pr-screenshots/381-focus-before.png)

After: the .focus-override input has no ring, and an ordinary button still shows the global focus ring.
![After: composer input focused with no grey box; Send button keyboard-focused with normal ring](/Users/zmarley/goose artifacts/pr-screenshots/381-focus.png)

381-focus-before

381-focus

Tailwind v4.3.3 drops the whole parent selector when a selector that
starts with the universal selector (e.g. `*:not(body):not(.focus-override)`)
uses `&` nesting. The three nested focus-visible rules in globals.css all
collapsed into a single bare `:focus-visible { ... }` block, so every
focused element got the last rule's 2px `.link` ring — including
`.focus-override` opt-outs like composer inputs (a stray grey ring) and the
menu surfaces whose ring was meant to be suppressed.

Flatten the three rules to equivalent single selectors (same specificity
and cascade order) and add a test that keeps CSS nesting out of
globals.css while the upstream bug stands.
@zrmarley
zrmarley marked this pull request as ready for review October 8, 2026 20:04
@zrmarley
zrmarley requested a review from a team October 8, 2026 20:04

@morgmart morgmart left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 Automated code review

REQUEST_CHANGES: The exact PR comparison contains no publishable implementation findings. It changes visible keyboard focus indicators in Berd's graphical interface, but the supplied GitHub evidence contains no screenshots or short screen recording. All 11 supplied GitHub check runs completed successfully.

Deterministic publication result: 0 blocking and 0 non-blocking inline finding(s) publishable; 0 duplicate(s) suppressed; 1 blocking screenshot-evidence requirement(s) in this review body.

🤖 Blocking · Screenshots needed

This PR changes Berd’s graphical interface. Please add screenshots or a short screen recording so the visual result can be reviewed. Screenshots are review evidence; they do not replace accessibility, responsive, theme, localization, or behavior validation.

@zrmarley
zrmarley marked this pull request as draft October 8, 2026 21:07
@zrmarley

zrmarley commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Added before/after screenshots (synthetic renders, no user data) to the PR description per the review.

This branch has not been deployed

No deployments
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.

2 participants