Dark mode contrast, tuner display and misc UI fixes

- color-scheme per theme so native controls (selects, checkboxes, number
  inputs, sliders) render dark-native instead of black-on-black.
- Themed .control-select text color; new .control-input class replaces
  hardcoded white-on-black inline input styles.
- Tuner widget stays mounted when enabled: idle state (dimmed, needle parked,
  '—') instead of appearing/disappearing. Fully theme-var based (was
  hardcoded white — invisible in light mode).
- Remove CSS transitions on per-frame values (tuner needle, level meter):
  transitions interrupted 60x/sec are left stuck by WebKit/Safari; the
  display EMA from the detection loop replaces them.
- VexFlow notation: resolve CSS var() to concrete colors (SVG attributes
  can't resolve var(), silently fell back to black) and override VexFlow's
  hardcoded #444 ledger-line default; re-render on theme changes
  (useLayoutEffect so children read the updated attribute in time).
- Themed switch toggles (were hardcoded #ddd / white — glowed in dark mode).
- Fixed-height pitch readout so the layout stops jumping when a note starts
  or stops sounding.
- Split SettingsContext into context/Provider/hook files (fast-refresh);
  memoized provider functions; typed webkitAudioContext; prefer-const;
  removed 'as any' casts. ESLint now passes clean.
This commit is contained in:
2026-09-13 18:52:27 +02:00
parent 079d574b66
commit 6e60ced477
13 changed files with 212 additions and 121 deletions

View File

@@ -203,7 +203,18 @@ button {
.pitch-readout {
display: flex;
flex-direction: column;
justify-content: center;
min-width: 100px;
/* Fixed height: the note display (3rem note + Hz line ≈ 70px) and the
"Listening..." placeholder differ in height — without a reserved height
everything below this element jumps up and down whenever a note
starts or stops sounding. */
height: 70px;
}
.pitch-readout .placeholder {
font-size: 0.9rem;
color: var(--color-text-muted);
}
.detected-note {
@@ -285,10 +296,18 @@ button {
.control-select {
padding: var(--spacing-sm);
border-radius: var(--radius-sm);
border: 1px solid #ddd;
border: 1px solid color-mix(in srgb, var(--color-text-main) 20%, transparent);
font-family: inherit;
font-size: 0.9rem;
background-color: var(--color-bg);
background-color: var(--color-surface);
color: var(--color-text-main);
}
.control-input {
color: var(--color-text-main);
background-color: var(--color-surface);
border: 1px solid color-mix(in srgb, var(--color-text-main) 20%, transparent);
border-radius: var(--radius-sm);
}
.control-button {
@@ -330,7 +349,9 @@ button {
width: 44px;
height: 24px;
border-radius: 99px;
background-color: #ddd;
/* Themed track: dark in dark mode, gray in light mode
(was hardcoded #ddd — glowed in dark mode) */
background-color: color-mix(in srgb, var(--color-text-main) 25%, transparent);
position: relative;
cursor: pointer;
transition: background-color 0.2s;
@@ -347,7 +368,8 @@ button {
.switch-thumb {
width: 18px;
height: 18px;
background-color: white;
/* Themed: contrasts with both the off-track and the active track */
background-color: var(--color-surface);
border-radius: 50%;
position: absolute;
left: 3px;