🐛 Make the dashboard template cards keyboard buttons (#12710)

A template card was an <a> without href inside a focusable div: Tab
reached the div, and neither Enter nor Space imported the template, so
the cards could not be used from the keyboard. Importing is an action,
so the card is now a <button> with the same look; it takes the tab
index the div had, and the manual Enter handler goes away.

Keyboard focus looks like hover (accent border and "Add to your
project").

The templates visual test gives 0 px of difference at rest, on hover and
for the scroll and collapse buttons; a new shot covers the focus.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-11-05 11:31:33 +01:00 committed by GitHub
parent 686b47742d
commit f827b7b4a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 23 additions and 18 deletions

View File

@ -897,6 +897,13 @@ test.describe("Templates section controls", () => {
await card.hover();
await expect(card).toHaveScreenshot(shot);
// Focused from the keyboard, a card looks like on hover.
await page.mouse.move(640, 100);
await page.keyboard.press("Shift");
await card.focus();
await expect(card).toHaveScreenshot(shot);
await card.blur();
const moveButtons = section.locator("[class*='move-button']");
await moveButtons.last().hover();
await expect(moveButtons.last()).toHaveScreenshot(shot);

View File

@ -98,26 +98,22 @@
(mf/use-fn
(mf/deps on-import)
(fn [event]
(on-import item event)))
on-key-down
(mf/use-fn
(mf/deps on-import)
(fn [event]
(when (kbd/enter? event)
(dom/stop-propagation event)
(on-import item event))))]
(on-import item event)))]
[:div {:class (stl/css :card-container)
:tab-index (if (or (not is-visible) collapsed) "-1" "0")
:id id
:data-index index}
[:a {:class (stl/css :template-card)
:on-click on-click
:on-mouse-down dom/prevent-default
:on-mouse-enter #(reset! hover? true)
:on-mouse-leave #(reset! hover? false)
:on-key-down on-key-down}
;; Importing the template is an action: a button, which already
;; handles Enter and Space.
[:button {:type "button"
:class (stl/css :template-card)
:tab-index (if (or (not is-visible) collapsed) "-1" "0")
:on-click on-click
:on-mouse-down dom/prevent-default
:on-mouse-enter #(reset! hover? true)
:on-mouse-leave #(reset! hover? false)
:on-focus #(reset! hover? true)
:on-blur #(reset! hover? false)}
[:div {:class (stl/css :img-container)}
[:img {:src (dm/str href)
:alt (:name item)

View File

@ -187,9 +187,11 @@
padding: 0 var(--sp-xs) var(--sp-s) var(--sp-xs);
border-radius: $br-8;
border: $b-2 solid transparent;
background-color: transparent;
&:hover {
text-decoration: none;
&:hover,
&:focus-visible {
outline: none;
border-color: var(--color-accent-primary);
.download-icon {