mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-04 17:53:56 +08:00
440 lines
25 KiB
CSS
440 lines
25 KiB
CSS
@layer theme {
|
|
/* ---------- panels / cards (cbi) ---------- */
|
|
.cbi-section, .cbi-map > .cbi-section-node, .panel {
|
|
background: var(--fs-panel);
|
|
border: var(--fs-hairline);
|
|
border-radius: var(--fs-radius-lg);
|
|
}
|
|
.cbi-section { padding: var(--fs-card-pad); margin-bottom: var(--fs-card-gap); }
|
|
/* bare <legend> only; ink from the heading ramp. No line-height — base already sets 1. */
|
|
legend { font-size: var(--fs-type-lg); font-weight: var(--fs-weight-bold); color: var(--fs-text); }
|
|
|
|
/* overview (luci-mod-status) DOM:
|
|
* .cbi-section > .cbi-title>h3 + div > table.table (.tr/.td) */
|
|
|
|
/* section title + collapse control */
|
|
.cbi-section > .cbi-title { margin-bottom: var(--fs-space-3); }
|
|
.cbi-section > .cbi-title h3,
|
|
.cbi-section > h3:first-child,
|
|
.cbi-section > legend {
|
|
margin: 0; padding: 0; border: 0;
|
|
}
|
|
/* bare title (no wrapper) sat flush below — same 12px; excludes .cbi-title h3, already gapped */
|
|
.cbi-section > h3:first-child,
|
|
.cbi-section > legend { margin-bottom: var(--fs-space-3); }
|
|
|
|
/* a heading FOLLOWING a table had no air; separates more from what precedes than introduces */
|
|
.cbi-section .table + h3 { margin-top: var(--fs-space-5); }
|
|
.cbi-section h3 + .table { margin-top: var(--fs-space-3); }
|
|
|
|
/* [id]/.fs-dt = data table; no id = key/value, no frame/clip; nested tables too */
|
|
.cbi-section .table:not([id]):not(.fs-dt),
|
|
.cbi-section-node .table:not([id]):not(.fs-dt),
|
|
.cbi-modal .table, .table .table, .cbi-value .table {
|
|
border: 0; border-radius: 0; overflow: visible; background: none;
|
|
}
|
|
.cbi-section .table { margin: 0; width: 100%; }
|
|
/* moved from base with the generic field box (theme/60-inputs.css) */
|
|
.table.cbi-section-table input[type="password"],
|
|
.table.cbi-section-table input[type="text"],
|
|
.table.cbi-section-table textarea,
|
|
.table.cbi-section-table select,
|
|
.table.cbi-section-table .cbi-select {
|
|
width: 100%;
|
|
min-width: auto;
|
|
}
|
|
.table.cbi-section-table .td.cbi-section-table-cell select,
|
|
.table.cbi-section-table .td.cbi-section-table-cell .cbi-select { width: inherit; }
|
|
/* filter placeholder sliced mid-glyph on a long title (issue #12); ellipsis changes the
|
|
* failure instead. No !important — the inline 30px flag width still stands. */
|
|
.table.cbi-section-table .cbi-section-filter {
|
|
padding: var(--fs-space-1) var(--fs-space-1-5);
|
|
font-size: var(--fs-type-field-xs);
|
|
text-overflow: ellipsis;
|
|
}
|
|
/* width:100% cannot shrink below min-content; the inverse rule further down restores the
|
|
* floor on a data table — read that block before touching either. */
|
|
.cbi-section .table .td,
|
|
.cbi-section .table .th {
|
|
padding-block: var(--fs-space-2-5);
|
|
padding-inline: 0 var(--fs-space-4);
|
|
border: 0; border-bottom: var(--fs-hairline);
|
|
line-height: 1.45; vertical-align: middle;
|
|
text-align: start;
|
|
}
|
|
/* hugs the far edge, beating text-align:start above (form.TableSection's own action cells;
|
|
* hand-built ones stay left) */
|
|
.cbi-section .table .td.cbi-section-table-cell.cbi-section-actions { text-align: end; }
|
|
.cbi-section .table .tr:last-child .td { border-bottom: 0; }
|
|
/* case is the app's: uppercase broke LuCI's own casing (issue #39); weight/size/ink alone
|
|
* still read as a heading. */
|
|
.cbi-section .table .th {
|
|
color: var(--fs-eyebrow-color); font-weight: var(--fs-eyebrow-weight); font-size: var(--fs-type-xs);
|
|
letter-spacing: var(--fs-eyebrow-tracking);
|
|
}
|
|
|
|
/* --- DATA tables ([id] or JS-tagged .fs-dt) --- `:is()` at ONE specificity: split, a table
|
|
* known only by the tag (Status -> Routing) lost to key/value's default. The padding, frame, corner
|
|
* and placeholder rules key on `:is(table, div).fs-dt` (0,1,1) as v0.14.14's `table.fs-dt`, plus
|
|
* the bare div: `.table.fs-dt .td` (0,3,0) beat an app's `.table td:nth-child(1)` (0,2,1),
|
|
* filemanager padding-left 5 -> 12px (docs/css.md). The rules that were `.table.fs-dt` stay so. */
|
|
.cbi-section .table:is([id], .fs-dt) .td,
|
|
.cbi-section .table:is([id], .fs-dt) .th,
|
|
:is(table, div).fs-dt .td, :is(table, div).fs-dt .th { padding: var(--fs-space-2-5) var(--fs-space-3); }
|
|
.cbi-section .table:is([id], .fs-dt) .td { font-family: var(--fs-font-mono); font-size: var(--fs-type); color: var(--fs-text); }
|
|
|
|
/* the min-content FLOOR belongs here: break-word (unlike base's -anywhere) does not lower
|
|
* min-content, so an overflow stays visible to fit.overflows(). :not(.fs-stacked) is the
|
|
* fitter's own measuring state. */
|
|
.table.fs-dt:not(.fs-stacked) .td { overflow-wrap: break-word; }
|
|
/* the ONE column the fitter allows to shred rather than card the table (fs-select.js);
|
|
* (0,5,0) beats the floor above on specificity, not source order */
|
|
.table.fs-dt:not(.fs-stacked) .td.fs-td-break { overflow-wrap: anywhere; }
|
|
|
|
/* processes.js's inline style= cannot be outranked by a layer — !important is the sanctioned
|
|
* fight against it. A CARD IS NOT EXEMPT: an unlayered app rule breaks a token that would fit. */
|
|
.table.fs-dt .td,
|
|
.table.fs-dt .td :not(button):not(.btn):not(input):not(select):not(textarea) {
|
|
word-break: normal !important;
|
|
}
|
|
|
|
/* Whether a table cards, breaks one column or scrolls is MEASURED (fs-select.js), at any width. */
|
|
|
|
/* sticks each .th, not the row — a row's fill does not travel with a sticky cell here. 0 in
|
|
* the sidebar (.fs-main scrolls itself), --fs-bar-live where the document does. */
|
|
:where(:root[data-layout="sidebar"]:not([data-narrow]) #view, #modal_overlay)
|
|
.table.fs-dt:not(.fs-stacked) .tr.table-titles .th {
|
|
position: sticky; top: 0; z-index: 1;
|
|
background: var(--fs-panel2);
|
|
}
|
|
:where(:root[data-layout="top"] #view, :root[data-narrow] #view)
|
|
.table.fs-dt:not(.fs-stacked) .tr.table-titles .th {
|
|
position: sticky; top: var(--fs-bar-live, var(--fs-bar-h)); z-index: 1;
|
|
background: var(--fs-panel2);
|
|
}
|
|
|
|
/* fitTables() forces a layout with marks off, and anchoring can re-pick a node inside it
|
|
* (18 jumps/1500 frames measured). */
|
|
.table.fs-dt { overflow-anchor: none; }
|
|
|
|
.cbi-section .table:is([id], .fs-dt) .tr:not(.table-titles):not(.placeholder):hover .td { background: var(--fs-panel2); }
|
|
.cbi-section .table:is([id], .fs-dt) .tr.table-titles .th { border-bottom-color: var(--fs-border); }
|
|
/* contour (border-collapse:separate, or the radius is silently dropped); #view too —
|
|
* Startup renders .table.fs-dt with no section */
|
|
.cbi-section .table[id], #view .table[id],
|
|
.cbi-section .table.fs-dt, .cbi-section table.fs-dt, :is(table, div).fs-dt {
|
|
border-collapse: separate; border-spacing: 0;
|
|
border: var(--fs-hairline); border-radius: var(--fs-radius-lg);
|
|
/* EXPLICIT: the generic .table rule below still says overflow:hidden, clipped otherwise */
|
|
overflow: visible;
|
|
}
|
|
/* frame is overflow:visible, so nothing clips the corner cells — round the four OUTER ones.
|
|
* `:is(.tr.table-titles, .thead)` covers both header shapes; without .thead a real <thead>
|
|
* kept square top corners. */
|
|
.cbi-section .table[id] :is(.tr.table-titles, .thead) .th:first-child,
|
|
#view .table[id] :is(.tr.table-titles, .thead) .th:first-child,
|
|
:is(table, div).fs-dt :is(.tr.table-titles, .thead) .th:first-child { border-start-start-radius: var(--fs-radius-lg); }
|
|
.cbi-section .table[id] :is(.tr.table-titles, .thead) .th:last-child,
|
|
#view .table[id] :is(.tr.table-titles, .thead) .th:last-child,
|
|
:is(table, div).fs-dt :is(.tr.table-titles, .thead) .th:last-child { border-start-end-radius: var(--fs-radius-lg); }
|
|
.cbi-section .table[id] .tr:last-child .td:first-child,
|
|
#view .table[id] .tr:last-child .td:first-child,
|
|
:is(table, div).fs-dt .tr:last-child .td:first-child { border-end-start-radius: var(--fs-radius-lg); }
|
|
.cbi-section .table[id] .tr:last-child .td:last-child,
|
|
#view .table[id] .tr:last-child .td:last-child,
|
|
:is(table, div).fs-dt .tr:last-child .td:last-child { border-end-end-radius: var(--fs-radius-lg); }
|
|
/* empty table: L.ui.Table adds .tr.placeholder; spanning it is base's job, framed flavour left here */
|
|
.cbi-section .table[id] .tr.placeholder .td,
|
|
#view .table[id] .tr.placeholder .td,
|
|
:is(table, div).fs-dt .tr.placeholder .td {
|
|
padding-block: var(--fs-space-2); /* this layer's own cell padding made the plate 4px tall */
|
|
text-align: start;
|
|
color: var(--fs-dim); font-family: var(--fs-font-sans);
|
|
border-radius: 0 0 var(--fs-radius-lg) var(--fs-radius-lg);
|
|
}
|
|
|
|
/* --- KEY/VALUE: "no id" is not enough — GridSection also renders no-id .cbi-section-table,
|
|
* excluded along with .fs-dt and the legend's inline table-layout. Shape, not a class, says
|
|
* data table: any .th or a 3rd .td rules a table out — a real key/value row is exactly 2 .td
|
|
* and no .th. docs/css.md, "Tables: the key/value discriminator". */
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .td:first-child {
|
|
color: var(--fs-dim); font-weight: var(--fs-weight); width: 40%; white-space: nowrap;
|
|
}
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .td:last-child:not(:first-child) {
|
|
font-family: var(--fs-font-mono); color: var(--fs-text); text-align: end;
|
|
}
|
|
/* below ~560px the 40%/nowrap column starves the value — stacked below (560, not the config
|
|
* table's 960px). :first-child:first-child doubles the pseudo-class ON PURPOSE, to outrank. */
|
|
@container fs-content (max-width: 560px) {
|
|
/* the row's own .td/.th padding above spent a FULL padding per half; moved to the ROW */
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .tr {
|
|
display: block;
|
|
padding: var(--fs-space-1) 0 var(--fs-space-1-5);
|
|
border: 0;
|
|
border-bottom: var(--fs-hairline);
|
|
}
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .tr:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .td:first-child:first-child {
|
|
display: block; width: auto; white-space: normal;
|
|
padding: 0; border: 0;
|
|
font-size: var(--fs-type-xs); font-weight: var(--fs-eyebrow-weight);
|
|
letter-spacing: var(--fs-eyebrow-tracking); color: var(--fs-eyebrow-color);
|
|
text-transform: uppercase;
|
|
}
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt):not(:has(.th)):not(:has(.td:nth-child(3))):not([style*="table-layout"]) .td:last-child:last-child:not(:first-child) {
|
|
display: block; width: auto; text-align: start;
|
|
padding: 0; border: 0;
|
|
}
|
|
}
|
|
|
|
/* --- the realtime graph legend: the one no-id .table with inline table-layout:fixed, six
|
|
* equal columns overflowing on a phone — folded into label/value pairs. */
|
|
@media (max-width: 767px) {
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt)[style*="table-layout"] .tr {
|
|
display: flex; flex-wrap: wrap;
|
|
}
|
|
.cbi-section .table:not([id]):not(.cbi-section-table):not(.fs-dt)[style*="table-layout"] .td {
|
|
flex: 1 1 50%; min-width: 0; width: auto;
|
|
}
|
|
}
|
|
|
|
/* remove double card when a section nests a table-only include */
|
|
.cbi-section .cbi-section { border: 0; box-shadow: none; padding: 0; margin: 0; background: transparent; }
|
|
|
|
/* generic links/labels inside cards; the MAC badge keeps base's flat 4px, square against the card corners (issue #5.4) */
|
|
.cbi-section .ifacebox, .cbi-section .zonebadge,
|
|
.network-status-table .ifacebox-body .ifacebadge { border-radius: var(--fs-radius); }
|
|
.zonebadge input { width: 6em; } /* moved from base with the generic field box */
|
|
/* head rounds WITH the box (issue #7); max() since Rounding goes to 0, an invalid subtraction */
|
|
.cbi-section .ifacebox > .ifacebox-head {
|
|
--fs-head-radius: max(0px, calc(var(--fs-radius) - 1px));
|
|
border-radius: var(--fs-head-radius) var(--fs-head-radius) 0 0;
|
|
}
|
|
|
|
/* inactive tab panes: base's height:0/overflow:hidden still drew empty padding/border bars
|
|
* (issue #10). visibility not display:none — stock JS reads a graph's width once in a rAF. */
|
|
[data-tab-title]:not([data-tab-active="true"]) {
|
|
visibility: hidden;
|
|
position: relative;
|
|
height: 0;
|
|
overflow: hidden;
|
|
padding: 0;
|
|
margin: 0;
|
|
border: 0;
|
|
}
|
|
/* 6%, not 12%: bold --fs-danger on a tint OF --fs-danger eats its own contrast (12% measured
|
|
* 4.4:1); at 6% every palette clears 4.5 (4.87/4.95/5.28/6.09) */
|
|
.cbi-section-error { border-color: var(--fs-danger); background: color-mix(in srgb, var(--fs-danger) 6%, var(--fs-panel)); color: var(--fs-text); }
|
|
.cbi-section-error ul li { color: var(--fs-danger); }
|
|
|
|
/* denser data tables (dhcp static leases, section-tables) */
|
|
.cbi-section-table .th, .cbi-section-table .td,
|
|
/* the GridSection name CELL on 24.10, a ::before from data-title where form.js has no real
|
|
* cell — named here too or the column lines up with nothing */
|
|
.table.cbi-section-table .th, .table.cbi-section-table .td,
|
|
.table.cbi-section-table .tr::before { padding-block: var(--fs-space-2); padding-inline: 0 var(--fs-space-3); }
|
|
/* zero inline-start padding is right only INSIDE a card; a self-framed table glued its header text
|
|
* to its own corner without this (issue #7) */
|
|
.table.cbi-section-table:not(.cbi-section *):not(.cbi-section-node *) .th,
|
|
.table.cbi-section-table:not(.cbi-section *):not(.cbi-section-node *) .td { padding: var(--fs-space-2-5) var(--fs-space-3); }
|
|
.cbi-section-table .tr.cbi-section-table-titles .th,
|
|
.cbi-section-table .tr.table-titles .th { font-size: var(--fs-type-xs); letter-spacing: var(--fs-eyebrow-tracking); line-height: 1.25; }
|
|
|
|
/* standalone data tables -> rounded card; inside a CBI section the section already IS the
|
|
* card, so the frame is stripped there (above) */
|
|
.table {
|
|
border: var(--fs-hairline);
|
|
border-radius: var(--fs-radius-lg);
|
|
overflow: hidden;
|
|
background: var(--fs-panel);
|
|
}
|
|
/* separators on the CELLS: the frame needs border-collapse:separate, a row's own border
|
|
* paints nothing. :not(:last-child) keeps a trailing one off the rounded corner. */
|
|
.table .tr.table-titles > .th,
|
|
.table .tr.cbi-section-table-titles > .th,
|
|
.table .tr.cbi-section-table-titles::before,
|
|
.table .tr:not(.table-titles):not(.cbi-section-table-titles):not(:last-child) > .td,
|
|
.table .tr.cbi-section-table-row:not(:last-child)::before { border-bottom: var(--fs-hairline); }
|
|
/* per-CELL border, so a foreign `position: relative; top:` (dashboard) drags one cell's
|
|
* border out of line — `top` AND `bottom` cancels either without touching `relative` itself */
|
|
:is(#view, #modal_overlay) .fs-dt .td { top: auto; bottom: auto; }
|
|
/* descendant combinator so a real <table>'s inserted <tbody> is covered too */
|
|
.table .tr.table-titles, .table .tr.cbi-section-table-titles {
|
|
background: transparent; /* drop the base panel2 header bar — match the dashboard's faint-label + underline look */
|
|
}
|
|
.table .tr.table-titles .th, .table .tr.cbi-section-table-titles .th,
|
|
.table .tr.cbi-section-table-titles::before {
|
|
color: var(--fs-eyebrow-color); font-size: var(--fs-type-xs); font-weight: var(--fs-eyebrow-weight);
|
|
letter-spacing: var(--fs-eyebrow-tracking);
|
|
background: transparent;
|
|
}
|
|
/* row NAME reads the same on both releases (real cell / ::before) — a LABEL, sans face */
|
|
.table.cbi-section-table .td.cbi-section-table-titles,
|
|
.table.cbi-section-table .tr.cbi-section-table-row::before {
|
|
font-family: var(--fs-font-sans); font-weight: var(--fs-weight-bold); color: var(--fs-text);
|
|
}
|
|
.table .tr:not(.table-titles):not(.cbi-section-table-titles) {
|
|
transition: background var(--fs-dur);
|
|
}
|
|
/* row hover ONLY on real data/config tables (an untagged plain .table lit its whole block,
|
|
* issue #5.3; a placeholder row lit up too, issue #5.2/#15) */
|
|
.table.cbi-section-table .tr.cbi-section-table-row:not(.placeholder):hover { background: var(--fs-panel2); }
|
|
|
|
/* .fs-dt: fs-select.js on a header-row #view .table that is NOT .cbi-section-table —
|
|
* replacing a live :has() selector re-evaluated on every mutation. */
|
|
|
|
/* One card-stack copy, no @container: fs-select.js already measures OVERFLOW and sets
|
|
* .fs-stacked. The config table keeps its own @container — its widgets bake in a width. */
|
|
/* a poll REPLACES a data table with a fresh, unmarked copy that briefly lays out full-width,
|
|
* throwing the reader 612px out and back — waits OUTSIDE the layout until fs-select.js
|
|
* answers, guarded on :root[data-fs-fit]. */
|
|
:root[data-fs-fit] #view .table.fs-dt:not(.fs-fitted),
|
|
:root[data-fs-fit] #modal_overlay .table.fs-dt:not(.fs-fitted) { display: none; }
|
|
|
|
.table.fs-stacked { display: block; width: 100%; }
|
|
/* .fs-drop-xs is the same drop one rung earlier, on an overflowed table before carding */
|
|
.table.fs-stacked .td.hide-sm, .table.fs-stacked .th.hide-sm,
|
|
.table.fs-stacked .td.hide-xs, .table.fs-stacked .th.hide-xs,
|
|
.table.fs-drop-xs .td.hide-sm, .table.fs-drop-xs .th.hide-sm,
|
|
.table.fs-drop-xs .td.hide-xs, .table.fs-drop-xs .th.hide-xs,
|
|
.table.fs-stacked .tr.table-titles,
|
|
.table.fs-stacked .thead,
|
|
.table.fs-stacked .tr.cbi-section-table-titles,
|
|
.table.fs-stacked .tr.cbi-section-table-descr { display: none; }
|
|
/* stretch (default), not flex-start: a 2-line cell's border-bottom broke out of line with a
|
|
* 1-line neighbour's (52px) */
|
|
/* a <tfoot> with no .tr IS the row. Two rules, chosen instead of :is(): a browser without :has() drops a whole list,
|
|
* losing display:flex on every card. Keep both in step by hand. */
|
|
.table.fs-stacked .tr { display: flex; flex-wrap: wrap; }
|
|
.table.fs-stacked tfoot:not(:has(> .tr)),
|
|
.table.fs-stacked .tfoot:not(:has(> .tr)) { display: flex; flex-wrap: wrap; }
|
|
.table.fs-stacked .td {
|
|
display: block; min-width: 0; width: auto;
|
|
padding-block: var(--fs-space-1-5); padding-inline: 0 var(--fs-space-2-5); border: 0; text-align: start;
|
|
overflow-wrap: anywhere; white-space: normal;
|
|
}
|
|
/* the column label, lifted above its value. A copy sits in theme/65-dropdown.css: the config
|
|
* table needs it under its own @container guard, which cannot share a block with a class. Keep
|
|
* the two identical by hand. */
|
|
.table.fs-stacked .td[data-title]::before {
|
|
content: attr(data-title); display: block; margin-bottom: var(--fs-space-1);
|
|
font-size: var(--fs-type-xs); font-weight: var(--fs-eyebrow-weight); letter-spacing: var(--fs-eyebrow-tracking);
|
|
color: var(--fs-eyebrow-color);
|
|
}
|
|
/* a footer cell needs [data-title] in its OWN selector to win the caption rule's (0,4,0) */
|
|
.table.fs-stacked .td.cbi-section-actions::before,
|
|
.table.fs-stacked .td:not([data-title])::before,
|
|
.table.fs-stacked .td[data-title=""]::before,
|
|
.table.fs-stacked tfoot .td[data-title]::before,
|
|
.table.fs-stacked .tfoot .td[data-title]::before { content: none; }
|
|
/* row buttons are not a label/value pair — full width below the pairs. Copy in theme/65-dropdown.css. */
|
|
.table.fs-stacked .td.cbi-section-actions {
|
|
display: flex; flex: 1 1 100%; justify-content: flex-start;
|
|
flex-wrap: wrap; gap: var(--fs-space-2); padding-top: var(--fs-space-2);
|
|
}
|
|
/* LuCI groups buttons in ONE inner <div>, so this flex-wrap never fires and .fs-main clipped
|
|
* the tail button. Copy in theme/65-dropdown.css. */
|
|
.table.fs-stacked .td.cbi-section-actions > :not(.cbi-dropdown) {
|
|
flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: var(--fs-space-2);
|
|
}
|
|
/* width: 0 — the label's nowrap min-content (288px for a Russian item at 360px) sizes the table-row-group
|
|
* past the card (td right 442 vs 315); basis `content` still sizes the item to its label (it was 266px, a whole card, with `1 1 auto`). Copy in theme/65-dropdown.css. */
|
|
.table.fs-stacked .td.cbi-section-actions > .cbi-dropdown {
|
|
flex: 0 1 content; width: 0; min-width: 0;
|
|
}
|
|
/* nothing in a half-width cell may force the row wider than the viewport — .fs-main clips,
|
|
* it does not scroll to an over-wide child */
|
|
.table.fs-stacked .td > *,
|
|
.table.fs-stacked .td .ifacebadge { min-width: 0; max-width: 100%; }
|
|
|
|
/* a <tfoot> is still a table-footer-group in a display:block table, drawing beside the cards
|
|
* instead of under them; `:has(> .tr)` tells the two shapes upstream writes it in apart. */
|
|
.table.fs-stacked tfoot:has(> .tr),
|
|
.table.fs-stacked .tfoot:has(> .tr) { display: block; }
|
|
.table.fs-stacked tfoot .td:empty,
|
|
.table.fs-stacked .tfoot .td:empty { display: none; }
|
|
|
|
/* --- DATA table flavour (L.ui.Table, tagged .fs-dt) --- the frame stays overflow:visible so a
|
|
* ComboButton's open list can leave the card; zebra and hover paint on the .tr, so the two outer
|
|
* rows carry the frame's inner radius (its radius less the 1px border) instead of a clip. */
|
|
.table.fs-dt.fs-stacked :is(.tr:first-child, :is(.tr.table-titles, .thead) + .tr) {
|
|
border-start-start-radius: calc(var(--fs-radius-lg) - 1px);
|
|
border-start-end-radius: calc(var(--fs-radius-lg) - 1px);
|
|
}
|
|
.table.fs-dt.fs-stacked .tr:last-child {
|
|
border-end-start-radius: calc(var(--fs-radius-lg) - 1px);
|
|
border-end-end-radius: calc(var(--fs-radius-lg) - 1px);
|
|
}
|
|
/* :not(.placeholder) — the empty-state plate is out of flow; its row only reserves height,
|
|
* and padding subtracted from that left text hanging below the frame (Wireless) */
|
|
.table.fs-dt.fs-stacked .tr:not(.placeholder) { padding: var(--fs-space-2-5) var(--fs-space-4) var(--fs-space-3); }
|
|
/* half the row makes the card; col-N cells are excluded by the second :not(), not outranked. Third :not() loses on purpose to the meter below. */
|
|
.table.fs-stacked .td:not(.cbi-section-actions):not([class*="col-"]):not(:has(.cbi-progressbar)) {
|
|
flex: 1 1 50% !important;
|
|
}
|
|
/* a first cell that is one token (fs-select.js): "/dev/mtdblock6" is 110px, the half-split left
|
|
* 94-106px at 360-390 and `anywhere` cut it mid-word. fit-content of one token is the token,
|
|
* capped at the row, so a 64-char hash still breaks inside the card; a phrase keeps its half. */
|
|
.table.fs-stacked .tr > .td.fs-td-token:first-child:not(.cbi-section-actions):not([class*="col-"]) { min-width: fit-content; }
|
|
/* a meter takes the whole line — its own title prints over the cell's caption otherwise */
|
|
.table.fs-stacked .td:has(.cbi-progressbar) { flex: 1 1 100% !important; }
|
|
|
|
/* column weights (1-10): a share of a CARDED row, unguarded — `flex` only applies once a
|
|
* `.td` becomes a flex item, so one copy serves every tier and is inert elsewhere. */
|
|
.table .td.col-1, .table .th.col-1 { flex: 1 1 30px; }
|
|
.table .td.col-2, .table .th.col-2 { flex: 2 2 60px; }
|
|
.table .td.col-3, .table .th.col-3 { flex: 3 3 90px; }
|
|
.table .td.col-4, .table .th.col-4 { flex: 4 4 120px; }
|
|
.table .td.col-5, .table .th.col-5 { flex: 5 5 150px; }
|
|
.table .td.col-6, .table .th.col-6 { flex: 6 6 180px; }
|
|
.table .td.col-7, .table .th.col-7 { flex: 7 7 210px; }
|
|
.table .td.col-8, .table .th.col-8 { flex: 8 8 240px; }
|
|
.table .td.col-9, .table .th.col-9 { flex: 9 9 270px; }
|
|
.table .td.col-10, .table .th.col-10 { flex: 10 10 300px; }
|
|
|
|
/* general table cell padding — compact but readable (Startup/Processes/etc) */
|
|
.table .td { padding-top: var(--fs-space-2); padding-bottom: var(--fs-space-2); }
|
|
|
|
/* last resort: no header row, never carded (no data-title to print). .fs-xscroll: same,
|
|
* MEASURED to overflow; refused to a table holding a widget (clips a popup inside). */
|
|
#view table:not(.table):not(.cbi-section-table):not(.fs-dt),
|
|
#modal_overlay table:not(.table):not(.cbi-section-table):not(.fs-dt),
|
|
#view .table.fs-xscroll, #modal_overlay .table.fs-xscroll {
|
|
display: block;
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
overscroll-behavior-x: contain; /* the gesture stops here instead of dragging the page */
|
|
scrollbar-width: thin; /* the scrollbar IS the affordance; a shadow trick shows through zebra rows */
|
|
}
|
|
|
|
/* a header-less table holding a control stacks instead of scrolling — a scroll container
|
|
* would clip the popup inside; nothing here creates one, so a dropdown still escapes. */
|
|
.table.fs-rowstack,
|
|
.table.fs-rowstack .tr,
|
|
.table.fs-rowstack .td,
|
|
.table.fs-rowstack .th { display: block; width: auto; }
|
|
/* the cells were table-cells a moment ago (side padding); as blocks they need the vertical
|
|
* rhythm instead, or the controls sit flush against each other */
|
|
.table.fs-rowstack .td { padding-inline: 0; padding-block: var(--fs-space-1); }
|
|
|
|
/* pins the row's NAME while scrolling — its own background (a row's fill does not travel
|
|
* with a sticky cell) and a hairline so it reads as an edge, not text over text. */
|
|
#view .table.fs-xscroll .tr > :first-child,
|
|
#modal_overlay .table.fs-xscroll .tr > :first-child,
|
|
#view .table.fs-xscroll tr > :first-child,
|
|
#modal_overlay .table.fs-xscroll tr > :first-child {
|
|
position: sticky; inset-inline-start: 0; z-index: 2;
|
|
background: var(--fs-panel);
|
|
border-inline-end: var(--fs-hairline);
|
|
}
|
|
/* a keyboard user tabs to the scroll box (fs-select.js gives it tabindex+name) — outline, not
|
|
* box-shadow, so forced-colors keeps it */
|
|
#view .table.fs-xscroll:focus-visible,
|
|
#modal_overlay .table.fs-xscroll:focus-visible {
|
|
outline: 2px solid var(--fs-accent);
|
|
outline-offset: 2px;
|
|
}
|
|
}
|