From 7ec9fcbafc41e99a5af2cb22364dc74af2258db3 Mon Sep 17 00:00:00 2001 From: scheinriese Date: Thu, 23 Apr 2026 14:42:05 +0200 Subject: [PATCH] feat: keyboard-navigate Custom tab tiles Wire the Custom tab into the picker's arrow-key navigation: Text, Avatar, and Image tiles now gain a blue ring when highlighted by keyboard and commit on Enter, matching Icons/Emojis. custom-tab-cp destructures the already-plumbed :highlighted-id from opts and emits data-item-id + conditional .is-highlighted on each button. CSS neutralizes the generic button.is-highlighted (which would paint the label column) and moves the ring onto the 48x48 preview child. A baseline transparent outline avoids the currentColor flash when the ring appears. --- src/main/frontend/components/icon.cljs | 18 ++++++++++++++---- src/main/frontend/components/icon.css | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/main/frontend/components/icon.cljs b/src/main/frontend/components/icon.cljs index 16c74467d9..c68eebe746 100644 --- a/src/main/frontend/components/icon.cljs +++ b/src/main/frontend/components/icon.cljs @@ -1571,12 +1571,16 @@ :data {:value avatar-value :backgroundColor backgroundColor :color color}}) - on-chosen (:on-chosen opts)] + on-chosen (:on-chosen opts) + highlighted-id (:highlighted-id opts)] [:div.custom-tab-content ;; Text option (when text-item [:button.custom-tab-item - {:on-click #(reset! *view :text-picker)} + {:data-item-id "custom-text" + :tabIndex "-1" + :class (when (= "custom-text" highlighted-id) "is-highlighted") + :on-click #(reset! *view :text-picker)} [:div.custom-tab-item-preview (icon text-item {:size 24})] [:span.custom-tab-item-label "Text"]]) @@ -1584,14 +1588,20 @@ ;; Avatar option (when avatar-item [:button.custom-tab-item - {:on-click #(on-chosen % avatar-item)} + {:data-item-id "custom-avatar" + :tabIndex "-1" + :class (when (= "custom-avatar" highlighted-id) "is-highlighted") + :on-click #(on-chosen % avatar-item)} [:div.custom-tab-item-preview (icon avatar-item {:size 24})] [:span.custom-tab-item-label "Avatar"]]) ;; Image option — always show dashed placeholder with camera icon [:button.custom-tab-item - {:on-click #(reset! *view :asset-picker)} + {:data-item-id "custom-image" + :tabIndex "-1" + :class (when (= "custom-image" highlighted-id) "is-highlighted") + :on-click #(reset! *view :asset-picker)} [:div.custom-tab-item-preview [:span.image-tile-placeholder {:style {:width 28 diff --git a/src/main/frontend/components/icon.css b/src/main/frontend/components/icon.css index 4d597f6d53..891f19a119 100644 --- a/src/main/frontend/components/icon.css +++ b/src/main/frontend/components/icon.css @@ -430,15 +430,40 @@ @apply transition-all duration-150; border: 1px solid var(--rx-gray-06); background-color: var(--rx-gray-03); + /* Baseline transparent outline so outline-color can transition + transparent → accent without the browser's currentColor flashing + through during the discrete style/width jump. */ + outline: 2px solid transparent; + outline-offset: -2px; &:hover { border-color: var(--rx-accent-09); } } +/* Keyboard-nav highlight for custom tiles: neutralize the generic + button.is-highlighted rule (which paints the full column including + the label) and move the ring onto the 48x48 preview child instead. */ +.cp__emoji-icon-picker button.custom-tab-item.is-highlighted { + background-color: transparent !important; + outline: none; + border-radius: 0 !important; +} + +.cp__emoji-icon-picker button.custom-tab-item.is-highlighted .custom-tab-item-preview { + background-color: var(--lx-accent-04); + outline: 2px solid var(--lx-accent-09); + outline-offset: -2px; +} + +.cp__emoji-icon-picker button.custom-tab-item.is-highlighted .custom-tab-item-label { + color: var(--lx-accent-11); +} + .custom-tab-item-label { @apply text-xs; color: var(--rx-gray-11); + transition: color 150ms; } /* Shared drag overlay hint — used by both icon picker and asset picker */