mirror of
https://github.com/we-promise/sure.git
synced 2026-07-19 16:25:24 +00:00
feat(ds): one canonical focus ring across primitives (#2140)
* feat(ds): one canonical focus ring across primitives (#2136) Replaces the grab-bag of per-primitive focus indicators (neutral ring-alpha-black/white, outline-gray-900/white, faint form-field ring-4) with a single recipe — the #1737 accessibility follow-up. - New --color-focus-ring token: blue-600 (light) / blue-500 (dark), >=4:1 against both surfaces. - Canonical .focus-ring / .focus-ring-within in components.css: a 2px outline + 2px offset on :focus-visible only. Outline (not a box-shadow ring) so the offset gap is transparent on any surface with no layout shift; :focus-visible so it never shows for mouse/touch. - Applied to every focusable DS primitive: Button (had none), Link, Disclosure summary, Tabs nav, MenuItem (replaces the browser-default box), SearchInput, Tooltip trigger, Popover trigger, Select panel (focus-within), Toggle (peer-driven outline-focus-ring). .form-field adopts it via :focus-within, replacing the ~1:1 ring-4. - Dialog close button is a DS::Button icon variant, so it inherits the focus-visible-only ring and keeps no resting border (fixes "stuck ring"). Verified in-browser, light+dark: focus-visible ring on button, input, and full-width menu row — consistent blue 2px+offset, legible on both surfaces. Remaining follow-up: >=44px touch targets (disclosure trigger, composer send); bespoke notification / account-new close buttons that still carry a permanent border. * fix(ds): #2136 interactive-state follow-ups — touch target + close-button chrome - Disclosure default trigger: add min-h-11 (44px) so the standalone disclosure summary clears the touch-target minimum (was px-3 py-2 ~36px). Composer send + the coming-soon icons are already DS::Button icon/md (w-11 h-11). - Notification close buttons (sync_toast, notice): drop the resting border-alpha-black-50 box ("frame shouts, glyph muted"); keep a bg-container + shadow-xs chip so the corner control stays visible over the page, and brighten the muted glyph on hover (text-subdued -> hover:text-primary). * refactor(ds): focus ring -> neutral hugging box-shadow (was blue outline) Per design feedback: the blue 2px outline + 2px offset read as a loud, detached frame on the otherwise-neutral UI. Switch the canonical .focus-ring to a soft box-shadow ring that hugs the control (follows border-radius, no gap), in the theme-aware neutral focus-ring token (alpha-black/white-400). Transparent outline kept as a forced-colors fallback; toggle peer-driver switched from outline-* to ring-* to match. Still one token, :focus-visible only. Strength is tunable (currently subtle ~1.5:1). * fix(ds): focus ring vanished on shadowed controls — outline, not box-shadow The neutral box-shadow ring lived in the components layer, so any utility-layer shadow-* (or .form-field's focus-within:shadow-none) on the same element overrode it and the ring silently disappeared on shadowed buttons/inputs. Draw the same subtle neutral ring with a hugging `outline` (outline-offset: 0) instead — a separate property with no box-shadow conflict, and it doubles as the forced-colors indicator. Toggle peer-driver switched ring-* -> outline-* to match. Look is unchanged (neutral, hugging, subtle); it just no longer vanishes. * fix(a11y): enlarge sync-toast close-button touch target (p-0.5 -> p-1.5) The hover-revealed close button had ~2px padding around a 20px icon (~24px total), at the WCAG 2.5.8 AAA boundary. p-1.5 brings the interactive area to ~32px. Addresses CodeRabbit review on #2140. * fix(ds): keep form-field's resting halo; stop the outline color flash Two testing findings: - .form-field reverts to its original always-on soft ring (focus-within ring-4 at low alpha, theme-aware) instead of adopting the keyboard-only outline. It's a resting decoration, not a focus indicator, and the lower-opacity halo was the better look. The canonical block's comment documents the deliberate opt-out. - .focus-ring/.focus-ring-within now carry a base transparent 2px outline so consumers with transition-all (form-field had it) animate transparent -> token on focus instead of passing through currentColor, which flashed as a black border appearing and then fading out. * feat(ds): focus-ring token clears WCAG 3:1 non-text contrast alpha-black-400 (20%) measured ~1.6:1 against white — visible but below the AA bar for focus indicators. Bump to the 700 stop (50%): ~3.95:1 on light containers, ~4.6:1 on dark. Recipe unchanged; one token edit via tokens:build. * fix(ds): ring the hand-rolled privacy toggle too The header pair showed two different focus treatments: panel-right (a DS::Button) got the new token ring while the hand-rolled privacy toggle next to it fell back to the browser-default ring — the sweep covered DS primitives but not bespoke buttons. Both privacy toggles (mobile + desktop) now carry .focus-ring. Also documents the transition interplay on the focused-state rule: consumers with transition-colors fade the ring in over 150ms because Tailwind v4's color transition list includes outline-color. Verified settled value at the intended 50% alpha via Playwright. * fix(ds): ring the sidebar and settings nav links The reshoot caught both nav species falling back to the browser's blue default ring — main sidebar items and settings nav items are bespoke link_to markup the primitive sweep missed, and they're the primary keyboard path in the app. Both adopt .focus-ring (main nav adds rounded-lg so the outline follows a shape). * fix(ds): retire the legacy base-layer button ring for the canonical outline The @layer base button rule still painted a ring-2 ring-offset-2 box-shadow on :focus-visible. Box-shadow and outline are independent properties, so .focus-ring (an outline) could never clear it and every button-tag primitive double-painted both indicators on keyboard focus. Apply the canonical recipe to the base button rule itself: every <button> now gets the transparent resting outline + focus-ring token on :focus-visible by default. Two bespoke buttons suppressed the outline with focus:outline-none and relied on the base ring for their keyboard indicator (category dropdown rows, the sign-up password toggle). Drop the suppression so they pick up the canonical outline — :focus-visible keeps it keyboard-only, which is what the suppression was protecting against anyway. * fix(ds): segmented control adopts the canonical focus recipe The segment rule inlined its own focus-visible outline (offset 2, alpha-400 colors) with a comment noting it was temporary until the canonical token landed — this branch is that token. Drop the inlined utilities: button segments get the outline from the base button rule, and link segments now carry .focus-ring.
This commit is contained in:
committed by
GitHub
parent
5f391f7fff
commit
77dda53ffb
@@ -15,6 +15,7 @@
|
||||
--color-info: var(--color-blue-600);
|
||||
--color-shadow: --alpha(var(--color-black) / 6%);
|
||||
--color-link: var(--color-blue-600);
|
||||
--color-focus-ring: var(--color-alpha-black-700);
|
||||
--color-tertiary: var(--color-alpha-black-100);
|
||||
--color-surface: var(--color-gray-50);
|
||||
--color-surface-hover: var(--color-gray-100);
|
||||
@@ -216,6 +217,7 @@
|
||||
--color-info: var(--color-blue-500);
|
||||
--color-shadow: --alpha(var(--color-white) / 8%);
|
||||
--color-link: var(--color-blue-500);
|
||||
--color-focus-ring: var(--color-alpha-white-700);
|
||||
--color-tertiary: var(--color-alpha-white-200);
|
||||
--color-surface: var(--color-black);
|
||||
--color-surface-hover: var(--color-gray-800);
|
||||
|
||||
@@ -11,10 +11,21 @@
|
||||
}
|
||||
|
||||
button {
|
||||
@apply cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-alpha-black-300;
|
||||
@apply cursor-pointer;
|
||||
|
||||
@variant theme-dark {
|
||||
@apply focus-visible:ring-alpha-white-300;
|
||||
/* Canonical keyboard-focus indicator (#2136) — the same recipe as
|
||||
`.focus-ring` in components.css, applied here so every <button> gets
|
||||
it by default. This used to be a `ring-2 ring-offset-2` box-shadow,
|
||||
which double-painted alongside the `.focus-ring` outline on DS
|
||||
primitives (Buttonish, tabs, popover, tooltip): box-shadow and
|
||||
outline are independent properties, so the components-layer class
|
||||
could never clear it. Transparent resting outline for the same
|
||||
transition reasoning as `.focus-ring`. */
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 0;
|
||||
|
||||
&:focus-visible {
|
||||
outline-color: var(--color-focus-ring);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
@layer components {
|
||||
/*
|
||||
Canonical keyboard-focus indicator (#2136). One recipe for every focusable
|
||||
primitive: a neutral 2px `outline` that hugs the control (`outline-offset: 0`
|
||||
so it sits right at the edge and follows the border-radius), in the
|
||||
theme-aware `focus-ring` token.
|
||||
|
||||
`outline` — NOT box-shadow — on purpose: a components-layer box-shadow loses
|
||||
to any utility-layer `shadow-*` (or `focus-within:shadow-none` on the field)
|
||||
on the same element, so the ring silently vanished on shadowed controls.
|
||||
`outline` is a separate property with no such conflict, and it doubles as the
|
||||
forced-colors (Windows high-contrast) indicator. `:focus-visible` only, so it
|
||||
never shows for mouse/touch. `.focus-ring-within` mirrors it onto a wrapper
|
||||
whose focus lands on a child input. `.form-field` deliberately does NOT
|
||||
adopt it: form fields keep their original always-on soft halo (ring-4 at
|
||||
low alpha) — a resting decoration, not a keyboard-only indicator.
|
||||
*/
|
||||
/* Base: an always-present transparent outline so consumers that carry
|
||||
`transition-all` (or any transition touching outline-color) animate
|
||||
transparent -> token instead of passing through `currentColor` — the
|
||||
latter reads as a black border flashing in and out. */
|
||||
.focus-ring,
|
||||
.focus-ring-within {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 0;
|
||||
}
|
||||
|
||||
.focus-ring:focus-visible,
|
||||
.focus-ring-within:focus-within {
|
||||
/* Note: consumers carrying `transition-colors` animate this in over
|
||||
150ms — Tailwind v4's color transition list includes outline-color.
|
||||
Combined with the transparent base above, that reads as a quick
|
||||
fade-in rather than a pop, never a flash through currentColor. */
|
||||
outline-color: var(--color-focus-ring);
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.form-field {
|
||||
@apply flex flex-col gap-1 relative px-3 py-2 rounded-md border bg-container border-secondary shadow-xs w-full;
|
||||
@@ -168,8 +203,9 @@
|
||||
because `@apply`-ing a custom utility drops its dark override. The near-black
|
||||
dark track (alpha-black-700) is what makes the gray-700 selected pill read —
|
||||
the bespoke controls used a too-light `container-inset` track, hence the
|
||||
audit's "selected pill invisible on dark." Focus = neutral outline (matches
|
||||
the canonical focus ring; inlined until that token lands on main).
|
||||
audit's "selected pill invisible on dark." Focus comes from the canonical
|
||||
recipe (`focus-ring` on link segments, the base button rule on button
|
||||
segments), not from this block.
|
||||
*/
|
||||
.segmented-control {
|
||||
@apply inline-flex items-center gap-0.5 p-1 rounded-lg bg-gray-50;
|
||||
@@ -182,8 +218,6 @@
|
||||
@apply inline-flex items-center justify-center px-2 py-1 rounded-md whitespace-nowrap;
|
||||
@apply text-sm font-medium text-secondary cursor-pointer transition-colors duration-200;
|
||||
@apply hover:bg-gray-200;
|
||||
@apply focus-visible:outline-2 focus-visible:outline-offset-2;
|
||||
@apply focus-visible:outline-alpha-black-400 theme-dark:focus-visible:outline-alpha-white-400;
|
||||
@variant theme-dark {
|
||||
@apply hover:bg-gray-800;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class DS::Buttonish < DesignSystemComponent
|
||||
# `aria-disabled` to stay clickable/focusable (e.g. submit buttons whose
|
||||
# click handler surfaces validation errors — a truly disabled default
|
||||
# submit would also swallow Enter-key implicit submission).
|
||||
"font-medium whitespace-nowrap disabled:cursor-not-allowed aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
|
||||
"font-medium whitespace-nowrap focus-ring disabled:cursor-not-allowed aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
|
||||
merged_base_classes,
|
||||
full_width ? "w-full justify-center" : nil,
|
||||
container_size_classes,
|
||||
|
||||
@@ -68,15 +68,15 @@ class DS::Disclosure < DesignSystemComponent
|
||||
# surface. Keep cursor + focus-visible ring + flex baseline.
|
||||
# Ring token matches `settings/provider_card.html.erb` (the
|
||||
# established focus pattern on container cards).
|
||||
"list-none cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-alpha-black-300 rounded-xl"
|
||||
"list-none cursor-pointer focus-ring rounded-xl"
|
||||
when :inline
|
||||
# Inline variant: no surface, no padding — the summary reads as
|
||||
# plain text-link copy. Caller markup (text + optional chevron)
|
||||
# provides the visual. Keep cursor + focus-visible ring + matching
|
||||
# alpha-black-300 token used by the card variants for consistency.
|
||||
"list-none cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-alpha-black-300 rounded-sm"
|
||||
"list-none cursor-pointer focus-ring rounded-sm"
|
||||
else
|
||||
"px-3 py-2 rounded-xl cursor-pointer flex items-center justify-between bg-surface focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-alpha-black-300"
|
||||
"px-3 py-2 rounded-xl cursor-pointer flex items-center justify-between bg-surface focus-ring min-h-11"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,12 +6,9 @@ class DS::Link < DS::Buttonish
|
||||
VARIANTS = VARIANTS.reverse_merge(
|
||||
default: {
|
||||
# Underline + `text-link` so the link is distinguishable by more
|
||||
# than color alone (WCAG 1.4.1). Focus ring uses the established
|
||||
# alpha-ring DS pattern (also used by DS::Toggle, DS::Tooltip,
|
||||
# provider_card, form-field) so theming stays centralized.
|
||||
container_classes: "text-link underline underline-offset-2 hover:no-underline " \
|
||||
"focus-visible:ring-2 focus-visible:ring-alpha-black-300 " \
|
||||
"theme-dark:focus-visible:ring-alpha-white-300",
|
||||
# than color alone (WCAG 1.4.1). Keyboard focus uses the canonical
|
||||
# `.focus-ring` (#2136) so every primitive shares one indicator.
|
||||
container_classes: "text-link underline underline-offset-2 hover:no-underline focus-ring",
|
||||
icon_classes: "text-secondary"
|
||||
}
|
||||
).freeze
|
||||
|
||||
@@ -81,7 +81,7 @@ class DS::MenuItem < DesignSystemComponent
|
||||
|
||||
def container_classes
|
||||
[
|
||||
"flex items-center gap-2 p-2 rounded-md w-full",
|
||||
"flex items-center gap-2 p-2 rounded-md w-full focus-ring",
|
||||
destructive? ? "hover:bg-red-tint-5 theme-dark:hover:bg-red-tint-10" : "hover:bg-container-hover"
|
||||
].join(" ")
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
the fallback `ds.popover.avatar_default_label` is used. %>
|
||||
<button type="button"
|
||||
data-DS--popover-target="button"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 cursor-pointer rounded-full focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 theme-dark:focus-visible:outline-white"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 cursor-pointer rounded-full focus-ring"
|
||||
aria-label="<%= trigger_aria_label %>"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded="false"
|
||||
|
||||
@@ -53,15 +53,11 @@ class DS::SearchInput < DesignSystemComponent
|
||||
"h-10 pl-10 w-full border-none rounded-lg " \
|
||||
"focus:outline-hidden focus:ring-0"
|
||||
else
|
||||
# `focus-visible:outline-*` matches the focus-ring pattern from
|
||||
# DS::Button (base.css) so every interactive surface in the design
|
||||
# system uses the same ring token. Replaces the broken
|
||||
# `focus:ring-gray-500` from the inline callsites — that utility
|
||||
# had no backing token and rendered invisibly on the bordered
|
||||
# bg-container surface.
|
||||
# Canonical `.focus-ring` (#2136) — one shared keyboard-focus
|
||||
# indicator across every DS primitive. Replaces the earlier neutral
|
||||
# `outline-gray-900 / theme-dark:outline-white` pair.
|
||||
"block w-full border border-secondary rounded-md py-2.5 pl-10 pr-3 bg-container text-base sm:text-sm " \
|
||||
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 " \
|
||||
"theme-dark:focus-visible:outline-white"
|
||||
"focus-ring"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@ class DS::SegmentedControl < DesignSystemComponent
|
||||
# `full_width: true` stretches segments to equal width (the "equal-footprint"
|
||||
# the #2137 audit asked for); default is content width.
|
||||
renders_many :segments, ->(label, active: false, href: nil, **opts) do
|
||||
# `focus-ring` is for link segments — button segments already get the
|
||||
# canonical outline from the base-layer button rule (it's an idempotent
|
||||
# duplicate there).
|
||||
classes = class_names(
|
||||
"segmented-control__segment",
|
||||
"segmented-control__segment focus-ring",
|
||||
("flex-1" if full_width),
|
||||
("segmented-control__segment--active" if active),
|
||||
opts.delete(:class)
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
<div class="absolute z-50 p-1.5 w-full min-w-32 rounded-lg shadow-border-lg bg-container mt-1.5 transition duration-150 ease-out -translate-y-1 opacity-0 hidden" data-select-target="menu">
|
||||
<% if searchable %>
|
||||
<div class="flex items-center bg-container border border-secondary rounded-lg mb-1 focus-within:ring-4 focus-within:ring-alpha-black-200 theme-dark:focus-within:ring-alpha-white-300 transition-shadow">
|
||||
<div class="flex items-center bg-container border border-secondary rounded-lg mb-1 focus-ring-within transition-shadow">
|
||||
<%= render DS::SearchInput.new(
|
||||
variant: :embedded,
|
||||
placeholder: t("helpers.select.search_placeholder"),
|
||||
|
||||
@@ -22,7 +22,7 @@ class DS::Tabs::Nav < DesignSystemComponent
|
||||
content_tag(
|
||||
:button, label, id: "#{dom_prefix}-tab-#{id}",
|
||||
type: "button",
|
||||
class: class_names(btn_classes, is_active ? active_btn_classes : inactive_btn_classes, classes),
|
||||
class: class_names("focus-ring", btn_classes, is_active ? active_btn_classes : inactive_btn_classes, classes),
|
||||
role: "tab",
|
||||
"aria-selected": is_active.to_s,
|
||||
"aria-controls": "#{dom_prefix}-panel-#{id}",
|
||||
|
||||
@@ -27,11 +27,10 @@ class DS::Toggle < DesignSystemComponent
|
||||
"after:top-0.5 after:left-0.5 after:w-4 after:h-4 after:shadow-sm",
|
||||
"motion-safe:after:transition-transform motion-safe:after:duration-300 motion-safe:after:ease-in-out",
|
||||
"peer-checked:bg-success peer-checked:after:translate-x-4",
|
||||
# Focus ring driven from the sr-only input via `peer-focus-visible:`.
|
||||
# Offset places the ring outside the track so it lands on the
|
||||
# surrounding chrome regardless of theme.
|
||||
"peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2",
|
||||
"peer-focus-visible:ring-alpha-black-300 theme-dark:peer-focus-visible:ring-alpha-white-300",
|
||||
# Canonical focus ring (#2136), driven from the sr-only input via
|
||||
# `peer-focus-visible:`. outline-offset places it just outside the track
|
||||
# so it lands on surrounding chrome in either theme.
|
||||
"peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-focus-ring",
|
||||
"peer-disabled:opacity-70 peer-disabled:cursor-not-allowed"
|
||||
)
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
name comes from `aria-label`, and the tooltip text is exposed
|
||||
via `aria-describedby`. %>
|
||||
<button type="button"
|
||||
class="inline-flex items-center cursor-default focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-alpha-black-300 theme-dark:focus-visible:ring-alpha-white-300 rounded"
|
||||
class="inline-flex items-center cursor-default focus-ring rounded"
|
||||
aria-describedby="<%= tooltip_id %>"
|
||||
aria-label="<%= t("ds.tooltip.trigger_label", default: "More info") %>">
|
||||
<%= helpers.icon icon_name, size: size, color: color %>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
),
|
||||
method: :patch,
|
||||
data: { turbo_frame: "category_dropdown" },
|
||||
class: "flex w-full items-center gap-1.5 cursor-pointer focus:outline-none" do %>
|
||||
class: "flex w-full items-center gap-1.5 cursor-pointer" do %>
|
||||
|
||||
<span class="flex items-center justify-center w-5 h-5 shrink-0">
|
||||
<% if is_selected %><%= icon("check") %><% end %>
|
||||
|
||||
@@ -60,7 +60,7 @@ end %>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<button type="button"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 rounded-lg text-secondary hover:text-primary hover:bg-container-inset-hover transition-colors"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 rounded-lg text-secondary hover:text-primary hover:bg-container-inset-hover transition-colors focus-ring"
|
||||
data-action="click->privacy-mode#toggle"
|
||||
data-privacy-mode-target="toggle"
|
||||
title="<%= t("layouts.application.privacy_mode") %>"
|
||||
@@ -158,7 +158,7 @@ end %>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button type="button"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 rounded-lg text-secondary hover:text-primary hover:bg-container-inset-hover transition-colors"
|
||||
class="inline-flex items-center justify-center w-9 h-9 pointer-coarse:w-11 pointer-coarse:h-11 rounded-lg text-secondary hover:text-primary hover:bg-container-inset-hover transition-colors focus-ring"
|
||||
data-action="click->privacy-mode#toggle"
|
||||
data-privacy-mode-target="toggle"
|
||||
title="<%= t("layouts.application.privacy_mode") %>"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%# locals: (name:, path:, icon:, icon_custom:, active:, mobile_only: false, preview: false) %>
|
||||
|
||||
<%= link_to path, class: "space-y-1 group block relative pb-1", aria: { current: ("page" if active) } do %>
|
||||
<%= link_to path, class: "space-y-1 group block relative pb-1 focus-ring rounded-lg", aria: { current: ("page" if active) } do %>
|
||||
<div class="grow flex flex-col lg:flex-row gap-1 items-center">
|
||||
<%= tag.div class: class_names("w-4 h-1 lg:w-1 lg:h-4 rounded-bl-sm rounded-br-sm lg:rounded-tr-sm lg:rounded-br-sm lg:rounded-bl-none", "bg-nav-indicator" => active) %>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
action: "input->password-validator#validate"
|
||||
} %>
|
||||
<button type="button"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-secondary hover:text-primary focus:outline-none"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-secondary hover:text-primary"
|
||||
data-action="click->password-visibility#toggle">
|
||||
<div data-password-visibility-target="showIcon">
|
||||
<%= icon("eye") %>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%# locals: (name:, path:, icon:) %>
|
||||
|
||||
<%= link_to path, class: class_names(
|
||||
"flex items-center gap-2 whitespace-nowrap px-3 py-2 rounded-lg text-sm",
|
||||
"flex items-center gap-2 whitespace-nowrap px-3 py-2 rounded-lg text-sm focus-ring",
|
||||
page_active?(path) ? "text-primary bg-container shadow-border-xs" : "text-secondary hover:bg-surface-hover border-transparent"
|
||||
), aria: { current: ("page" if page_active?(path)) } do %>
|
||||
<%= icon(icon) if icon %>
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
</div>
|
||||
|
||||
<div class="absolute -top-2 -right-2">
|
||||
<%= icon "x", class: "p-0.5 hidden group-hover:inline-block border border-alpha-black-50 border-solid rounded-lg bg-container text-subdued cursor-pointer", data: { action: "click->element-removal#remove" } %>
|
||||
<%= icon "x", class: "p-0.5 hidden group-hover:inline-block rounded-lg bg-container shadow-xs text-subdued hover:text-primary cursor-pointer", data: { action: "click->element-removal#remove" } %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"shadow": { "$value": "{color.black|6%}", "$type": "color", "$extensions": { "sure.dark": "{color.white|8%}" } },
|
||||
|
||||
"link": { "$value": "{color.blue.600}", "$type": "color", "$extensions": { "sure.dark": "{color.blue.500}" } },
|
||||
"focus-ring": { "$value": "{color.alpha-black.700}", "$type": "color", "$extensions": { "sure.dark": "{color.alpha-white.700}" } },
|
||||
"tertiary": { "$value": "{color.alpha-black.100}", "$type": "color", "$extensions": { "sure.dark": "{color.alpha-white.200}" } },
|
||||
"surface": { "$value": "{color.gray.50}", "$type": "color", "$extensions": { "sure.dark": "{color.black}" } },
|
||||
"surface-hover": { "$value": "{color.gray.100}", "$type": "color", "$extensions": { "sure.dark": "{color.gray.800}" } },
|
||||
|
||||
Reference in New Issue
Block a user