Files
action a97651184f
marry-jell / merge (push) Canceled after 0s
update 2026-11-01 11:53:54
2026-11-01 11:53:54 +08:00

252 lines
14 KiB
CSS

@layer page {
/* Every !important here fights an inline style= from luci-mod-status's 29_ports.js — page is
* the last layer, so a flag fighting only another footstrap rule is not kept. docs/css.md
* "Overview: the port-tile reskin". */
/* decoration only, glyph aria-hidden — docs/css.md "Overview: the disclosure pill, the grid,
* and the duplicate heading" */
.cbi-title .label[data-indicator="poll-status"] {
font-size: 0;
width: 16px;
height: 16px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: 0;
color: var(--fs-faint);
}
/* two selectors: wrapper differs by release (25.12 .cbi-title>h3, 24.10 bare h3); matches 0 of
* 14 h3 on live 24.10.8 — not dead there, that release has no hide/show control */
:is(.cbi-title h3, .cbi-section > h3)[role="button"] {
cursor: pointer;
-webkit-user-select: none; /* every shipped Safari/iOS reads only the prefixed form */
user-select: none; /* rapid header clicks read as a double-click and select the card */
border-radius: var(--fs-radius-sm);
}
:is(.cbi-title h3, .cbi-section > h3)[role="button"]:focus-visible {
outline: none;
box-shadow: var(--fs-focus-ring);
}
/* every page-scoped selector below anchors on :where(#view)[data-page="…"], not bare #view —
* docs/css.md ":where(#view), not bare #view". */
/* drops what hideEmptyCard() marked (a stock include with nothing to show) and the stock <h2> duplicate of the chrome's title, the latter via .fs-content not #view */
:where(#view)[data-page="admin-status-overview"] .cbi-section.fs-ovl-empty,
.fs-content[data-page="admin-status-overview"] h2[name="content"] { display: none; }
.fs-ovl {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--fs-space-4);
margin-bottom: var(--fs-card-gap); /* match stock .cbi-section spacing below the grid */
}
/* min-width:0 so an item shrinks to its TRACK, not its content's min-width — docs/css.md above */
.fs-ovl > .fs-ovl-sys,
.fs-ovl > .fs-ovl-mem,
.fs-ovl > .fs-ovl-sto { margin: 0; min-width: 0; }
.fs-ovl > .fs-ovl-sys { grid-column: 1; grid-row: 1 / 3; }
.fs-ovl > .fs-ovl-mem { grid-column: 2; grid-row: 1; }
.fs-ovl > .fs-ovl-sto { grid-column: 2; grid-row: 2; }
@container fs-view (max-width: 800px) {
.fs-ovl { grid-template-columns: 1fr; }
.fs-ovl > .fs-ovl-sys,
.fs-ovl > .fs-ovl-mem,
.fs-ovl > .fs-ovl-sto { grid-column: 1; grid-row: auto; }
}
/* Port status reskin. Scoped to :has(img[src*="/port_"]) — do not unscope, Network/Wireless
* reuse the same .ifacebox classes. Child order: 1 name-head, 2 speed, 3 zone bar, 4 traffic. */
:where(#view)[data-page="admin-status-overview"] div:has(> .ifacebox img[src*="/port_"]) {
/* 8px not 10: an 11-port switch in a 1190px column fits 11 cards only at 8px gap */
gap: var(--fs-space-2);
grid-template-columns: repeat(auto-fit, minmax(calc(76px * var(--fs-density-type) + var(--fs-space-3) * 2), 1fr)) !important;
margin-bottom: 0 !important;
}
/* flex-wrap, not a grid+@container threshold (issue #7) — docs/css.md above */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) {
margin: 0 !important;
min-width: 0 !important;
/* caps a 2-port device growing into two half-page cards; fights stock's inline max-width */
max-width: 200px !important;
/* ImmortalWrt 25.12 sets inline margin/width instead of stock's min/max pair */
width: auto !important;
/* same fork centres the grid CONTAINER; *-self wins over parent *-items, no flag needed */
justify-self: stretch;
align-self: stretch;
text-align: start;
background: var(--fs-panel2);
border: var(--fs-hairline);
border-radius: var(--fs-radius-lg);
padding: var(--fs-space-1-5) var(--fs-space-3);
/* no container-type: broke poll-replaced cards before containment resolved (bisected) */
display: flex;
/* NOT redundant: base/95-luci.css sets .ifacebox inline-flex column */
flex-direction: row;
flex-wrap: wrap;
/* row-gap 0: every row carries its own spacing already (zone bar margins, name line box) */
column-gap: var(--fs-space-2); row-gap: 0;
align-items: baseline;
}
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) img { display: none; }
/* two stock border-bottoms stack around the colour line; drop both */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) .ifacebox-head { border-bottom: 0; border-radius: 0; }
/* zone-colour bar (WAN red/LAN green, issue #13); order places it between name and traffic.
* No overflow/border-radius: both clip the absolutely-positioned .cbi-tooltip */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-head:nth-child(3) {
order: 1;
flex: 0 0 100%;
width: 100%;
margin: var(--fs-space-0-5) 0 var(--fs-space-1);
}
/* no status dot: speed + zone bar report carrier */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-head:first-child {
flex: 0 0 auto;
width: 100%;
/* one line (nowrap from .ifacebox), ellipsised: without min-width: 0 a long name sets every tile's min width */
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
line-height: var(--fs-leading);
font-size: var(--fs-type); color: var(--fs-text);
}
/* no link (*_down.svg): dim to match the dim speed and faded zone bar */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]):has(img[src*="_down.svg"]) > .ifacebox-head:first-child { color: var(--fs-dim); }
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-body:nth-child(2) {
order: 2; /* below the zone-colour line (child 3, order 1) */
font-size: var(--fs-type); font-weight: var(--fs-weight); color: var(--fs-good); text-align: start;
/* margin-left:auto forces its own row in Chrome's line-break count; flex-grow instead */
flex: 1 1 auto; white-space: normal; overflow-wrap: break-word;
}
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-body:nth-child(2) br { display: none; }
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]):has(img[src*="_down.svg"]) > .ifacebox-body:nth-child(2) { color: var(--fs-dim); }
/* traffic: tx over rx, as stock's <br> leaves them — CSS cannot reorder br-separated text.
* Size flag fights the inline font-size:80%. */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-body:nth-child(4) {
order: 2; /* shares the speed's row, below the zone line */
text-align: end;
/* grows to anchor text-align:end, never shrinks (nowrap figures) */
flex: 1 0 100%;
}
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-body:nth-child(4) > .cbi-tooltip-container {
font-family: var(--fs-font-mono);
/* widest thing a card must fit whole, sets the card's floor; fights inline font-size:80% */
font-size: var(--fs-type-2xs) !important;
color: var(--fs-dim);
line-height: var(--fs-leading-tight);
white-space: nowrap;
/* fights the element's own inline text-align:left */
text-align: end !important;
}
/* 25.12+ PoE line: inline color:#000 is 1.65:1 on the dark tile (#2d333b); beats it, needs the flag */
:where(#view)[data-page="admin-status-overview"] .ifacebox:has(img[src*="/port_"]) > .ifacebox-body:nth-child(4) > .cbi-tooltip-container > span[style*="color:#000"] { color: var(--fs-text) !important; }
/* meter rows — a key/value-shaped .table row with a .cbi-progressbar (same shape test as
* :218 below and 30-tables.css's key/value rule); a data table's own bar stays untouched.
* docs/css.md above. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))):has(.cbi-progressbar) { display: block; }
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--fs-space-1) var(--fs-space-2-5);
/* halved from 8/10: the outer padding duplicated the gap. Bottom stays larger for the bar. */
padding: var(--fs-space-1) 0 var(--fs-space-1-5);
border: 0;
position: relative;
}
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar):last-child { padding-bottom: var(--fs-space-0-5); }
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .td {
display: block;
/* beats base's .td[width="33%"] with no flag: that sets a width ATTRIBUTE, a later layer wins */
width: auto;
padding: 0;
border: 0;
}
/* label — undoes key/value nowrap: a mount point is one unbreakable line past the card.
* padding-inline-end is the gutter for the out-of-flow value (::after over the bar's edge). */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .td:first-child {
grid-column: 1; grid-row: 1;
align-self: center;
min-width: 0;
white-space: normal;
padding-inline-end: min(230px, 50%);
font-size: var(--fs-type);
}
/* value rides the label's own line at desktop (::after floats over the row's top edge) —
* opt out of the bare-meter default reserve. <=560px below restores it (own line, no room). */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .cbi-progressbar {
margin-block-start: 0;
}
/* narrow card: the value takes its own line, the reserve above cannot fit it — docs/css.md
* above has the 218px/190px measurement. */
@container fs-view (max-width: 560px) {
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .td:first-child {
padding-inline-end: 0;
}
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .cbi-progressbar {
margin-block-start: calc(var(--fs-type-xs) * var(--fs-leading));
}
}
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr:has(.cbi-progressbar) .td:nth-child(2) {
grid-column: 1 / -1; grid-row: 2;
}
/* per-row dividers dropped, keyed on table SHAPE (no .th, no 3rd .td — same test as
* 30-tables.css's key/value rule, docs/css.md "Tables: the key/value discriminator") not on the
* three named cards. attendedsysupgrade's 11_upgrades.js (a stock header row) keeps its
* dividers under this test. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .td,
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .th,
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .tr { border-bottom: 0; }
/* own rule, chosen instead of :is(): .cbi-value carries no :has(), and a selector list is not
* forgiving below the browser floor */
:where(#view)[data-page="admin-status-overview"] .cbi-section .cbi-value { border-bottom: 0; }
/* key/value name is the QUIET half: --fs-dim at normal weight, matching what the System table's
* first column already resolved to. Data tables excluded by the same shape test above. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .td:first-child {
font-weight: var(--fs-weight-normal);
color: var(--fs-dim);
}
/* .active fill (theme/45-misc.css) makes three saturated bars at once here; "Up" said in
* colour+text on --fs-panel2 instead. Network -> Interfaces keeps the fill (tells boxes apart
* there), so this is scoped to Overview only. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .ifacebox .ifacebox-head.active {
background: var(--fs-panel2);
color: var(--fs-good);
font-weight: var(--fs-weight);
}
/* L.itemlist wraps every caption in <strong> at 700 weight — the boldest text on the card,
* inverted like the key/value rule above; the value keeps --fs-text with no rule of its own */
:where(#view)[data-page="admin-status-overview"] .cbi-section .ifacebox-body strong {
font-weight: var(--fs-weight-normal);
color: var(--fs-dim);
}
/* progressbar's attr(title) was --fs-type-xs/--fs-dim beside a now-demoted label; give Memory's
* bars something at full strength. Overview only — on Software the value sits beside its own
* field label. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:not(:has(.th)):not(:has(.td:nth-child(3))) .cbi-progressbar::after {
font-size: var(--fs-type);
color: var(--fs-text);
}
/* row height in DATA tables (DHCP leases, station list, attendedsysupgrade's 11_upgrades.js):
* theme/30-tables.css pads for a page that IS a table, here it's a card read at a glance — 8
* leases measured 740px against a 440px System card. Positive test is the complement of the
* key/value shape above (:has(.th) or a 3rd .td), not .tr.table-titles — a stock header .th
* emitted through initFromMarkup never carries that class. One step down the space ladder,
* only here. */
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:is(:has(.th), :has(.td:nth-child(3))) .td,
:where(#view)[data-page="admin-status-overview"] .cbi-section .table:is(:has(.th), :has(.td:nth-child(3))) .th {
padding-block: var(--fs-space-1-5);
}
}