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

12 KiB

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

<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:

<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.