feat(player): centre the transport and show the volume inline
Two issues over one bar, because they are one relayout. #42's own findings say so: giving wa-slider a label grows it 6px to 14px and moves the transport, which is #23's subject, so doing them in sequence means measuring the bar twice and throwing the first set away. The bar was `320px 1fr auto`, so the transport sat in the middle of what the metadata and the queue button did not use — its centre was ~140px right of the window's at every width. The outer two tracks are the same expression now, so the middle is centred by construction. The side width is the metadata's, capped at a quarter of the bar, and the cap was measured as a regression before it was a decision: reserving the full `--now-playing-width` on both sides is perfectly centred and takes the seek bar's track from 257px to 61px at 800px, and to 0 at 200% text. The control you drag was paying for the symmetry. With the cap it is 246, which is parity. It is a `min()` rather than a breakpoint because that variable is user state — the metadata has a drag handle — and tying both sides to it is also what keeps dragging meaningful; a plain `1fr … 1fr` centres just as well and silently makes the handle a no-op. The volume moved out of `audio-player` into the bar because the transport column has to hold the transport and nothing else, and it joins the queue button in one cell rather than a second column, since the centring compares columns. It is a slider by default and a popup by setting. The stored flag names the *popup*, which is this config's polarity rule — the zero value has to be the intended answer, so an existing config.toml gets the new default with no migration. Inline, the icon is the mute toggle and is named after that action rather than the state, because with the slider beside it there is nothing to disclose; the component tier now covers both presentations rather than whichever is default. Three nested rules in this block began with a bare element selector, which Chrome 120 relaxed and the phone's Chrome 113 **silently drops** — including the ellipsis on the bar's own title and artist, which has therefore never truncated on the device. They are `&`-prefixed now. Filed as #154 for the class and for a check. `bottom-bar.spec.ts` pins both halves separately on purpose: an uncapped build is perfectly centred and fails only the seek-bar width, so a spec asserting centring alone would have passed the regression above. Both were verified by mutation. Closes #23 Closes #42
This commit is contained in:
+78
-5
@@ -211,7 +211,39 @@ body div.sidebar {
|
||||
padding: 0.25em;
|
||||
background-color: var(--yj-bg-elevated, #343a40);
|
||||
display: grid;
|
||||
grid-template-columns: var(--now-playing-width, 320px) 1fr auto;
|
||||
/* Three columns whose outer two are the *same* width, which is what
|
||||
centres the middle one (#23). It was `var(--now-playing-width) 1fr
|
||||
auto`, so the transport's centre sat at `W/2 + 140px` — in the
|
||||
middle of the space left over, which is not the same thing and
|
||||
reads as an alignment mistake at every window size.
|
||||
|
||||
The outer width is still `--now-playing-width`, so **the metadata
|
||||
panel's drag handle keeps meaning something**: widening it takes
|
||||
room from the transport on both sides at once, symmetrically. An
|
||||
`1fr … 1fr` pair would have centred the transport just as well and
|
||||
silently made that handle a no-op.
|
||||
|
||||
**The cap is what stops that being a regression**, and it was
|
||||
measured as one first. Reserving the full metadata width on both
|
||||
sides costs the transport twice: at 800px the outer pair wanted
|
||||
640 of 800, and the seek bar's track went from 257px to 61px
|
||||
(and to 0 at 200% text) — the control you drag, squeezed out to
|
||||
centre the buttons above it. So the side tracks are the metadata
|
||||
width *or a quarter of the bar*, whichever is smaller, which
|
||||
leaves the drag handle meaningful everywhere it has room to be
|
||||
and hands the difference to the transport where it does not.
|
||||
|
||||
`minmax(0, …)` on the outer tracks and `min-content` on the middle
|
||||
decide who yields when even that is not enough: the metadata and
|
||||
the end group shrink (both truncate; neither loses an action), and
|
||||
the transport keeps at least its buttons. Without the `min-content`
|
||||
floor the middle collapses first, because a `1fr` track's minimum
|
||||
is `auto` only until something else insists. */
|
||||
--bar-side: min(var(--now-playing-width, 320px), 25%);
|
||||
grid-template-columns:
|
||||
minmax(0, var(--bar-side))
|
||||
minmax(min-content, 1fr)
|
||||
minmax(0, var(--bar-side));
|
||||
align-items: center;
|
||||
contain: layout style;
|
||||
|
||||
@@ -239,23 +271,44 @@ body div.sidebar {
|
||||
text-wrap-mode: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
p {
|
||||
/* `& p`, not `p`. **A nested rule that begins with a bare
|
||||
element selector is silently dropped before Chrome 120**
|
||||
(relaxed nesting), and the phone this app runs on renders
|
||||
in Chrome 113 -- so this ellipsis, and the two rules
|
||||
below, have never applied on the device. Nothing fails;
|
||||
the text simply overflows there. The `&` form is valid in
|
||||
both, which is why it is used for every element selector
|
||||
in this file's nested blocks. */
|
||||
& p {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
now-playing {
|
||||
& now-playing {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
audio-player {
|
||||
& audio-player {
|
||||
margin: 0.5em 1em;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* The right-hand group, and the thing the left column is matched
|
||||
against. It is one grid cell rather than two columns because the
|
||||
centring rule above compares *columns*: volume and the queue
|
||||
button in separate tracks would make the outer pair unequal by
|
||||
whatever the volume happens to measure. */
|
||||
.bar-end {
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#queue-button {
|
||||
justify-self: end;
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
@@ -440,6 +493,11 @@ body div.sidebar {
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
/* The phone keeps the two-part bar it had: metadata, then the
|
||||
transport and the queue button. There is no third column to
|
||||
balance because the centring the desktop does is a luxury of
|
||||
having room — at 360px the metadata needs all of the space the
|
||||
controls do not. */
|
||||
.bottom-bar {
|
||||
grid-template-columns: minmax(0, 1fr) auto auto;
|
||||
gap: 0.25em;
|
||||
@@ -448,4 +506,19 @@ body div.sidebar {
|
||||
.bottom-bar audio-player {
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
/* Volume stands down here whatever the setting says, because this
|
||||
is about room and about the platform rather than about
|
||||
preference: the hardware keys own volume on a phone, which is
|
||||
also why mediacontrols' Android handler implements no volume
|
||||
callback. It moved from `audio-player`'s own media query when
|
||||
#42 moved the control into the bar — same rule, and now stated
|
||||
where the element actually is.
|
||||
|
||||
`.bottom-bar volume-control`, not the one in
|
||||
`now-playing-view`: that view is the phone's transport and is
|
||||
where a slider does belong. */
|
||||
.bottom-bar volume-control {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user