fix(themes): unify input & dropdown focus borders across themes (#1052)

* chore(themes): drop the dead installs field from RegistryTheme

The store no longer reads install counts (the themes registry stopped
emitting them), so this optional field on RegistryTheme was unused.

* fix(themes): give dropdown border/shadow tokens a cascade default

--border-dropdown and --shadow-dropdown were set by only two themes and had
no base fallback, so dropdown/popover borders rendered without a themed value
in every other theme. Default them in the semantic cascade next to the other
menu tokens; themes that set them explicitly still override.

* fix(themes): unify input focus rings, drop the double border

Text inputs draw their own border + box-shadow focus ring, but the global
:focus-visible outline stacked a second ring outside it — a double border on
every field. Suppress the outline for text inputs centrally; the specificity
(0,1,1) beats the global ring but loses to the colour-blind-safe themes'
[data-theme] *:focus-visible (0,2,0), so those keep their stronger AAA ring on
every field by design — the header search now carries that ring on its cluster
there too. Align the few input classes that had a weaker or missing ring to the
shared border + 3px accent-dim standard.

* docs(changelog): add #1052 input/dropdown focus border fixes
This commit is contained in:
Psychotoxical
2026-06-09 22:23:41 +02:00
committed by GitHub
parent 155ef88cc0
commit 98bdf310d6
11 changed files with 72 additions and 7 deletions
+9
View File
@@ -265,6 +265,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Themes — consistent focus borders on inputs and dropdowns
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1052](https://github.com/Psychotoxical/psysonic/pull/1052)**
* Text fields no longer draw a double border when focused — they now show a single clean ring across every theme. The colour-blind-safe themes keep their stronger high-contrast focus ring on every field, including the header search.
* Dropdown and popover borders now follow the active theme in all themes (a couple of themes previously rendered them with an unthemed colour).
## [1.47.0]
> **🙏 Thank you to our amazing Discord community.** This release would not have been possible without your tireless support, quality checks, bug reports and all-round collaboration. Every report, every repro and every bit of feedback shaped what shipped here — thank you. Come join us: [discord.gg/AMnDRErm4u](https://discord.gg/AMnDRErm4u)
@@ -46,12 +46,18 @@
flex: 1;
min-width: 0;
background: var(--input-bg);
border: 1px solid var(--accent);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 12px;
padding: 3px 7px;
outline: none;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.context-submenu-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
.context-submenu-input::placeholder {
@@ -817,9 +817,11 @@
font-size: 14px;
box-sizing: border-box;
outline: none;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.orbit-start-modal__input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
.orbit-start-modal__reshuffle {
@@ -469,6 +469,6 @@
.playback-delay-custom__input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
+2 -1
View File
@@ -20,11 +20,12 @@
padding: 6px 12px;
width: 220px;
outline: none;
transition: border-color 0.15s;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.playlist-filter-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
.playlist-filter-input::placeholder {
+2 -2
View File
@@ -165,8 +165,8 @@
.folder-col-filter-input:focus {
outline: none;
border-color: color-mix(in srgb, var(--accent) 70%, var(--border-subtle));
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent);
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
.folder-col.folder-col--compact .folder-col-filter {
+23
View File
@@ -15,3 +15,26 @@
outline-offset: var(--focus-ring-offset);
}
/* Text inputs draw their OWN focus ring (border + box-shadow), so the global
outline above stacks a second ring outside it the "double border". Drop it
for them. Specificity is deliberate: `input:focus-visible` is (0,1,1), which
beats the global :focus-visible (0,1,0) but LOSES to the colour-blind-safe
themes' `[data-theme] *:focus-visible` (0,2,0) so those themes keep their
stronger AAA ring on every field (incl. inputs) by design. The `:where()`
type-exclusion stays weightless so non-text controls (range/checkbox/) keep
the outline everywhere. */
input:not(:where([type='button'], [type='submit'], [type='reset'], [type='checkbox'], [type='radio'], [type='range'], [type='color'], [type='file'], [type='image'])):focus-visible,
textarea:focus-visible {
outline: none;
}
/* Under forced-colors the OS drops the box-shadow ring, so restore an outline
as the focus indicator for those same text inputs. */
@media (forced-colors: active) {
input:not(:where([type='button'], [type='submit'], [type='reset'], [type='checkbox'], [type='radio'], [type='range'], [type='color'], [type='file'], [type='image'])):focus-visible,
textarea:focus-visible {
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: var(--focus-ring-offset);
}
}
+2
View File
@@ -55,6 +55,8 @@
--menu-item-hover: var(--bg-hover);
--menu-item-active-text: var(--accent);
--menu-divider: var(--border-subtle);
--border-dropdown: var(--border);
--shadow-dropdown: rgba(0, 0, 0, 0.5);
--modal-bg: var(--bg-card);
--modal-scrim: rgba(17, 17, 27, 0.85);
@@ -35,6 +35,18 @@
outline-offset: 2px;
}
/* The header search wraps its input in a cluster <div>, so the global outline
lands on the inner field, not the visible field box. Mirror the AAA outline
onto the cluster (and drop the inner one) so the search carries the same
strong ring as every other input in this colour-blind-safe theme. */
[data-theme='vision-dark'] .live-search-field-cluster:focus-within {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
[data-theme='vision-dark'] .live-search-field:focus-visible {
outline: none;
}
/* Scrollbar thumb — explicit contrast (auto-hide still applies) */
[data-theme='vision-dark'] ::-webkit-scrollbar-thumb {
background: var(--ctp-overlay0);
@@ -32,6 +32,18 @@
outline-offset: 2px;
}
/* The header search wraps its input in a cluster <div>, so the global outline
lands on the inner field, not the visible field box. Mirror the AAA outline
onto the cluster (and drop the inner one) so the search carries the same
strong ring as every other input in this colour-blind-safe theme. */
[data-theme='vision-navy'] .live-search-field-cluster:focus-within {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
[data-theme='vision-navy'] .live-search-field:focus-visible {
outline: none;
}
/* Scrollbar thumb — explicit contrast (auto-hide still applies) */
[data-theme='vision-navy'] ::-webkit-scrollbar-thumb {
background: var(--ctp-overlay0);
-2
View File
@@ -28,8 +28,6 @@ export interface RegistryTheme {
css: string;
/** Repo-relative path to the thumbnail. */
thumbnail: string;
/** All-time CDN downloads of the theme's CSS (≈ installs); 0 until any land. */
installs?: number;
/** ISO date of the last commit touching the theme in the registry repo. */
updatedAt?: string;
}