feat: add focus-visible rings and refactor accordion chevron

Add global :focus-visible outline style and migrate form components
from :focus box-shadow to :focus-visible outline. Refactor accordion
chevron from CSS ::after pseudo-element to explicit span element.
This commit is contained in:
Florian Schroedl
2026-03-05 11:34:07 +01:00
parent aa3370565f
commit e3787363d2
4 changed files with 50 additions and 27 deletions

View File

@@ -26,7 +26,9 @@
base-attrs (cond-> (merge {:class classes} attrs)
open (assoc :open true))]
[:details base-attrs
[:summary {:class "accordion-trigger"} title]
[:summary {:class "accordion-trigger"}
[:span {:class "accordion-trigger-text"} title]
[:span {:class "accordion-chevron" :aria-hidden "true"}]]
(into [:div {:class "accordion-content"}] children)])
:cljs
@@ -35,7 +37,9 @@
base-attrs (cond-> (merge {:class classes} attrs)
open (assoc :open true))]
[:details base-attrs
[:summary {:class ["accordion-trigger"]} title]
[:summary {:class ["accordion-trigger"]}
[:span {:class ["accordion-trigger-text"]} title]
[:span {:class ["accordion-chevron"] :aria-hidden "true"}]]
(into [:div {:class ["accordion-content"]}] children)])
:clj
@@ -44,5 +48,7 @@
base-attrs (cond-> (merge {:class classes} attrs)
open (assoc :open true))]
[:details base-attrs
[:summary {:class "accordion-trigger"} title]
[:summary {:class "accordion-trigger"}
[:span {:class "accordion-trigger-text"} title]
[:span {:class "accordion-chevron" :aria-hidden "true"}]]
(into [:div {:class "accordion-content"}] children)])))

View File

@@ -9,6 +9,10 @@
overflow: hidden;
}
.accordion-trigger:focus-visible {
outline-offset: -2px;
}
.accordion + .accordion {
margin-top: -1px;
border-top-left-radius: 0;
@@ -25,7 +29,6 @@
align-items: center;
justify-content: space-between;
gap: var(--size-2);
width: 100%;
padding: var(--size-4);
font-weight: 500;
font-size: inherit;
@@ -50,15 +53,20 @@
background: var(--bg-1);
}
.accordion-trigger::after {
content: "";
.accordion-trigger-text {
flex: 1;
text-align: left;
}
.accordion-chevron {
display: block;
width: 1em;
height: 1em;
flex-shrink: 0;
background-color: currentColor;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
mask-size: contain;
mask-repeat: no-repeat;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
transition: transform 150ms ease;
}
@@ -66,7 +74,7 @@
border-bottom: var(--border-0);
}
.accordion[open] > .accordion-trigger::after {
.accordion[open] > .accordion-trigger .accordion-chevron {
transform: rotate(180deg);
}

View File

@@ -63,6 +63,17 @@
margin: 0;
background: var(--bg-0);
color: var(--fg-0);
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
box-shadow: none;
}
:focus:not(:focus-visible) {
outline: none;
box-shadow: none;
}")
(defn collect-component-css

View File

@@ -23,10 +23,10 @@
border-color: var(--danger);
}
.form-field--error .form-input:focus,
.form-field--error .form-textarea:focus,
.form-field--error .form-select:focus {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 20%, transparent);
.form-field--error .form-input:focus-visible,
.form-field--error .form-textarea:focus-visible,
.form-field--error .form-select:focus-visible {
outline-color: var(--danger);
}
/* ── Label ─────────────────────────────────────────────────────── */
@@ -60,12 +60,11 @@
color: var(--fg-2);
}
.form-input:focus,
.form-textarea:focus,
.form-select:focus {
outline: none;
.form-input:focus-visible,
.form-textarea:focus-visible,
.form-select:focus-visible {
border-color: var(--accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent);
outline-offset: -1px;
}
.form-input:disabled,
@@ -81,9 +80,9 @@
border-color: var(--danger);
}
.form-input--error:focus,
.form-textarea--error:focus {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 20%, transparent);
.form-input--error:focus-visible,
.form-textarea--error:focus-visible {
outline-color: var(--danger);
}
/* ── Textarea ──────────────────────────────────────────────────── */
@@ -148,10 +147,9 @@
background-repeat: no-repeat;
}
.form-checkbox:focus,
.form-radio:focus {
outline: none;
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent);
.form-checkbox:focus-visible,
.form-radio:focus-visible {
outline-offset: 0;
}
.form-checkbox:disabled,