#59 took the seek bar off the phone's transport, so the one thing a mini player is expected to say without being opened -- how far through the song it is -- had nowhere left to be said. It is the shell's element and its own 2px grid row between `bottom-bar` and `bottom-nav`, because those two are separate components and either one drawing the line means reaching into the other's box. The fill is `scaleX()` off the same `PlaybackPositionChanged` the seek bar renders, with the same `trackChangeId`/`seq` guards and an interval that only interpolates *between* reports -- never its own clock, which is the rule that exists because a local counter drifted 30 s away from the backend across four keyboard seeks. It is `aria-hidden` and takes no pointer events at any depth: Now Playing's seek bar is what announces the position, and a 2px strip on the top edge of the tab bar is exactly where a thumb aiming at a tab lands. It renders nothing above 600px, from `matchMedia` rather than a media query, because a stylesheet cannot stop a 1 Hz interval running for the life of every desktop session about a line nobody can see. Its phone rule is at the foot of index.css beside `job-band`'s, not in the phone block above: a media query adds no specificity, so a `display: block` written before the `display: none` that takes it out of the desktop grid loses to it and the line never appears at all. Closes #58
629 lines
23 KiB
CSS
629 lines
23 KiB
CSS
*,
|
||
*::before,
|
||
*::after {
|
||
-webkit-user-select: none;
|
||
user-select: none;
|
||
}
|
||
|
||
html {
|
||
height: 100%;
|
||
}
|
||
|
||
body {
|
||
background-color: var(--yj-bg-base, black);
|
||
color: var(--yj-text-primary, white);
|
||
margin: 0;
|
||
height: 100vh;
|
||
display: grid;
|
||
grid-template: "top-bar top-bar" 4em "sidebar main-panel" 1fr "bottom-bar bottom-bar" 4em / auto 1fr;
|
||
|
||
/* a11y.21 (WCAG 1.4.10), measured rather than taken as filed.
|
||
|
||
The finding's mechanism is vertical — "the 4em bars grow while
|
||
the viewport does not, and anything that no longer fits is
|
||
clipped with no scrollbar" — and that is not what happens. The
|
||
middle row is `1fr`, so it absorbs the bars exactly: at 200%
|
||
text on an 800×600 window the bars go 64px → 128px and the main
|
||
panel goes 472px → 344px, with the footer's bottom still landing
|
||
on 600. Nothing is clipped, and Settings stays reachable because
|
||
the sidebar scrolls (007 phase 5).
|
||
|
||
What is real is the other axis, which the finding does not
|
||
mention: at 200% text the shell is 1014px wide in an 800px
|
||
viewport, and at 320px (400% page zoom of 1280) it is 611px —
|
||
291px of the app unreachable behind `overflow: hidden`. So the
|
||
horizontal axis scrolls and the vertical one stays fixed, which
|
||
is also what keeps the transport bar where a desktop player's
|
||
transport bar belongs. At every size this app promises
|
||
(800×600 and up, default text) there is no overflow on either
|
||
axis and no scrollbar appears. */
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
}
|
||
|
||
|
||
|
||
/* Above the phone breakpoint the tab bar does not exist. It is in the
|
||
markup unconditionally and eagerly, for the reason notification-host
|
||
is: navigation that has to fetch a chunk before it can navigate is
|
||
not navigation. */
|
||
@media (min-width: 600px) {
|
||
bottom-nav {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
p {
|
||
margin: 0;
|
||
/* I want to set paragraph margins myself */
|
||
}
|
||
|
||
/* a11y.30. Out of flow in both states, because `body` is a grid with
|
||
named areas and an in-flow extra child is auto-placed into one of
|
||
them — the link would silently take a row from the shell. It is not
|
||
`display: none`: a skip link that is not focusable is not a skip
|
||
link. */
|
||
.skip-link {
|
||
position: absolute;
|
||
left: -9999px;
|
||
top: 0;
|
||
z-index: 100;
|
||
padding: 0.5em 1em;
|
||
background-color: var(--yj-accent);
|
||
color: var(--yj-accent-fg);
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
border-radius: 0 0 4px 0;
|
||
}
|
||
|
||
.skip-link:focus {
|
||
left: 0;
|
||
}
|
||
|
||
/* The target of that link, so it can take focus at all. `main` is not
|
||
focusable by default, and a fragment link to an unfocusable element
|
||
moves the scroll and leaves the tab sequence exactly where it was —
|
||
which is the whole thing the link exists to change. */
|
||
.main-panel:focus {
|
||
outline: none;
|
||
}
|
||
|
||
.top-bar {
|
||
grid-area: top-bar;
|
||
height: 100%;
|
||
padding-left: 2em;
|
||
padding-right: 2em;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background-color: var(--yj-bg-elevated, #343a40);
|
||
gap: 1em;
|
||
}
|
||
|
||
.top-bar search-bar {
|
||
flex: 0 1 320px;
|
||
}
|
||
|
||
/* What the bar gives up when it does not fit is decided by measuring
|
||
it (`services/top-bar-fit.ts`, #143). Two rules here are what make
|
||
that measurement mean anything.
|
||
|
||
**Nothing but the search box may shrink.** `scrollWidth` reports a
|
||
perfect fit while a child quietly truncates -- #69's trap, one
|
||
component over -- and the indicator's label is `text-overflow:
|
||
ellipsis`, so it would have absorbed the deficit and hidden it. The
|
||
search box is exempt because it shrinks between its 320px basis and
|
||
the 200px floor its own stylesheet sets, and a narrower input hides
|
||
nothing it was showing. */
|
||
.top-bar hgroup,
|
||
.top-bar library-filter,
|
||
.top-bar job-indicator {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* **The wordmark yields its width, not its existence.** It is the
|
||
app's top-level heading as well as its brand, and `display: none`
|
||
would take a document from one `h1` to none at exactly the widths
|
||
where the view's own header is the only thing left saying where you
|
||
are. This is `styles/sr-only.css.ts`'s recipe, written out because
|
||
that one is a `CSSResult` for shadow roots and this is the light
|
||
DOM. */
|
||
.top-bar hgroup.yj-collapsed {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip-path: inset(50%);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
/* The bar is `justify-content: space-between`, which with four children
|
||
spreads them evenly and left back/forward floating in the middle of
|
||
nothing. Collecting the free space *after* this one puts the pair
|
||
beside the brand, where a browser keeps them, and leaves the
|
||
right-hand group exactly as it was. */
|
||
.top-bar nav-history {
|
||
margin-right: auto;
|
||
}
|
||
|
||
ul {
|
||
list-style-type: none;
|
||
}
|
||
|
||
.title {
|
||
font-size: 1.5em;
|
||
/* Both margins, not just the bottom one. The pair is flex-centred
|
||
in a 4em bar and the UA gives an h1 a 0.67em top margin, so the
|
||
hgroup measured 67px inside 64 and the subtitle's descenders were
|
||
clipped by the bar. That was pre-existing; a11y.29 made it
|
||
visible by taking the h3's bottom margin away with it, which
|
||
shortened the block and moved the whole pair down into the clip.
|
||
This is the state that fix landed in. */
|
||
margin-block: 0;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 0.8em;
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* The same breakpoint the sidebar collapses at (AUTO_COLLAPSE_VIEWPORT
|
||
in app-sidebar.ts). The subtitle wrapped to two lines below ~780 px,
|
||
which made the title block 98 px tall inside a 4em bar and pushed it
|
||
down into the nav (H-11). */
|
||
@media (max-width: 899px) {
|
||
.subtitle {
|
||
display: none;
|
||
}
|
||
|
||
/* Back/forward is Desktop-band chrome (#6), and 900 is the same
|
||
line the sidebar's labels and the subtitle are already given up
|
||
at -- below it the shell is narrow enough that the header is
|
||
what runs out of room first. Measured at 600, the bottom of the
|
||
Compact band: the bar is 611px inside a 600px viewport *before*
|
||
this component exists (filed separately), and 695px with it, so
|
||
keeping it here would be widening a violation of the promise
|
||
that nothing scrolls sideways at a supported size.
|
||
|
||
Nothing is unreachable as a result, which is the rule that
|
||
decides it: Alt+Left / Alt+Right are global and every width has
|
||
them, the detail views keep their own back buttons, and the
|
||
phone additionally has the platform's gesture. */
|
||
.top-bar nav-history {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
body div.sidebar {
|
||
grid-area: sidebar;
|
||
background-color: var(--yj-bg-surface, #212529);
|
||
overflow: hidden;
|
||
contain: layout style paint;
|
||
}
|
||
|
||
|
||
|
||
.bottom-bar {
|
||
grid-area: bottom-bar;
|
||
padding: 0.25em;
|
||
background-color: var(--yj-bg-elevated, #343a40);
|
||
display: grid;
|
||
/* 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;
|
||
|
||
#now-playing-info {
|
||
justify-self: start;
|
||
display: flex;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
|
||
#album-art {
|
||
width: 3.5em;
|
||
min-width: 3.5em;
|
||
height: 3.5em;
|
||
min-height: 3.5em;
|
||
border-radius: 0.25em;
|
||
background-color: var(--yj-accent, #ffd43b);
|
||
}
|
||
|
||
#track-info {
|
||
display: flex;
|
||
margin-left: 1em;
|
||
flex-direction: column;
|
||
font-size: 0.75em;
|
||
justify-content: center;
|
||
text-wrap-mode: nowrap;
|
||
overflow: hidden;
|
||
|
||
/* `& 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 {
|
||
overflow: hidden;
|
||
}
|
||
|
||
& 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 {
|
||
background: none;
|
||
border: none;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
padding: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
#queue-button:hover {
|
||
color: var(--yj-accent, #ffd43b);
|
||
}
|
||
|
||
/* An open queue is a state this button can be in, and it used to
|
||
look exactly like the closed one -- so the only way to tell what
|
||
pressing it would do was to look at the other side of the window
|
||
and infer it. `aria-expanded` is the same fact for anyone not
|
||
looking at all, and it points at the panel it controls. */
|
||
#queue-button[aria-expanded='true'] {
|
||
color: var(--yj-accent, #ffd43b);
|
||
background: var(--yj-bg-overlay, #404040);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
#queue-button.drag-over {
|
||
color: var(--yj-accent, #ffd43b);
|
||
outline: 2px dashed var(--yj-accent, #ffd43b);
|
||
outline-offset: -2px;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
.content-area {
|
||
grid-area: main-panel;
|
||
display: flex;
|
||
overflow: hidden;
|
||
contain: layout style;
|
||
|
||
/* The containing block for the queue panel's overlay mode (plan
|
||
018, #24), which spans this box rather than taking width from
|
||
the main panel beside it. `contain: layout` already establishes
|
||
one; this says so on purpose, so that removing the containment
|
||
for a paint reason does not silently reparent the overlay to the
|
||
viewport. */
|
||
position: relative;
|
||
}
|
||
|
||
.main-panel {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: var(--yj-bg-surface, #212529);
|
||
overflow: hidden;
|
||
contain: layout style paint;
|
||
}
|
||
|
||
.main-panel > * {
|
||
flex: 1;
|
||
min-height: 0;
|
||
box-sizing: border-box;
|
||
contain: layout style paint;
|
||
}
|
||
|
||
/* Hidden cached views: use visibility+size collapse instead of
|
||
display:none so scroll containers preserve their scrollTop.
|
||
display:none discards scroll state in WebKitGTK. */
|
||
.main-panel > .view-hidden {
|
||
visibility: hidden !important;
|
||
flex: 0 0 0px !important;
|
||
min-height: 0 !important;
|
||
max-height: 0 !important;
|
||
height: 0 !important;
|
||
padding: 0 !important;
|
||
margin: 0 !important;
|
||
border: none !important;
|
||
overflow: hidden !important;
|
||
pointer-events: none !important;
|
||
contain: strict !important;
|
||
}
|
||
|
||
|
||
/* ===================================================================
|
||
The phone shell (plan 016 B2).
|
||
|
||
**This section is last on purpose.** A media query adds no
|
||
specificity, so `@media (max-width: 599px) { .title { … } }` placed
|
||
above the plain `.title` rule loses to it -- which is exactly what
|
||
happened when this landed in the middle of the file: the header kept
|
||
its 2em gutters, its 16px gap and its 24px title on a 390px phone,
|
||
and every one of these declarations was dead. Nothing failed,
|
||
because the shell fits for a different reason (the `min-width: 0`
|
||
below and each component's own media query), so a screenshot was
|
||
what caught it.
|
||
|
||
600px, not the sidebar's 900: 900 is a *laptop* and the response to
|
||
it is a narrower sidebar, which is still a sidebar. Below 600 there
|
||
is no room for one at all -- 360px of viewport over a 200px nav is
|
||
not a layout -- so the navigation moves to the bottom, where a thumb
|
||
is, and the eleven-item list moves into `bottom-nav`'s drawer.
|
||
=================================================================== */
|
||
@media (max-width: 599px) {
|
||
body {
|
||
/* **There is no top-bar row here (#57).** Every one of the five
|
||
things that bar held has somewhere else to be below 600px:
|
||
`nav-history` is the platform's own gesture (gone from 899
|
||
down), the job indicator is `<job-band>` (#62), the search
|
||
box is a modal opened from the view's own header
|
||
(`search-trigger`), the library filter is Settings ->
|
||
Libraries (#148), and the wordmark is below. That is 3.25em
|
||
of a 439 CSS px viewport -- the single biggest vertical win
|
||
available on the reference device, which is why #57 asks for
|
||
the row rather than for a smaller bar. */
|
||
grid-template:
|
||
"jobs-band" auto
|
||
"main-panel" 1fr
|
||
"bottom-bar" auto
|
||
"progress-line" auto
|
||
"bottom-nav" auto
|
||
/ 1fr;
|
||
/* Nothing may scroll sideways here. On a desktop the shell is
|
||
allowed to overflow a zoomed-in window (a11y.21 above); a
|
||
phone *is* the small viewport, so the shell has to fit it. */
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
body div.sidebar {
|
||
display: none;
|
||
}
|
||
|
||
bottom-nav {
|
||
grid-area: bottom-nav;
|
||
}
|
||
|
||
/* The bar is out of the layout, and out of it the way the *wordmark*
|
||
already goes at desktop widths: visually hidden rather than
|
||
`display: none`, because that `h1` is the document's top-level
|
||
heading and this app would otherwise have none on the pages whose
|
||
own header is empty by design (`page-header` renders no `h1` when
|
||
`heading` is '', and Settings has no `page-header` at all).
|
||
|
||
Its four *controls* are `display: none` below, which is what
|
||
keeps them out of the tab order -- a visually-hidden container is
|
||
still focusable, and tabbing into a search box nobody can see is
|
||
worse than not having one.
|
||
|
||
This is `styles/sr-only.css.ts`'s recipe again, written out
|
||
because that one is a `CSSResult` for shadow roots and this is
|
||
the light DOM. `position: absolute` is also what tells
|
||
`services/top-bar-fit.ts` there is no row to fit into. */
|
||
.top-bar {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip-path: inset(50%);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
gap: 0;
|
||
min-width: 0;
|
||
}
|
||
|
||
.top-bar nav-history,
|
||
.top-bar library-filter,
|
||
.top-bar search-bar,
|
||
.top-bar job-indicator {
|
||
display: none;
|
||
}
|
||
|
||
/* `min-width: 0` is load-bearing wherever a box sits between the
|
||
viewport and content that must shrink. A grid item's implicit
|
||
minimum is `auto` -- its content -- so one child insisting on
|
||
580px makes the *body* 580px wide inside a 360px viewport, and
|
||
`overflow-x: hidden` then hides the right-hand third of the app
|
||
rather than fitting it. */
|
||
.content-area,
|
||
.main-panel,
|
||
.bottom-bar {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* The full-screen now-playing view *is* the transport, so the bar
|
||
repeating it underneath is 4em of a small screen spent saying
|
||
the same thing twice -- visible in a screenshot, invisible to
|
||
every assertion about either one.
|
||
|
||
`:has()` rather than a class toggled from index.ts: which view
|
||
is showing is already published as an attribute, and a second
|
||
expression of the same fact is a second thing to keep in step.
|
||
The view carries its own queue button, because this is where
|
||
that one lived. */
|
||
body:has(#main-content[data-active-view="now-playing"]) .bottom-bar,
|
||
body:has(#main-content[data-active-view="now-playing"]) player-progress-line {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@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;
|
||
}
|
||
|
||
.bottom-bar audio-player {
|
||
margin: 0.25em;
|
||
}
|
||
|
||
/* Volume stands down here whatever the setting says, because this
|
||
is about room: five controls and a slider do not fit a 360px
|
||
bar, and the full-screen now-playing view is where seeking and
|
||
volume go on a phone. 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.
|
||
|
||
**This rule used to carry the platform argument too, and no
|
||
longer does** (#64). "The hardware keys own the volume" is not a
|
||
width: it is false of a narrow desktop window and true of an
|
||
Android tablet, which this selector gets backwards both ways.
|
||
The player answers it now — `SystemOwnsVolume` — and
|
||
`volume-control` renders nothing when it is true, at every
|
||
width and in both of its mount points. What is left here is the
|
||
question a stylesheet can actually answer. */
|
||
.bottom-bar volume-control {
|
||
display: none;
|
||
}
|
||
|
||
/* The queue leaves the phone's bar (#59), because #55 made it a
|
||
screen with an entry in the back stack and Now Playing already
|
||
carries its own button for it. The route is the mini player's
|
||
art -> Now Playing -> the queue, which is the "reachable only
|
||
from Now Playing" this issue asks for.
|
||
|
||
This is allowed to remove a control only because the control is
|
||
still reachable: plan 018's matrix promises that no action is
|
||
ever unreachable at any supported size, and that promise is what
|
||
`phone-transport.spec.ts` asserts rather than the button count.
|
||
|
||
**`.bottom-bar #queue-button`, not `#queue-button`**, and that is
|
||
not decoration. The rule this overrides is written *nested*
|
||
inside `.bottom-bar`, so it builds to a descendant selector one
|
||
class more specific than it looks in the source -- and a bare
|
||
`#queue-button` here loses to it, media query or not. Being last
|
||
in the file is not enough when the thing above is more specific,
|
||
which is the same lesson as this section's own header one level
|
||
down: nesting adds specificity the source does not show, and the
|
||
failure is silent (the button simply stayed). */
|
||
.bottom-bar #queue-button {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* Out of the desktop grid entirely. `job-band` renders nothing above
|
||
600px anyway, but an in-flow grid child with no named area is
|
||
auto-placed into a row of the shell -- the same trap the skip link is
|
||
absolutely positioned to avoid. `player-progress-line` (#58) is the
|
||
same element in the same position for the same reason: below 600px it
|
||
has a named row, and above it there is no border for it to sit on --
|
||
the desktop bar carries a real, interactive seek bar. */
|
||
body job-band,
|
||
body player-progress-line {
|
||
display: none;
|
||
}
|
||
|
||
/* #62. The job indicator stands down on the phone, and its work is
|
||
shown in the notification band instead (notification-host).
|
||
|
||
Three reasons, and the first is the report: its popover is anchored
|
||
to the top bar, which is 3.25em here on a viewport 439 CSS px tall,
|
||
and it was reported as unreadable behind other UI. The second is
|
||
that a popover is a disclosure, and background work is the one thing
|
||
a phone should not make you disclose. The third is #57, which
|
||
deletes this bar entirely and is blocked on the indicator having
|
||
somewhere else to live -- this is that somewhere.
|
||
|
||
#57 has since done exactly that, so the indicator's own rule now
|
||
lives with the other three in the phone block above, where the bar
|
||
goes out of the layout in one statement rather than four. What stays
|
||
here is the band, and the argument for it. */
|
||
@media (max-width: 599px) {
|
||
/* The indicator's rows appear here, in the grid row above the content.
|
||
In flow rather than over it: a fixed band reads fine in a
|
||
screenshot and is unusable, because at 424x439 a compact panel
|
||
is ~200px of a 439px screen and it *covers* what is under it.
|
||
Measured, not assumed -- four e2e specs failed on that version,
|
||
two phone-shell journeys and the header's action menu, because
|
||
the panel was intercepting the taps. */
|
||
body job-band {
|
||
display: block;
|
||
grid-area: jobs-band;
|
||
background-color: var(--yj-bg-elevated, #343a40);
|
||
}
|
||
}
|
||
|
||
/* #58. How far through the song we are, in its own grid row between
|
||
the two bars -- so the line is *on* the border rather than inside
|
||
either of them, and in flow rather than over it. The row is `auto`
|
||
and the element renders nothing while no track is loaded, so it costs
|
||
no height at all until there is something to say.
|
||
|
||
**This block is below the `display: none` above and has to be**, for
|
||
the reason the band's rule is: a media query adds no specificity, so
|
||
`body player-progress-line { display: block }` written before that
|
||
rule loses to it at equal specificity and the line never appears at
|
||
any width. Nothing fails; it is simply not there. */
|
||
@media (max-width: 599px) {
|
||
body player-progress-line {
|
||
display: block;
|
||
grid-area: progress-line;
|
||
}
|
||
}
|