mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-11-09 13:38:16 +08:00
update 2026-10-17 16:12:15
This commit is contained in:
1 parent
fbf8d8b632
commit
63dbfd1d1b
167 files changed
+407
-38039
No files matched your search
@@ -1,384 +0,0 @@
|
||||
@layer theme {
|
||||
/* generic .dropdown-menu popup: nothing in luci-base emits it — coverage vocabulary, since
|
||||
* stock luci-theme-bootstrap styles it and a third-party app may emit it */
|
||||
.dropdown-menu { background-color: var(--fs-panel); border-color: var(--fs-border); }
|
||||
.dropdown-menu a { color: var(--fs-text); text-shadow: none; }
|
||||
.dropdown-menu a:hover,
|
||||
.dropdown-menu a:focus-visible { background: var(--fs-panel2); color: var(--fs-accent); }
|
||||
|
||||
/* the outer div IS the button, class = ui.js's SELECTED mode; unguarded this erased that
|
||||
* variant's fill while its ink survived — measured page-on-page at 1.06-1.13:1. */
|
||||
.cbi-page-actions .cbi-dropdown:not(.btn):not(.cbi-button) {
|
||||
border: 0; border-radius: var(--fs-radius); background: transparent;
|
||||
}
|
||||
/* Apply is the one variant with no fill of its own (theme/55-buttons.css gives the rest theirs); painted by specificity, not source order */
|
||||
.cbi-page-actions .cbi-dropdown.cbi-button-apply,
|
||||
.cbi-page-actions .cbi-dropdown > .cbi-button {
|
||||
background: var(--fs-accent);
|
||||
border: 0;
|
||||
color: var(--fs-on-accent);
|
||||
}
|
||||
.cbi-page-actions .cbi-dropdown.cbi-button-apply:hover,
|
||||
.cbi-page-actions .cbi-dropdown > .cbi-button:hover { filter: var(--fs-hover-lift); }
|
||||
|
||||
/* ---- cbi-dropdown widget: the JS select used across all forms ---- */
|
||||
/* machinery absorbed from base, driven by ui.js attributes; the state machine below is
|
||||
* UNFLAGGED, stating openly what upstream's !important flags resolved — docs/css.md
|
||||
* "Dropdown: the display state machine, unflagged" has the specificity table. */
|
||||
.cbi-dropdown {
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
.cbi-dropdown img {
|
||||
/* constrain a vector icon of any intrinsic size to the text line */
|
||||
max-height: 1.6em;
|
||||
width: auto;
|
||||
}
|
||||
.cbi-dropdown > ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
/* clears the button's own side inset so the label starts on its text, unflagged (0,3,1) over the rule above's (0,1,1) */
|
||||
.cbi-dropdown.btn > ul:not(.dropdown),
|
||||
.cbi-dropdown.cbi-button > ul:not(.dropdown) {
|
||||
margin-block: 0;
|
||||
margin-inline: var(--fs-space-3) 0;
|
||||
}
|
||||
.cbi-dropdown.btn.spinning > ul:not(.dropdown),
|
||||
.cbi-dropdown.cbi-button.spinning > ul:not(.dropdown) {
|
||||
margin: 0;
|
||||
}
|
||||
.cbi-dropdown > ul.preview {
|
||||
display: none;
|
||||
}
|
||||
/* no `display` here: `.open` states its own below, `.more` is none until the showers flip it — one declaration per state or the audit's same-selector shadow check fires */
|
||||
.cbi-dropdown > .open,
|
||||
.cbi-dropdown > .more {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
.cbi-dropdown.btn > .open,
|
||||
.cbi-dropdown.cbi-button > .open {
|
||||
margin-inline-start: .5em;
|
||||
}
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[placeholder] {
|
||||
display: none;
|
||||
}
|
||||
.cbi-dropdown > ul > li {
|
||||
display: none;
|
||||
font-size: var(--fs-type-field);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* ellipsis did nothing until min-width: 0 — a flex item's automatic minimum floors it at its
|
||||
* own content width — docs/css.md "Dropdown: ellipsis needs `min-width: 0` first". */
|
||||
min-width: 0;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
color: inherit;
|
||||
}
|
||||
.cbi-dropdown > ul > li .hide-open { display: initial; }
|
||||
.cbi-dropdown > ul > li .hide-close { display: none; }
|
||||
/* the chip divider on a BUTTON dropdown's preview; the form dropdown zeroes it below */
|
||||
.cbi-dropdown > ul > li[display]:not([display="0"]) {
|
||||
border-inline-start: var(--fs-hairline);
|
||||
}
|
||||
.cbi-dropdown[empty] > ul {
|
||||
max-width: 1px;
|
||||
}
|
||||
.cbi-dropdown > ul > li > form {
|
||||
display: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.cbi-dropdown > ul > li img {
|
||||
vertical-align: middle;
|
||||
margin-inline-end: .25em;
|
||||
}
|
||||
.cbi-dropdown > ul > li > form > input[type="checkbox"] {
|
||||
margin-block: 0;
|
||||
margin-inline: 0 var(--fs-space-1);
|
||||
}
|
||||
|
||||
/* ---- the display state machine, on specificity (docs/css.md has the per-selector table) ---- */
|
||||
/* a closed optional dropdown with an empty value renders EMPTY unflagged too: the placeholder
|
||||
* hider out-specifies the generic shower. :not([open]) keeps this out of the open list. */
|
||||
.cbi-dropdown > ul > li[display],
|
||||
.cbi-dropdown[open] > ul.preview,
|
||||
.cbi-dropdown[open] > ul.dropdown > li,
|
||||
.cbi-dropdown[multiple] > ul > li > label,
|
||||
.cbi-dropdown[multiple][open] > ul.dropdown > li,
|
||||
.cbi-dropdown[multiple][more] > .more,
|
||||
.cbi-dropdown[multiple][empty] > .more,
|
||||
.cbi-dropdown:not([open]):not(.btn):not(.cbi-button) > ul > li[placeholder][display] {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
}
|
||||
.cbi-dropdown[empty] > ul > li,
|
||||
.cbi-dropdown[optional][open] > ul.dropdown > li[placeholder] {
|
||||
display: block;
|
||||
}
|
||||
.cbi-dropdown[multiple][open] > ul.dropdown > li > form {
|
||||
display: flex;
|
||||
}
|
||||
.cbi-dropdown[open] > ul.dropdown > li .hide-open { display: none; }
|
||||
/* min-width: a CHILD with an inline width overflows, wrapping the row cannot reach it —
|
||||
* form.js's RichListValue assumes an unbounded list (issue #15). */
|
||||
.cbi-dropdown[open] > ul.dropdown > li .hide-close { display: initial; min-width: 0 !important; }
|
||||
.cbi-dropdown[open] > ul.dropdown > li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.cbi-dropdown[open] > ul.dropdown > li[unselectable] {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button) {
|
||||
background: var(--fs-panel2);
|
||||
border: var(--fs-hairline);
|
||||
border-radius: var(--fs-radius);
|
||||
color: var(--fs-text);
|
||||
min-height: var(--fs-ctl-h);
|
||||
/* fixed height: without it a closed dropdown grows to its tallest child (Save & Apply's
|
||||
* 40px button, measured 38 -> 42px on the gallery); --fs-ctl-h, since the min-height floor always won */
|
||||
height: var(--fs-ctl-h);
|
||||
font-size: var(--fs-type);
|
||||
transition: var(--fs-field-transition);
|
||||
/* absorbed from base/30-forms.css; NOT the dynlist's numbers (240/440) — one value vs a column of them */
|
||||
min-width: 210px;
|
||||
max-width: 400px;
|
||||
width: auto;
|
||||
}
|
||||
.cbi-dropdown.btn, .cbi-dropdown.cbi-button { padding: 0; }
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button):hover { border-color: var(--fs-dim); }
|
||||
.cbi-dropdown:focus, .cbi-dropdown[open] {
|
||||
border-color: var(--fs-accent);
|
||||
box-shadow: var(--fs-focus-ring);
|
||||
outline: none;
|
||||
}
|
||||
/* `.cbi-dropdown > ul.dropdown > li` is the same row on a BUTTON dropdown, excluded by
|
||||
* :not(.btn):not(.cbi-button) below — one menu, one row, but two selectors. */
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li,
|
||||
.cbi-dropdown > ul.dropdown > li {
|
||||
padding: var(--fs-space-2) var(--fs-space-3); color: var(--fs-text);
|
||||
font-family: var(--fs-font-sans);
|
||||
/* a grid step, not calc(--fs-type * --fs-leading) (19.5px) — a half-pixel box edge is what the scale exists to stop */
|
||||
min-height: var(--fs-space-5);
|
||||
}
|
||||
/* a MULTI-select wraps items in <form><label>…</label>, which theme/60-inputs.css's `.cbi-value-field *` would hand mono — inherit keeps it the row's sans, a LABEL not a value */
|
||||
.cbi-dropdown > ul > li > form,
|
||||
.cbi-dropdown > ul > li label { font-family: inherit; }
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[display]:not([display="0"]) { border-inline-start: 0; }
|
||||
/* same ink base/30-forms.css gives `placeholder`; repeats the row rule's two selectors rather
|
||||
* than a shorter form, which LOSES to the row's own color and rendered in the value's ink. */
|
||||
.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[placeholder],
|
||||
.cbi-dropdown > ul.dropdown > li[placeholder] { color: var(--fs-placeholder); }
|
||||
/* the multiselect's inline filter/create inputs, moved from base with the generic input box (theme/60-inputs.css) */
|
||||
.cbi-dropdown > ul > li input[type="text"] { height: var(--fs-space-5); }
|
||||
.cbi-dropdown[open] > ul.dropdown > li > input.create-item-input:first-child:last-child { width: 100%; }
|
||||
|
||||
/* `.open` is the only hit target that opens the list (a click elsewhere on a button dropdown
|
||||
* RUNS the button). `font-size: 0` collapsed the stock line-height to 28x0, putting the
|
||||
* chevron outside its own box — a real box fixes that. */
|
||||
.cbi-dropdown > .open {
|
||||
display: flex;
|
||||
font-size: 0; line-height: 0; border: 0;
|
||||
position: relative; padding: 0;
|
||||
align-self: stretch;
|
||||
min-width: var(--fs-btn-size); min-height: var(--fs-ctl-h-sm);
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* `.more` was one base rule with `.open`, split when the theme took `.open` for the chevron */
|
||||
.cbi-dropdown > .more { line-height: 2em; padding: 0 .3em; color: var(--fs-dim); display: none; }
|
||||
.cbi-dropdown > .open > * { display: none; }
|
||||
.cbi-dropdown > .open::after {
|
||||
content: ""; position: absolute; inset: 0; margin: auto;
|
||||
width: 7px; height: 7px;
|
||||
border-right: 2px solid var(--fs-dim); border-bottom: 2px solid var(--fs-dim);
|
||||
transform: translateY(-2px) rotate(45deg); /* optical centre of a "v" */
|
||||
}
|
||||
/* --fs-dim reads as a smudge on the accent-filled Save & Apply; currentColor uses the variant's own ink */
|
||||
.cbi-dropdown.btn > .open::after,
|
||||
.cbi-dropdown.cbi-button > .open::after { border-color: currentColor; }
|
||||
|
||||
/* open list -> design popover */
|
||||
.cbi-dropdown[open] { z-index: var(--fs-z-dropdown); }
|
||||
.cbi-dropdown[open] > ul.dropdown {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: var(--fs-z-dropdown);
|
||||
transition: max-height var(--fs-dur-move);
|
||||
overflow-y: auto;
|
||||
background: var(--fs-panel);
|
||||
border: var(--fs-hairline);
|
||||
border-radius: var(--fs-radius-lg);
|
||||
box-shadow: var(--fs-shadow-pop);
|
||||
padding: var(--fs-space-1-5);
|
||||
margin-top: var(--fs-space-1-5);
|
||||
/* anchored to its widget: ui.js's touch branch assumes the viewport as containing block; !important beats the inline style */
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: auto !important;
|
||||
min-width: 100%;
|
||||
/* desktop only: min(92vw,420px) ignores trigger's left offset, grew past viewport under 767px (113 findings) */
|
||||
width: max-content;
|
||||
max-width: min(92vw, 420px);
|
||||
}
|
||||
.cbi-dropdown[open] > ul.dropdown > li {
|
||||
border-radius: var(--fs-radius-sm);
|
||||
border-inline-start: 0;
|
||||
position: relative;
|
||||
/* an OPEN row wraps; only the closed preview ellipsizes — the menu is capped at
|
||||
* min(92vw, 420px), so a verbose option would otherwise decide the width of every menu */
|
||||
white-space: normal; overflow-wrap: anywhere;
|
||||
/* no border-bottom: it curls at the corners of a rounded box; the ::after inset strip below is the divider instead */
|
||||
border-bottom: 0;
|
||||
}
|
||||
/* The row's own wrap above never reaches a `.zonebadge` (base/95-luci.css): `white-space: nowrap`
|
||||
* there, right for a one-line pill in a table cell, wrong here where a multi-homed interface
|
||||
* (RichListValue) nests two `.ifacebadge` chips in one badge with no space between them — an
|
||||
* unbreakable 254-258px at 320px (`R` past the 320px viewport), overflowing
|
||||
* regardless of the popup's own width. Scoped to the open list only: every other `.zonebadge`
|
||||
* keeps `nowrap`. */
|
||||
.cbi-dropdown[open] > ul.dropdown > li .zonebadge {
|
||||
white-space: normal; overflow-wrap: anywhere;
|
||||
}
|
||||
.cbi-dropdown[open] > ul.dropdown > li:not(:last-child)::after {
|
||||
content: ""; position: absolute; inset-inline: 11px; bottom: 0;
|
||||
height: 1px; background: var(--fs-border);
|
||||
}
|
||||
.cbi-dropdown[open] > ul.dropdown > li:hover,
|
||||
.cbi-dropdown[open] > ul.dropdown > li.focus { background: var(--fs-panel2); color: var(--fs-accent); }
|
||||
/* after hover on purpose: hovering the selected row must still read as selected — docs/css.md
|
||||
* "Dropdown: the open list's rail and the selected-row's contrast". */
|
||||
.cbi-dropdown[open] > ul.dropdown > li[selected] {
|
||||
/* @mirror selected-row/paint */
|
||||
background: var(--fs-panel2); color: var(--fs-accent); box-shadow: var(--fs-row-rail);
|
||||
/* @endmirror */
|
||||
}
|
||||
/* theme's ring, not the UA's outline:auto; inset, since the menu clips overflow, rail rides
|
||||
* INSIDE it so a focused+selected row keeps both */
|
||||
.cbi-dropdown[open] > ul.dropdown > li:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--fs-row-rail), inset 0 0 0 2px var(--fs-accent-soft);
|
||||
}
|
||||
|
||||
/* MULTI-select shows "chosen" via its CHECKBOX, drops the rail; pinned byte-identical to
|
||||
* `select option:checked` in theme/60-inputs.css (tools/mirror.mjs), which has no checkbox */
|
||||
.cbi-dropdown[multiple][open] > ul.dropdown > li[selected]:not(:focus-visible) { box-shadow: none; }
|
||||
.cbi-dropdown[multiple][open] > ul.dropdown > li[selected]:focus-visible {
|
||||
box-shadow: inset 0 0 0 2px var(--fs-accent-soft);
|
||||
}
|
||||
|
||||
.cbi-dropdown[open] > ul.dropdown > li.create-item { border-top: var(--fs-hairline); margin-top: var(--fs-space-1); }
|
||||
.cbi-dropdown .create-item-input { background: var(--fs-panel2); border: var(--fs-hairline); border-radius: var(--fs-radius); color: var(--fs-text); }
|
||||
/* dropdown add-item (e.g. Community Lists) — the .cbi-dropdown styles itself, wrapper stays transparent: no double frame */
|
||||
.cbi-dynlist > .add-item:has(> .cbi-dropdown) {
|
||||
background: none; border: 0; overflow: visible; box-shadow: none;
|
||||
}
|
||||
|
||||
.cbi-section-table .td .cbi-dropdown,
|
||||
.cbi-section-table .td .cbi-input-select,
|
||||
.cbi-section-table .td select,
|
||||
.cbi-section-table .td input[type="text"] {
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* below ~960px a multi-dropdown row no longer fits, so each row cards, same shape as the
|
||||
* data-table stack (theme/30-tables.css); a @container, not the tables' own measurement, since
|
||||
* un-collapsing a row to measure it CHANGES what its widgets read. 992, not 960 — docs/css.md
|
||||
* "Dropdown: 992px, not 960" has the padding subtraction and the regression it fixed. */
|
||||
@container fs-content (max-width: 992px) {
|
||||
.table.cbi-section-table,
|
||||
.table.cbi-section-table .thead,
|
||||
.table.cbi-section-table .tbody,
|
||||
.table.cbi-section-table .td { display: block; width: auto; }
|
||||
/* a card repeats every label, so the header goes in BOTH markups (form.js's .thead children,
|
||||
* the Lua CBI's bare titles/descr rows) — except the opt-in Table Filters row (25.12 tags it
|
||||
* -titles too): no card repeats it, and hidden its 4 gallery controls rendered 0x0 */
|
||||
.table.cbi-section-table .thead > :not(.cbi-section-table-filter),
|
||||
.table.cbi-section-table .tr.cbi-section-table-titles:not(.cbi-section-table-filter),
|
||||
.table.cbi-section-table .tr.cbi-section-table-descr { display: none; }
|
||||
/* stretch (not flex-start) keeps a pair's cells the same height when one wraps */
|
||||
.table.cbi-section-table .tr {
|
||||
display: flex; flex-wrap: wrap; width: auto;
|
||||
border: var(--fs-hairline);
|
||||
border-radius: var(--fs-radius-lg);
|
||||
background: var(--fs-panel2);
|
||||
padding: var(--fs-space-2) var(--fs-space-3-5) var(--fs-space-3);
|
||||
margin-bottom: var(--fs-space-3);
|
||||
}
|
||||
.table.cbi-section-table .tr:hover { background: var(--fs-panel2); }
|
||||
/* margin: 0 load-bearing: the Lua CBI gives a table CELL the .cbi-value-field class a form
|
||||
* field carries, base indents it 20px — without the reset two cells no longer fit a line */
|
||||
.table.cbi-section-table .td {
|
||||
flex: 1 1 100%; min-width: 0; margin: 0;
|
||||
padding-block: var(--fs-space-1-5); padding-inline: 0 var(--fs-space-2-5); border: 0; text-align: start;
|
||||
/* overflow-wrap: anywhere for opaque tokens (a real IPv6 GUA has no break point) — one
|
||||
* ran 156px past a 360px viewport before this */
|
||||
overflow-wrap: anywhere; white-space: normal;
|
||||
}
|
||||
/* the filter cells are .th, so the .td rule above never reaches them: one field per card
|
||||
* line, and no row divider (.cbi-section .table .th's border-bottom) between fields */
|
||||
.table.cbi-section-table .tr.cbi-section-table-filter > .th {
|
||||
flex: 1 1 100%; min-width: 0; border: 0;
|
||||
padding-block: var(--fs-space-1-5); padding-inline: 0 var(--fs-space-2-5);
|
||||
}
|
||||
/* a cell that only READS a value is short, two fit a line; a real widget keeps full width (a dropdown at half a phone's width is unusable) */
|
||||
.table.cbi-section-table .td[data-widget="CBI.DummyValue"],
|
||||
.table.cbi-section-table .td[data-widget="CBI.FlagValue"],
|
||||
.table.cbi-section-table .td[data-widget="CBI.Button"],
|
||||
.table.cbi-section-table .td[data-widget="dvalue"],
|
||||
.table.cbi-section-table .td[data-widget="fvalue"],
|
||||
.table.cbi-section-table .td[data-widget="button"] { flex-basis: 50%; }
|
||||
/* PINNED to the data table's copy (theme/30-tables.css, tools/mirror.mjs): needed under a
|
||||
* CLASS there and under this @container here, and CSS cannot OR the two guards. */
|
||||
.table.cbi-section-table .td[data-title]::before {
|
||||
/* @mirror table-card/label */
|
||||
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);
|
||||
/* @endmirror */
|
||||
}
|
||||
/* the row's buttons are not a label/value pair: full width, sharing the line. PINNED — see above. */
|
||||
.table.cbi-section-table .td.cbi-section-actions {
|
||||
/* @mirror table-card/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);
|
||||
/* @endmirror */
|
||||
}
|
||||
.table.cbi-section-table .td.cbi-section-actions::before { content: none; }
|
||||
/* LuCI groups the row's buttons in ONE inner div, so the td's own wrap never fires — the
|
||||
* div is a single flex item. PINNED to the data table's copy, same trap on Startup. */
|
||||
.table.cbi-section-table .td.cbi-section-actions > * {
|
||||
/* @mirror table-card/actions-inner */
|
||||
flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: var(--fs-space-2);
|
||||
/* @endmirror */
|
||||
}
|
||||
.table.cbi-section-table .td .zone-forwards { width: 100%; }
|
||||
}
|
||||
|
||||
/* the table card (theme/30-tables.css) clips with overflow: hidden, but a .cbi-dropdown's open
|
||||
* list is absolutely positioned — drop the clip, restore corners via separated borders instead */
|
||||
.table:has(.cbi-dropdown),
|
||||
.table:has(.cbi-dynlist) {
|
||||
overflow: visible;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user