256 lines
12 KiB
Markdown
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.
|