Files
Veskslop-t/docs/ui-changes.md
T

256 lines
12 KiB
Markdown

# Veskslop UI — Nocturne fidelity pass
The shell (header, rail, call strip, composer, TTS box, console, right column)
is faithful to the design. The drift is all in the layer added since: the Media
archive and Bot tabs, plus a few spots in `style.css`.
**Part two** (icons, settings, menus, dialogs, toasts) starts further down.
## Install
```html
<link rel="stylesheet" href="nocturne.css" />
<link rel="stylesheet" href="tokens-status.css" /> <!-- new, additive -->
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="media.css" /> <!-- replaces yours -->
<link rel="stylesheet" href="style-overrides.css" /> <!-- new, load last -->
<link rel="stylesheet" href="settings-popups.css" /> <!-- new, load last -->
<script src="icons.js"></script> <!-- replaces yours -->
<script src="polish.js"></script> <!-- new, after icons.js -->
```
`tokens-status.css`, `style-overrides.css`, `settings-popups.css` and `polish.js`
are new files. `media.css` and `icons.js` are your files with the changes
applied — diff them, don't assume.
---
## The root cause
Nocturne ships one accent and a neutral ramp. It has **no error / warning /
success role and no categorical data scale.** So every feature that needed one
invented its own: `#e5484d`, `#ffaa33`, `#44cc88`, `#ff5555`, `#ff8080`,
`#c04040`, `#4f8cff`, `#8a6cff`, `#ff7a45`, `#2fbf71`, `#6b7280`, `#d29922`.
Eighteen hexes, four different reds, none on the system's lightness scale.
`tokens-status.css` fills that hole properly — danger, warn, ok and a five-step
data scale, generated in OKLCH on Nocturne's **own** shared lightness scale, so
step 400 of danger carries the same visual weight as step 400 of accent. Chroma
stays low; the accent is still the loudest thing on screen.
Everything else in this patch is downstream of having those tokens.
---
## What changed
### 1. Colour
- 18 hard-coded hexes replaced with tokens. Zero remain.
- **Wrong-hue fallbacks removed.** `var(--color-accent-400, #4f8cff)` appeared
six times. `#4f8cff` is blue; Nocturne's accent is blurple `#9184d9`. Any time
that fallback fired — a load-order slip, a scoping mistake — the Media and Bot
tabs turned a different colour than the rest of the app. Silent, and hard to
spot because it only happens when something else is already broken.
- The storage bar's five saturated hues become the low-chroma `--color-data-*`
family. Nocturne's rule is that contrast comes from the tonal ramps, not from
saturation; five alarm colours in a 10px bar broke it harder than anything
else on screen.
- `--color-mention: #d29922` was declared in a stray second `:root` block 400
lines into `style.css`. It now lives with the other tokens as `--color-warn-400`.
### 2. Floods
Nocturne outlines; it does not fill.
- `.vk-toggle-half.vk-toggle-on` was a solid `accent-400` panel with `#fff`
text — the one thing the guide names as a don't. Now tinted and outlined, the
same treatment `.vk-scope-btn.active` already uses.
- `.vk-media-badge` was `#e5484d` with white text at 9px. Now a `danger-800`
tint with `danger-200` ink at 10px.
- `.vk-bot-state-*` pills moved from alpha washes over saturated hues to deep
ramp fills with full-opacity ink — an alpha wash over an unknown backdrop is
how small text loses contrast without anyone noticing.
### 3. Weight
Nocturne heads cap at 500; hierarchy is size and space. Removed 11 instances of
`font: 600` and `font-weight: 600` in `media.css`, plus `font-weight: 700` on
the speaking state in `style.css`. That last one also **reflowed the row every
time someone started or stopped talking** — the names jittered. Speaking now
reads in `accent-200`, with the level meter beside it doing the loud work.
### 4. Edges and radii
`border: 1px solid` → the system's `inset 0 0 0 1px` edge, so elevation stays
"hairline plus ambient darkness" rather than a drawn box. Literal `3px / 5px /
6px / 7px` radii → `--radius-sm` / `--radius-md`. Six different corner radii
were in play across the Bot tab.
### 5. Focus
`nocturne.css` sets a 2px accent `:focus-visible` ring, but every control built
from a bare `<button>` that resets `background` and `border` loses it against
its own surface. Explicitly restored on 18 controls: rail rows, guild marks,
scope buttons, console tabs, chips, stars, mobile tabs, settings summaries,
media links, tree heads, group titles. **This is the only keyboard path through
the app** — worth checking by tabbing the whole thing once.
### 6. Contrast
- `.vk-media-done-note` was `color-mix(neutral-400 55%, transparent)` at 10px —
roughly 2.4:1. Now `neutral-500`.
- `.vk-bot-history-entry` and friends: 10px mono at `neutral-500` sits just
under 4.5:1 on surface. Lifted one step to `neutral-400`.
- **The offline state was the worst of it.** `opacity: .5` plus
`saturate(.6)` on the entire body and console pushed every label under 3:1 —
in the one state where you most need to read what is on screen and work out
what died. Now the chrome dims behind a scrim and the type stays legible.
Input is still blocked.
### 7. Status marks that were saying the wrong thing
- `.vk-dot.error` was `neutral-200` — a bright white dot, which reads as
healthy. Now `danger-400`.
- `.vk-toast.error` and `.vk-toast.ok` differed only by one hue step of the same
near-white. Now genuinely different roles, plus a `warn` variant.
- `.vk-presence.idle` and dnd both landed on `neutral-200`, so they were
indistinguishable. idle drops a step; dnd keeps the bright dot and gains the
bar that makes it readable without colour.
### 8. Rules
Nocturne's rules fade to transparent over 48px a side. The channel header does
this; the settings panel's dozens of flat `inset 0 -1px 0` rules did not, which
is most of why that panel reads as a different product bolted on. Now fading.
---
## Needs a small markup change
The storage bar is colour-only. `media.css` now carries
`.vk-media-usage-legend` / `.vk-media-usage-key` — render one key per segment
beside the bar:
```html
<div class="vk-media-usage-legend">
<span class="vk-media-usage-key image"><i></i>images 4.1 GiB</span>
<span class="vk-media-usage-key gif"><i></i>gifs 0.8</span>
<span class="vk-media-usage-key video"><i></i>video 11.2</span>
<span class="vk-media-usage-key audio"><i></i>audio 0.3</span>
<span class="vk-media-usage-key"><i></i>other 0.1</span>
</div>
```
Add `.over` to `.vk-media-usage` past the warn threshold.
Also: `.vk-presence.dnd` now exists as a state. If `app.js` only emits
`online` / `idle` / `offline`, dnd is still rendering as plain online.
---
## Not changed — worth a conversation
**The Media and Bot tabs are a second application inside the shell.** They
don't use the rail, the right column or the console; they take over the whole
body with their own toolbar, their own grid, their own modals at their own
z-index tier. The patch makes them *look* like the rest of the app. It doesn't
make them *behave* like it. Worth deciding whether Media is a scope alongside
Guild/DM — which is what the segmented control currently implies — or a separate
surface that should stop pretending to be one.
**Settings is carrying nine sections in one slide-over**, several of which
(Flextalk voice profile, Bot audio, Speech) are deep enough to be their own
screens. The `<details>` grouping is holding for now, but the voice profile
panel already had to escape into its own draggable dialog, which is the usual
sign.
**`.vk-media-play` is a text glyph** (font-size 44px) where the rest of the app
uses Phosphor. Small, but it is the only place an icon doesn't match.
---
# Part two — icons, settings, menus, dialogs
## 9. Icons
`icons.js` is replaced. The old set was drawn on a 16-unit grid at a fixed 1.3
stroke; the new one is Phosphor geometry on Phosphor's 256 grid.
- **Optical weight is now constant.** Stroke is computed per render size
(`1.35 / size * 256`), so a 44px icon and an 11px icon carry the same line.
Before, stroke scaled with the icon and large ones looked heavy.
- **Round caps and joins throughout** — Phosphor regular's terminal, which the
old mic, camera and gear each handled differently.
- **The gear, mic, camera, refresh and warning were redrawn.** The gear was a
circle with eight straight spokes; the mic was a rounded rect over an arc.
They read as icons now rather than as diagrams.
- **19 icons added**: headphones / headphonesSlash (the listening and deafened
states, which had no mark of their own), screenshare, waveform, image, film,
audio, folder, pause, stop, check, plus, minus, dots, funnel, sliders,
download, copy, pencil, trash, pin, popout, info, error, ok, clock.
- **Every old name still resolves** — aliases cover `microphone`, `deafen`,
`video`, `close`, `settings`, `tts`, `magnify` — so nothing needs editing.
- Unknown names now warn in the console instead of rendering an empty `<svg>`.
That surfaced one already: `star` was used before it was defined, and worked
only because a later line patched it in.
- `VK.hydrateIcons(root)` fills any empty `[data-icon]`; it skips anything
already rendered, so it won't fight `app.js`.
If you would rather ship the real Phosphor package, swap the file for
`@phosphor-icons/web` and keep `VK.icon` as a wrapper — the names are Phosphor's
own, so the mapping is one to one.
## 10. `polish.js` — what CSS can't reach
Three text glyphs are doing an icon's job. This is a small idempotent pass with
a MutationObserver, so it also catches the Media and Bot tabs rebuilding:
- `<button id="scrape-x">X</button>` and its siblings → the `x` icon, plus
`aria-label="Close"`. There were several, in the scrape and clean modals.
- `.vk-media-play`'s ▶ character at 44px → the `play` icon.
- `.vk-info`'s `title` → `data-tip`, so the styled tooltip below is the only one
that fires, plus `aria-label` and `tabindex` so the help is reachable at all.
No markup edits. If you'd rather fold these into the source, the script says
exactly what to change.
## 11. Settings, menus, dialogs, toasts
`settings-popups.css`. Presentation only, no behaviour or markup changes.
- **Scrim.** 55% flat wash → 72% of the page ground with a 3px blur. The page
behind was legible enough that the eye kept reading it.
- **Panel.** Square left edge → `--radius-lg` on the leading corners, the
system's hairline, and a 200ms slide from the edge it is attached to.
- **Head is sticky** and sits on a fading rule. Settings scrolls a long way;
the title used to leave.
- **Section rows.** The card was a flat fill with rows running together. Now the
system edge, rows separated by rules that fade at both ends, values on
tabular numerals so they align down the column.
- **`.vk-info` was an unstyled letter `i`** with its help text trapped in a
native tooltip — half a second of delay, no styling, invisible on touch. It is
now a 14px ringed mark with a themed tooltip. Eleven help strings were sitting
in `title` attributes where most people would never see them.
- **Collapsible groups.** The summary was a bare uppercase line with nothing
saying it was a control. It is a row now — hover tint, turning caret, and the
body indented against a hairline so nesting is visible.
- **Native controls.** Selects kept the platform arrow and a 1px border, numbers
kept their spinners, ranges kept the platform track. All three now use the
system's edge, accent thumb and focus ring. The select caret is a mask taking
`--color-neutral-400`, not a hex baked into a data URI.
- **Menus** get the hairline, a 120ms entrance and an accent bar on the active
item instead of a tint with nothing anchoring it.
- **Dialogs.** The voice-profile drawer was square-topped against the scrim and
the media modals had unrelated radii. Both on `--radius-lg` and the system
edge now; the profile drawer gains a grab handle, since its head was already
the drag target but nothing said so.
- **Toasts** get an icon slot coloured by role from `tokens-status.css`.
- All of it respects `prefers-reduced-motion`.
## Worth doing next
`.vk-status-value:has(> .vk-select)` carries the select caret. If a select ever
ships outside a status row, wrap it in `.vk-select-wrap` — the rule is there.