Commit Graph
2 Commits
Author SHA1 Message Date
Matthijsanddermotduffy e6bb0eb0e0 fix: claim pointer focus without a visible focus ring (#2733)
Since f240646 (#2639, first released in v8.0.0) the card claims focus on
any `pointerdown` inside it, so that `key` triggers receive their
keyboard events (`keyboard-state-manager.ts`). The claim is a script
call, `element.focus({ preventScroll: true })`, and script-initiated
focus comes with the browser's focus indicator: after a pointer press
while focus was outside the card, the card matches `:focus-visible` and
Chromium draws its default ring around the entire card (measured:
`outline: auto 1px rgb(238, 238, 238)`, a bright line on a dark
dashboard). The ring then persists until focus leaves the card, which
users experience as a white border that appears intermittently when they
click or tap the card.

Ordinary dashboard cards are unaffected because they rely on the
browser's native pointer focus, which shows no indicator. Isolated in
the same browser, a plain `tabindex` element gains focus from a click
without matching `:focus-visible`, while `focus()` from script does
match it. v7 did not claim focus at all, so it never showed this.

**Change:** pass the intent along with the claim: `element.focus({
preventScroll: true, focusVisible: false })`. This code path only runs
for pointer interaction, where no indicator is wanted. Keyboard focus
does not pass through it: tabbing to the card keeps its ring, and the
`key` trigger support from #2639 is unchanged. Browsers without
`FocusOptions.focusVisible` ignore the option and simply keep today's
behaviour. (`focusVisible` is not yet in the bundled TypeScript DOM
types, hence the small global augmentation.)

**Verification:**

- Unit test asserts the focus claim carries `focusVisible: false`.
- Measured in Chromium 152 on a live dashboard: before, a pointer press
on the card leaves it `:focus-visible` with the UA default ring; after,
the same press focuses the card without one, and reaching the card with
Tab still shows the ring. In the same browser, `focus({ focusVisible:
false })` verifiably suppresses `:focus-visible` where a plain `focus()`
sets it.
- `yarn run test`, `yarn run test:browser` (chromium and firefox), `yarn
run lint` and `yarn run typecheck` pass. The webkit browser run fails
one focus test in this local environment, identically on unmodified
`main`, so it is unrelated to this change.

---------

Co-authored-by: dermotduffy <dermot.duffy@gmail.com>
2026-08-30 14:04:40 -07:00
Matthijsanddermotduffy fc7e09952f fix: vertically center icons within icon buttons (#2734)
Menu icons sit visibly high inside their round buttons in the iOS
companion app, and one pixel high everywhere else. The card's icon
wrapper (`advanced-camera-card-icon`) is slotted into `ha-icon-button`,
whose label renders slotted content inside a plain `<span>`. The wrapper
is an `inline-block`, so it rides the text baseline and leaves the
font's descender space underneath it: the span grows taller than the
icon (measured: 24.5px around a 22.5px icon under Roboto), the button's
flex layout centers the *span*, and the icon lands high by half the
descent.

That descent is a property of the active font, which is why the offset
differs per platform: about 1px under Roboto, and about 2px when the
font stack resolves to a system font, as on iOS, where it is clearly
visible inside the round menu button background. Slotted Home Assistant
icons do not show this because they are laid out block-level via
`--ha-icon-display`, which the card already sets; the card's own wrapper
element reintroduces the inline-block one level up.

**Change:** lay the wrapper out block-level wherever it is slotted into
`ha-icon-button` (`button.scss`, which also covers the submenu and
next/previous controls). Block layout has no baseline, so the label span
shrinks to exactly the icon and the icon centers.

**Verification:**

- The structure (flex button, label span, slotted icon) rebuilt
standalone in Playwright Chromium and WebKit: with an inline-block icon
the offset is 1px under Roboto and 1.75 to 2px under system fonts, in
both engines; with a block icon it is 0.0px in every engine, font, and
button size tried.
- Measured on a live HA 2026.8.3 dashboard (45px menu buttons): before,
every menu icon center sat 1px above its button center; after, 0.0px for
both a standard menu button and a conditional
`advanced-camera-card-menu-icon` element.
- `yarn run test`, `yarn run test:browser` (chromium), `yarn run lint`,
`yarn run typecheck` pass.

---------

Co-authored-by: dermotduffy <dermot.duffy@gmail.com>
2026-08-30 12:11:10 -07:00