better toolbar

This commit is contained in:
Your Name 2026-10-01 00:11:16 -04:00
parent 2f1c9b9c02
commit 5fb04f6a7d
6 changed files with 433 additions and 87 deletions

View file

@ -123,6 +123,33 @@ audio { display: none; }
border-bottom: 1px solid var(--line);
color: var(--dim);
letter-spacing: .03em;
/* One line, always. A strip of controls that reflows to two lines resizes the
pane under it, and the pane under this one is the timeline. */
white-space: nowrap;
}
.pane-head .readout { color: var(--fg); }
/* A pick-one that looks like the other pick-ones. A native select in this strip
draws a tall white well and a chevron of the browser's choosing, which reads
as a form field among buttons; the app's own idiom for "choose one of these"
is `label ▾`, and this IS one of those. So: the select keeps the behaviour and
the keyboard, and borrows the look. The arrow is the wrapper's, because an
option list cannot carry one. */
.pick { position: relative; display: inline-flex; align-items: center; }
.pick > select {
appearance: none;
background: var(--pane);
padding: 1px 15px 1px 6px;
cursor: pointer;
}
.pick > select:hover { background: #fff; }
.pick::after {
content: "▾";
position: absolute;
right: 5px;
color: var(--dim);
pointer-events: none;
}
.pane-head .spacer { flex: 1; }
@ -173,6 +200,47 @@ input[type="range"] { width: 100%; accent-color: var(--sel); }
.dim { color: var(--dim); }
.warn { color: var(--warn); }
/* Buttons that are one control: a transport, a stepper, a mode picker. They
share their borders, so the group reads as a single object with parts rather
than as several things that happen to be adjacent — which is the whole claim
a segmented control makes, and the reason `timeline` and `cel sheet` are one
of these. `.seg` is `.group` with that meaning; they are drawn the same
because the difference is what the buttons do, not how they look.
The negative margin collapses the doubled border between two buttons into
one hairline. A pressed button then has to be raised, or the neighbour's
plain border covers half of its accent one. */
.group, .seg { display: flex; align-items: center; }
.group > button, .seg > button { border-radius: 0; margin-left: -1px; }
.group > button:first-child, .seg > button:first-child { margin-left: 0; border-radius: 2px 0 0 2px; }
.group > button:last-child, .seg > button:last-child { border-radius: 0 2px 2px 0; }
.group > button.on, .seg > button.on { position: relative; z-index: 1; }
/* The rule between two groups of unrelated controls. Shorter than the strip, so
it reads as a division of the row rather than as a wall across it. */
.sep { flex: 0 0 1px; width: 1px; height: 13px; background: var(--line); }
/* An icon button. The glyph is inked in `currentColor`, so `.on` colours it
with the same rule that colours a word. 11px to sit on 11px type. */
button.ico { padding: 2px 5px; line-height: 0; }
button.ico > svg { display: block; width: 11px; height: 11px; }
/* Play is the one control in the strip you aim at without looking. */
button.ico-play { padding-left: 8px; padding-right: 8px; }
/* `hold −` / `hold +`: one label over two steppers, because the word is shared
and repeating it in both buttons was most of their width. */
.stepper { display: flex; align-items: center; gap: 4px; }
.stepper-label { color: var(--dim); }
.stepper button { padding: 1px 6px; }
/* A number that changes every frame. Tabular figures stop it twitching, and
stop the controls after it being nudged about as the count passes 9 and 99. */
.readout { font-variant-numeric: tabular-nums; }
/* The answer to a refusal rather than a standing command — `lane-retry` only
exists because something was declined and named this as the way through. */
button.retry { border-color: var(--sel); color: var(--sel); }
/* --------------------------------------------------------------------------
top bar */
@ -296,6 +364,39 @@ a.brand { text-decoration: none; }
.undo > .undo-list { padding: 0 4px; margin-right: 4px; border-left: 0; border-radius: 0 2px 2px 0; }
.menu.menu-left { left: 0; right: auto; }
/* A menu dropping out of a PANE HEAD. Fixed and placed by `ui/menu` against the
button, because a pane clips its own overflow and an absolutely positioned
panel would be cut off at the pane's edge — invisibly, and more of it the
longer the menu gets. `top`/`bottom`/`left`/`max-height` all come from there.
Wide enough that a command's one-line explanation is one line. At 215px every
`.sub` in the cel menu wrapped, which doubled the panel's height for no gain:
the explanations are the reason these are menu rows and not buttons, so they
get the width to be read in. */
.menu.menu-drop {
position: fixed;
/* BOTH offsets on each axis are reset, and `ui/menu` sets exactly one of each
inline. Leaving `.menu`'s own `top` standing alongside an inline `bottom`
over-constrains the box, and a fixed box given both offsets takes its HEIGHT
from them — which, from a toolbar near the foot of the window, computes
negative and collapses the panel to its padding with the commands still
inside it. */
top: auto;
bottom: auto;
left: auto;
right: auto;
width: 272px;
overflow: auto;
}
.menu-note { padding: 1px 6px 5px; }
/* A disabled row still says what the command is FOR. That is the whole trade:
a greyed button hides its reason in a `title`, a greyed row prints it. */
.menu-item:disabled { opacity: .5; cursor: default; }
.menu-item:disabled:hover { background: none; }
.menu-item { padding: 3px 6px; }
.menu-item .sub { font-size: 10px; line-height: 1.3; white-space: normal; }
/* The one way to hand the document to somebody else, so it looks like one. */
button.share-button { background: var(--sel); color: #fff; border-color: var(--sel); font-weight: 600; padding: 1px 12px; }
button.share-button:hover, button.share-button.on { filter: brightness(1.1); }