Skip to content

Commit 1792216

Browse files
committed
Accessibility/CSS: Add missing focus indicators
- Add `:focus` rules with `border-color` + `box-shadow` to `SearchDialog` inputs (`.name-input`, `.filter-select`, `.filter-input`) and `CommandPalette` search input - Change `.name-input` from `border: none` to `border: 1px solid transparent` so the focus border is visible - Document focus indicator pattern in `design-system.md`: standard input pattern, element-type table, `:focus` vs `:focus-visible` rationale - Update app rule #6 to reference the new focus indicators section
1 parent 57e2d23 commit 1792216

3 files changed

Lines changed: 42 additions & 2 deletions

File tree

apps/desktop/src/lib/command-palette/CommandPalette.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@
231231
flex-shrink: 0;
232232
}
233233
234+
.search-input:focus {
235+
border-bottom-color: var(--color-accent);
236+
box-shadow: var(--shadow-focus);
237+
}
238+
234239
.search-input::placeholder {
235240
color: var(--color-text-tertiary);
236241
}

apps/desktop/src/lib/search/SearchDialog.svelte

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,13 +866,18 @@
866866
.name-input {
867867
flex: 1;
868868
font-size: var(--font-size-md);
869-
border: none;
869+
border: 1px solid transparent;
870870
background: transparent;
871871
color: var(--color-text-primary);
872872
outline: none;
873873
min-width: 0;
874874
}
875875
876+
.name-input:focus {
877+
border-color: var(--color-accent);
878+
box-shadow: var(--shadow-focus);
879+
}
880+
876881
.name-input::placeholder {
877882
color: var(--color-text-tertiary);
878883
}
@@ -985,6 +990,11 @@
985990
outline: none;
986991
}
987992
993+
.filter-select:focus {
994+
border-color: var(--color-accent);
995+
box-shadow: var(--shadow-focus);
996+
}
997+
988998
.filter-input {
989999
font-size: var(--font-size-sm);
9901000
/* stylelint-disable-next-line declaration-property-value-disallowed-list */
@@ -996,6 +1006,11 @@
9961006
outline: none;
9971007
}
9981008
1009+
.filter-input:focus {
1010+
border-color: var(--color-accent);
1011+
box-shadow: var(--shadow-focus);
1012+
}
1013+
9991014
.size-input {
10001015
width: 70px;
10011016
}

docs/design-system.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,26 @@ opacity fade.
336336

337337
## Component patterns
338338

339+
### Focus indicators (app)
340+
341+
Every interactive element needs a visible focus indicator. This is a keyboard-driven app, so all focus must be visible
342+
(use `:focus`, not `:focus-visible`, for inputs).
343+
344+
| Element type | How focus is handled |
345+
|---|---|
346+
| Buttons | Global `button:focus-visible` rule in `app.css` — automatic, no per-component work needed |
347+
| Inputs, selects, textareas | Each component provides its own `:focus` rule: `border-color: var(--color-accent); box-shadow: var(--shadow-focus);` |
348+
| File list containers (BriefList, FullList) | Cursor/selection highlight serves as the focus indicator. `outline: none` is intentional |
349+
| Non-interactive containers (`<div>` without tabindex, or tabindex=-1) | May use `outline: none` freely — no focus indicator needed |
350+
351+
**Standard pattern for inputs:** Set `outline: none` on the base selector, then pair it with a `:focus` rule that
352+
applies `border-color: var(--color-accent)` and `box-shadow: var(--shadow-focus)`. If the element has `border: none`,
353+
add `border: 1px solid transparent` to the base so the border-color change is visible on focus.
354+
355+
**Why `:focus` instead of `:focus-visible`:** Browsers only apply `:focus-visible` to keyboard-initiated focus, hiding
356+
the ring on click. In a keyboard-first file manager, users constantly switch between mouse and keyboard mid-action —
357+
hiding the indicator on click would make the currently focused element invisible when they reach for the keyboard.
358+
339359
### File list (app — the primary surface)
340360

341361
The file list is what users see 90% of the time. Every other component is secondary.
@@ -535,7 +555,7 @@ Auto-dismiss after 4 seconds. Close button on hover.
535555
3. All spacing via `--spacing-*` tokens. Never use arbitrary px values.
536556
4. Accent color is dynamic — never assume it's blue. Derive hover/subtle variants with `color-mix()`.
537557
5. Transitions default to `var(--transition-base)`. Only `--transition-fast` or `--transition-slow` with justification.
538-
6. Focus ring on every interactive element via `:focus-visible`. No bare `outline: none`.
558+
6. Focus indicator on every interactive element. No bare `outline: none` — see "Focus indicators" under Component patterns.
539559
7. `prefers-reduced-motion` wraps all non-essential animation.
540560
8. Scrollbars stay native. Never style `::-webkit-scrollbar`.
541561

0 commit comments

Comments
 (0)