Files
op-packages/luci-theme-footstrap/styles/theme/35-alerts.css
T

221 lines
14 KiB
CSS

@layer theme {
/* Alert panel: a left accent stripe on a panel card; the type variants recolour the frame.
* `background: var(--fs-panel)` used to be repeated per variant to out-specify base's
* gradients — a layer does that for free (theme beats base whatever the specificity), so
* one rule does it. */
.alert-message {
padding: var(--fs-space-3-5) var(--fs-space-4);
border-radius: var(--fs-radius-lg);
border: var(--fs-hairline);
/* logical: the stripe marks the READING start of the alert, so it swaps sides with the
* document (LuCI ships ar/fa/he catalogues) — physical, it sat on the far edge in RTL. */
border-inline-start: 3px solid var(--fs-accent);
/* The surface is OPAQUE and the status tint rides on top of it. A variant that replaces the
* background with `var(--fs-*-fill)` paints an 18% wash with nothing behind it — invisible on
* a flat canvas, and transparent to the wallpaper under Appearance -> Wallpaper, where the
* tile reads straight through every status alert.
*
* So --fs-panel stays the background-COLOUR for every variant and the tint is a
* background-IMAGE layer over it. The named `-fill` step is unchanged, so an untinted router
* sees the same pixels, and the local `--fs-alert-tint` is what keeps this one rule instead
* of six `background:` overrides that each have to re-state the panel. */
background-color: var(--fs-panel);
background-image: linear-gradient(var(--fs-alert-tint, transparent), var(--fs-alert-tint, transparent));
color: var(--fs-text);
box-shadow: var(--fs-shadow);
gap: var(--fs-space-3);
}
/* ---- status lives in the stripe and the surface; the ring is always neutral ----
*
* Split between `border-color` (all four sides, including the 3px stripe) and
* `border-left-color` (the stripe alone), four of seven alerts wear a coloured ring and three a
* grey one — a hierarchy nobody designed, with the difference between "not applied yet" and
* "this is about to fail" hanging on one pixel of frame.
*
* The surface carries it instead, at the ladder's named `-fill` step, which
* docs/design-system.md describes as the callout surface — exactly what an alert is. Not a fresh
* `color-mix()`: an unnamed level is how four spellings of one tint appeared here before.
*
* `:not(.btn)` is load-bearing: `admin/system/flash` renders its confirm control as
* `<label class="btn alert-message danger">`, a BUTTON wearing the alert classes, and without
* the guard the tint out-ranks `.btn.danger` on source order: the "Force upgrade" control faded
* from its danger fill (rgb(234,122,116)) to a pale wash. */
.alert-message.warning:not(.btn) {
border-color: var(--fs-border); border-inline-start-color: var(--fs-warn);
--fs-alert-tint: var(--fs-warn-fill);
}
.alert-message.error:not(.btn), .alert-message.danger:not(.btn) {
border-color: var(--fs-border); border-inline-start-color: var(--fs-danger);
--fs-alert-tint: var(--fs-danger-fill);
}
.alert-message.success:not(.btn) {
border-inline-start-color: var(--fs-good);
--fs-alert-tint: var(--fs-good-fill);
}
/* `.info` is a STATUS and tints like the others; a bare .alert-message stays a flat panel, because
* a note is not a status.
*
* `.notice` is grouped HERE rather than with .success, where it is pixel-identical — same fill,
* same rail, no other channel — so one status word carries two meanings while the theme's own
* `.label.notice` is accent and `.cbi-tooltip.notice` neutral. What settles the direction is
* what LuCI means by it: ui.js emits `notice spinning` for the "applying configuration changes"
* banner, i.e. IN PROGRESS rather than done, and stock luci-theme-bootstrap keeps green for
* success alone. Informational, so accent. */
.alert-message.info:not(.btn), .alert-message.notice:not(.btn) {
border-inline-start-color: var(--fs-accent);
--fs-alert-tint: var(--fs-accent-fill);
}
/* ---- a spinning alert is a glyph and a message, side by side ----
*
* The spinner is a `::before` on the element carrying `.spinning` (base/95-luci.css), and it is
* `inline-block` so that it sits in the flow of the button labels and `em.spinning` texts that
* usually host it. This host is neither: `ui.changes.displayStatus()` builds the apply dialog as
* a BLOCK box whose content is a BLOCK element, so the glyph opens a line box of its own and the
* message goes underneath it.
*
* Grid rather than flex, because the caller decides how many children there are and flex would
* lay a second one out beside the first: `displayStatus()` passes one `<p>` today but builds two
* elements for its non-spinning states, and `addNotification()` takes `spinning` from anyone.
* Two columns — glyph, then everything else. Measured over the three shapes a caller can
* produce: the content started 24.6 / 20.6 / 19.5px BELOW the glyph's line and now starts on it.
*
* The notification variant is deliberately untouched: ui.js writes `style="display:flex"` on the
* element itself, and an inline style beats a layered rule, so those keep their own flex row. */
.alert-message.spinning {
display: grid;
grid-template-columns: auto 1fr;
/* centres the glyph against the FIRST row rather than pinning it to that row's top */
align-items: center;
/* `.alert-message`'s own `gap` above is the row gap between stacked children; the column gap
* is the glyph's own spacing and is the smaller step, so it is stated rather than inherited
* from that shorthand. */
column-gap: var(--fs-space-2);
}
/* the glyph is a grid item now, so its inline margin would double the column gap; and it is
* pinned to row 1 so that it stays beside the FIRST child however many follow */
.alert-message.spinning::before { grid-row: 1; margin-inline-end: 0; }
.alert-message.spinning > * { grid-column: 2; }
/* h2 is what the theme's own notices emit (the document had no heading above h4 at all); h4 is
* here because a third-party alert may use it, and coverage only grows. h5/h6 joined for the
* same reason — base styled them and the theme did not, so an alert heading looked like an
* alert heading down to h4 and then fell back to base's. */
.alert-message :is(h2, h3, h4, h5, h6) { margin: 0 0 var(--fs-space-1); font-size: var(--fs-type-lg); font-weight: var(--fs-weight-bold); color: var(--fs-text); }
/* the alert's own content reset — moved from base with the heading ramp (45-misc.css): its
* `line-height: inherit` beat the ramp's tight leading in-layer, and left behind it would
* lose to the ramp by layer (measured: alert headings dropped 21 -> 16.8px) */
.alert-message h4,
.alert-message h5,
.alert-message pre,
.alert-message ul,
.alert-message li,
.alert-message p {
border: none;
line-height: inherit;
background: transparent;
padding: 0;
}
/* --fs-text, not --fs-dim: the paragraph IS the message. The line below already gives every
* ul/ol/li/pre inside an alert full-strength ink, so a two-sentence alert had its first
* sentence muted and its bullet list not — and on the tinted surface above, dim is the value
* that measured 4.06:1 when the Reset button had it (55-buttons.css). */
.alert-message p { margin: var(--fs-space-1) 0; color: var(--fs-text); font-size: var(--fs-type); }
/* …and the body elements an alert may carry. Absorbed from base/90-widgets.css, same values:
* the theme had claimed only <p> out of that rule's six selectors. */
.alert-message :is(ul, ol, li, pre) { color: var(--fs-text); margin: .25em 0; }
/* inner table (firmware Current/Available) — flatten so it does not read as a nested card
* on the alert panel; subtle row dividers only */
.alert-message .table { margin: var(--fs-space-2-5) 0; background: transparent; border: 0; box-shadow: none; border-radius: 0; }
/* --fs-text, not --fs-eyebrow-color: everywhere else an eyebrow sits on --fs-panel, but here the
* surface is a status tint, and the muted ink does not survive it — measured on the firmware
* table over the warning fill, hicontrast/light: 3.35-3.36:1 against AA's 4.5. The eyebrow still
* reads as one from its tracking and size; only the ink changes. */
.alert-message .table .th,
/* The title-row selectors are not decoration: `.table .tr.table-titles .th` in 30-tables.css is
* FOUR classes (0,4,0) and out-ranks the plain one above (0,3,0), so without these the header
* kept the eyebrow ink no matter what this rule said — being later in the sheet buys nothing
* against a heavier selector, which is the point of the "never win on source order" rule. */
.alert-message .table .tr.table-titles .th,
.alert-message .table .tr.cbi-section-table-titles .th { color: var(--fs-text); font-weight: var(--fs-eyebrow-weight); font-size: var(--fs-type-xs); letter-spacing: var(--fs-eyebrow-tracking); }
/* the column gap is --fs-space-3 like every other cell in the theme; --fs-space-3-5 here was the
* only 14px cell gap in the sheet, and nothing above defends it — the comment covers flattening
* the frame, not the rhythm. (The row density stays --fs-space-2: a table inside an alert is a
* dense readout, same as the config tables that state it deliberately.) */
.alert-message .table .th, .alert-message .table .td { padding: var(--fs-space-2) var(--fs-space-3) var(--fs-space-2) 0; background: transparent; border-color: color-mix(in srgb, var(--fs-border) 60%, transparent); }
.alert-message .table .td { color: var(--fs-text); }
/* The validation tooltip (ui.js pops it on a .cbi-tooltip-container). Its colour words were the
* one status surface the theme never claimed, and nothing could see it: the gallery rendered a
* plain `.cbi-tooltip` only, and an un-rendered widget shows no diff, which reads as "already
* dead". The gallery renders all four now.
*
* The fill stays SOLID, unlike `.label` below: a tooltip floats over arbitrary content, so a
* border carrying the status would have to read against whatever happens to be behind it. The
* ink is the role's own `--fs-on-*`, which 03-palettes.css sizes per palette and per mode — a
* global white failed AA on seven of the eight dark-palette fills. */
/* One elevation for the widget. base lit it with a raw 2px halo in the hairline colour, which
* measures 1.08-1.86:1 against the surfaces a tooltip lands on, while the theme overrode the
* single instance somebody happened to look at. A tooltip floats over ARBITRARY content — the
* same reason the colour variants below stay opaque — so it is the last surface that should be
* separated from the page by a hairline. */
.cbi-tooltip { box-shadow: var(--fs-shadow-pop); }
/* base reveals it on :hover alone. The container is `cursor: help` and carries no tabindex in
* anything LuCI ships, so nothing can focus it today and this matches nothing — but the
* focusability belongs to ui.js, and a third-party app that gives its own tooltip container a
* tabindex gets a keyboard-reachable tooltip the day it does. */
.cbi-tooltip-container:focus-within .cbi-tooltip:not(:empty) { inset-inline-start: auto; opacity: 1; }
.cbi-tooltip.info { background: var(--fs-accent); border-color: var(--fs-accent); color: var(--fs-on-accent); }
.cbi-tooltip.success { background: var(--fs-good); border-color: var(--fs-good); color: var(--fs-on-good); }
.cbi-tooltip.error { background: var(--fs-danger); border-color: var(--fs-danger); color: var(--fs-on-danger); }
.cbi-tooltip.notice { background: var(--fs-panel2); border-color: var(--fs-border); color: var(--fs-text); }
/* The `.label` pill. LuCI core barely uses it, but any luci-app-* may reach for it, so the
* theme owns it in tokens (base's copy is absorbed). `.important` is the conventional name
* for what we call accent; `.danger` has no counterpart there and is added because
* packages use it. See docs/gallery.html. */
.label {
display: inline-block; padding: var(--fs-space-1) var(--fs-space-2); border-radius: var(--fs-radius-pill);
font-size: var(--fs-type-xs); font-weight: var(--fs-weight); line-height: 1.4;
background: var(--fs-panel2); border: var(--fs-hairline);
color: var(--fs-dim); text-shadow: none; text-transform: none;
/* carried over from the absorbed styles/base .label rule */
white-space: nowrap; margin: .125em 0 .125em .4em;
}
/* The variants set no background, deliberately: they inherit the opaque --fs-panel2 from `.label`
* above. A translucent tint of their own colour under text of that colour drags the background
* toward the text and eats the contrast it needs, and being translucent it renders differently
* on every surface, so no single percentage is safe — measured across palette x mode, every
* variant landed at 3.4-4.4:1. On --fs-panel2 the palette guarantees >=4.5 by construction, and
* the coloured border carries the status just as loudly. */
.label.important, .label.notice { border-color: var(--fs-accent); color: var(--fs-accent); }
.label.warning { border-color: var(--fs-warn); color: var(--fs-warn); }
.label.success { border-color: var(--fs-good); color: var(--fs-good); }
.label.danger, .label.error { border-color: var(--fs-danger); color: var(--fs-danger); }
a.label:link, a.label:visited { color: var(--fs-dim); }
a.label:hover { text-decoration: none; color: var(--fs-text); }
/* The show/hide toggles in a section title are .label pills too, but clickable. SINGLE
* owner of the pill look: pages/20-overview.css used to restate it (page layer, unscoped —
* so it silently won everywhere) with drifted values; these are the ones that were actually
* rendering. The trailing overrides (text-transform … box-shadow) kill the uppercase /
* margin / min-width / badge decorations base used to put on these pills. */
.cbi-title .label {
font-size: var(--fs-type-xs); font-weight: var(--fs-weight);
padding: var(--fs-space-1) var(--fs-space-3); border-radius: var(--fs-radius-pill);
background: var(--fs-panel2); border: var(--fs-hairline);
color: var(--fs-dim); cursor: pointer; line-height: 1.4;
text-transform: none; letter-spacing: 0;
margin: 0; min-width: 0; box-shadow: none;
}
.cbi-title .label:hover { color: var(--fs-text); border-color: var(--fs-dim); opacity: 1; }
/* These pills toggle section visibility — interactive, so keyboard focus must show. */
.cbi-title .label:focus-visible {
outline: none; border-color: var(--fs-accent); box-shadow: var(--fs-focus-ring);
}
/* Surface stays OPAQUE (--fs-panel2 inherited from the base pill): accent text on
* --fs-accent-soft is the accent-on-its-own-tint AA failure measured at 4.21:1 in the open
* dropdown (theme/65-dropdown.css) — the border carries the accent instead. */
.cbi-title .label.notice { opacity: 1; color: var(--fs-accent); border-color: var(--fs-accent); }
}