mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-10 18:34:18 +08:00
296 lines
16 KiB
CSS
296 lines
16 KiB
CSS
@layer theme {
|
|
/* Buttons placed straight in a field, not in a .control-group (app action rows
|
|
* Enable/Disable, Start/Restart/Stop): inline-block buttons only get whitespace spacing
|
|
* horizontally, so on wrap the rows collapsed together. Wrapping flex with a real gap. */
|
|
.cbi-value-field:has(> .cbi-button) {
|
|
display: inline-flex; flex-wrap: wrap; gap: var(--fs-space-2); align-items: center; justify-content: flex-end;
|
|
}
|
|
|
|
/* The button. The native `input` types are in the selector list on purpose: LuCI's own views
|
|
* always use <button class="cbi-button">, but a third-party luci-app-* may emit a bare
|
|
* <input type="submit">, and base only gives those width/height: auto — coverage is a
|
|
* contract (docs/gallery.html). `.btn` and `.cbi-button` are interchangeable in LuCI markup
|
|
* (a row-action button usually carries both), so every rule here names both. */
|
|
.cbi-button, .btn,
|
|
input[type="button"], input[type="submit"], input[type="reset"] {
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
/* auto, stated: theme/60-inputs.css's generic `input` box says 210px x 32px, and a base
|
|
* reset would lose to it by layer — a button sizes to its label */
|
|
width: auto; height: auto;
|
|
min-height: var(--fs-ctl-h);
|
|
padding: var(--fs-space-2) var(--fs-space-3-5);
|
|
font-family: var(--fs-font-sans); font-weight: var(--fs-weight); line-height: 1.1;
|
|
color: var(--fs-text);
|
|
background: var(--fs-panel2);
|
|
border: var(--fs-hairline);
|
|
border-radius: var(--fs-radius);
|
|
box-shadow: none; text-shadow: none;
|
|
/* border-COLOR, not `border`: the shorthand animates border-width too — a layout
|
|
* animation on every hover instead of a paint one (absorbed from base with the ring) */
|
|
transition: var(--fs-field-transition);
|
|
}
|
|
/* bare `button` rides along: a third-party app's unclassed <button> hovered the same border
|
|
* cue in base's old glow rule, and border-color is a no-op on its border: 0 box — harmless
|
|
* there, meaningful the moment the app gives it a border */
|
|
.cbi-button:hover, .btn:hover, button:hover,
|
|
input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover { border-color: var(--fs-dim); }
|
|
|
|
/* Keyboard focus, not decoration: without this a button shows nothing on focus. The
|
|
* `box-shadow: none` above kills any stray focus glow (theme beats base regardless of
|
|
* specificity), and the UA outline only ever shows on :focus-visible, which this rule restyles.
|
|
* A plain mouse :focus deliberately shows nothing — a glow rings every clicked button, and the
|
|
* pressed cue below already answers the click. Same --fs-focus-ring as every field. */
|
|
.cbi-button:focus-visible, .btn:focus-visible,
|
|
input[type="button"]:focus-visible, input[type="submit"]:focus-visible, input[type="reset"]:focus-visible {
|
|
outline: none;
|
|
border-color: var(--fs-accent);
|
|
box-shadow: var(--fs-focus-ring);
|
|
}
|
|
/* Solid variants sit ON --fs-accent/--fs-good/--fs-danger, so an accent-tinted ring is invisible
|
|
* against them: ring in the surface colour with an accent halo. Written out as a literal ending
|
|
* in --fs-accent-soft, that halo was a 10-15% tint at 1.15-1.29:1 and the only thing it painted
|
|
* was the 2px gap; --fs-focus-ring-solo is the same geometry with a solid accent, under one
|
|
* name. */
|
|
.cbi-button-positive:focus-visible, .cbi-button-action:focus-visible,
|
|
.cbi-button-negative:focus-visible, .cbi-button-remove:focus-visible,
|
|
.cbi-button-apply:focus-visible, .cbi-button-save:focus-visible,
|
|
.btn.primary:focus-visible, .btn.danger:focus-visible,
|
|
.btn.error:focus-visible, .btn.success:focus-visible, .btn.info:focus-visible {
|
|
outline: none;
|
|
border-color: transparent;
|
|
box-shadow: var(--fs-focus-ring-solo);
|
|
}
|
|
|
|
/* Pressed state. The `box-shadow: none` above would erase any press cue, so the inset shadow
|
|
* is declared here — the sole copy (base's identical one was absorbed). */
|
|
.cbi-button:active, .btn:active, .btn.active,
|
|
input[type="button"]:active, input[type="submit"]:active, input[type="reset"]:active {
|
|
box-shadow: inset 0 2px 4px color-mix(in srgb, var(--fs-border) 40%, transparent);
|
|
}
|
|
|
|
/* Solid variants — three fills, not six. `.cbi-button-*` is what LuCI's own views emit and
|
|
* `.btn.<colour-word>` what a third-party luci-app-* tends to emit, and base paints those words
|
|
* with gradients that are dead: the plain `.btn` rule above sets background, border and colour
|
|
* in a later layer, so an unclaimed `.btn.danger` renders as a neutral grey button with no
|
|
* colour cue. Coverage is a contract, so the theme claims them — in the SAME rule as their
|
|
* .cbi-button-* twin, because written apart a recolour lands on one half and not the other. */
|
|
.cbi-button-positive, .btn.success {
|
|
background: var(--fs-good); border-color: transparent; color: var(--fs-on-good);
|
|
}
|
|
.cbi-button-action, .cbi-button-action.important, .btn.primary, .btn.info {
|
|
background: var(--fs-accent); border-color: transparent; color: var(--fs-on-accent);
|
|
}
|
|
.cbi-button-negative, .cbi-button-negative.important, .cbi-button-remove,
|
|
.btn.danger, .btn.error {
|
|
background: var(--fs-danger); border-color: transparent; color: var(--fs-on-danger);
|
|
}
|
|
/* This hover must stay AHEAD of the page-action Save rule below: at equal specificity its
|
|
* `border-color: transparent` would erase Save's outline on hover. (.cbi-button-remove is
|
|
* deliberately absent — it never had the lift, and the row-action rules colour it their
|
|
* own way.) */
|
|
.cbi-button-positive:hover, .cbi-button-action:hover, .cbi-button-apply:hover,
|
|
.cbi-button-save:hover, .cbi-button-negative:hover,
|
|
.btn.success:hover, .btn.info:hover, .btn.danger:hover, .btn.error:hover {
|
|
filter: var(--fs-hover-lift); border-color: transparent;
|
|
}
|
|
.cbi-button-reset, .cbi-button-link { background: transparent; border-color: var(--fs-border); color: var(--fs-dim); }
|
|
|
|
/* Size words. The `.btn` rule above re-sets padding and radius from the token scale in a
|
|
* later layer, so a size word that carried only a font-size (as base's absorbed copy did)
|
|
* came out with default-button padding. Theme owns .btn.large/.small in full now, geometry
|
|
* on the radius scale rather than base's hardcoded 6px. */
|
|
.btn.large { font-size: var(--fs-type-lg); min-height: var(--fs-ctl-h-lg); padding: var(--fs-space-2) var(--fs-space-4); border-radius: var(--fs-radius); }
|
|
.btn.small { font-size: var(--fs-type-xs); min-height: var(--fs-ctl-h-sm); padding: var(--fs-space-1) var(--fs-space-2-5); border-radius: var(--fs-radius-sm); }
|
|
|
|
.cbi-section .table[id] .td .btn,
|
|
.cbi-section .table[id] .td .cbi-button { padding: var(--fs-space-1) var(--fs-space-2-5); font-size: var(--fs-type); }
|
|
.alert-message .btn, .alert-message .cbi-button {
|
|
border-radius: var(--fs-radius); padding: var(--fs-space-2) var(--fs-space-3); font-weight: var(--fs-weight);
|
|
background: var(--fs-panel2); border: var(--fs-hairline); color: var(--fs-text);
|
|
}
|
|
.alert-message .btn:hover { border-color: var(--fs-accent); color: var(--fs-accent); }
|
|
|
|
/* Page action bar (.cbi-page-actions) — the Save/Apply footer. base renders a tall
|
|
* half-empty plate with bottom-only radius; make it a compact rounded action bar. */
|
|
.cbi-page-actions {
|
|
background: transparent;
|
|
border: 0;
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
padding: var(--fs-space-2) 0;
|
|
margin: 0 0 var(--fs-card-gap);
|
|
text-align: end;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: var(--fs-space-2);
|
|
}
|
|
.cbi-page-actions .cbi-button,
|
|
.cbi-page-actions .btn {
|
|
min-height: var(--fs-ctl-h);
|
|
padding: 0 var(--fs-space-5);
|
|
border-radius: var(--fs-radius);
|
|
font-weight: var(--fs-weight);
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
vertical-align: middle;
|
|
}
|
|
/* Reset takes the same step of the ladder as Save (-soft at rest, -fill on hover), so the two read
|
|
* as equally weighted secondary actions beside the solid Save & Apply — but off the DANGER role,
|
|
* not the accent one: Reset discards your unsaved changes. (An opaque --fs-panel2 was the first
|
|
* attempt and weighed MORE than Save's translucent 10-15% tint: a solid surface and a tint do
|
|
* not read as the same fill, whatever their lightness.) The label is --fs-text for the same
|
|
* reason as Save's — --fs-dim on
|
|
* --fs-danger-soft measures 4.49:1 in the worst palette x mode block, under AA by a hundredth.
|
|
* The role lives in the fill and the border. */
|
|
.cbi-page-actions .cbi-button-reset {
|
|
background: var(--fs-danger-soft);
|
|
border: 1px solid var(--fs-danger-line);
|
|
color: var(--fs-text);
|
|
}
|
|
.cbi-page-actions .cbi-button-reset:hover {
|
|
background: var(--fs-danger-fill);
|
|
border-color: var(--fs-danger-line-hi);
|
|
}
|
|
|
|
/* There is no `.spinning` padding ladder here, and it must not come back: seven selectors
|
|
* reserving room for an absolutely-positioned glyph, each climbing a class above whatever else
|
|
* padded that element. The ladder lost twice in the field (issues #15 and #22), and every loss
|
|
* looks the same — the spinner draws on top of the label, silently; #22 lost to a 0,5,0
|
|
* data-table rule. base/95-luci.css puts the
|
|
* glyph IN THE FLOW instead, so it reserves its own space. */
|
|
|
|
input[type="file"]::file-selector-button {
|
|
margin-inline-end: var(--fs-space-3); padding: var(--fs-space-2) var(--fs-space-3-5);
|
|
font-family: var(--fs-font-sans); font-size: var(--fs-type); font-weight: var(--fs-weight);
|
|
color: var(--fs-text); background: var(--fs-panel2);
|
|
border: var(--fs-hairline); border-radius: var(--fs-radius-sm); cursor: pointer;
|
|
}
|
|
input[type="file"]::file-selector-button:hover { border-color: var(--fs-accent); color: var(--fs-accent); }
|
|
|
|
/* Row-action buttons (Edit / Delete / Start-Stop) in a table or a .cbi-section-actions container.
|
|
* Compact so tables stay dense: neutral outline by default, with the variants colouring the ones
|
|
* that carry meaning. The neutral :hover comes FIRST on purpose — last, it greys the coloured
|
|
* variants back out on hover, which only an !important on their colours hides. Equal
|
|
* specificity, so a variant hover must be written after it.
|
|
*
|
|
* WCAG 2.2 SC 2.5.8: a pointer target must be >= 24x24 px, or spaced so 24px circles centred on
|
|
* adjacent targets do not overlap. At 28px tall with a 1px margin either side the circles DO
|
|
* overlap, so the exemption does not apply; 32px clears it. */
|
|
.td .cbi-button, .td .btn, .cbi-section-actions .cbi-button, .cbi-section-actions .btn,
|
|
.cbi-section-table .cbi-button {
|
|
min-height: var(--fs-ctl-h-sm); padding: 0 var(--fs-space-3); font-size: var(--fs-type);
|
|
}
|
|
.td .cbi-button, .cbi-section-actions .cbi-button, .cbi-section-table .cbi-button {
|
|
background: transparent; background-image: none;
|
|
border: var(--fs-hairline); color: var(--fs-dim);
|
|
}
|
|
.td .cbi-button:hover { border-color: var(--fs-dim); color: var(--fs-text); }
|
|
|
|
.td .cbi-button-action, .td .cbi-button-edit, .td .cbi-button-apply,
|
|
.cbi-section-actions .cbi-button-edit, .cbi-section-actions .cbi-button-action {
|
|
color: var(--fs-accent); border-color: var(--fs-accent-line); background: transparent;
|
|
}
|
|
/* THE HOVER INK IS --fs-text, NOT THE ROLE COLOUR, and that is the rule this file already
|
|
* states twice with measurements: text of colour C on a translucent tint of C eats its own
|
|
* contrast (Save: accent on accent-soft = 4.25:1, an AA failure; the alert `.label` chip: 3.4-4.4
|
|
* across the palettes). The row actions were the last place it survived — the role still reads,
|
|
* from the fill and the border, which is exactly how Save and Reset carry theirs. */
|
|
.td .cbi-button-action:hover, .td .cbi-button-edit:hover, .td .cbi-button-apply:hover,
|
|
.cbi-section-actions .cbi-button-action:hover {
|
|
background: var(--fs-accent-soft); color: var(--fs-text);
|
|
border-color: var(--fs-accent-line-hi);
|
|
}
|
|
/* An enabled/positive toggle in a table cell (System -> Startup) rendered as blue text on a
|
|
* green fill = unreadable. Green outline instead, matching the red-outline negative button. */
|
|
.td .cbi-button-positive, .cbi-section-actions .cbi-button-positive {
|
|
color: var(--fs-good); border-color: var(--fs-good-line); background: transparent;
|
|
}
|
|
/* …and the border-colour the other two variants already had. Without it the green outline stayed
|
|
* at -line while action and negative brightened to -line-hi, so hovering the positive toggle
|
|
* read as less responsive than its neighbours in the same row. */
|
|
.td .cbi-button-positive:hover, .cbi-section-actions .cbi-button-positive:hover {
|
|
background: var(--fs-good-soft); color: var(--fs-text);
|
|
border-color: var(--fs-good-line-hi);
|
|
}
|
|
.td .cbi-button-negative, .td .cbi-button-remove,
|
|
.cbi-section-actions .cbi-button-remove, .cbi-section-actions .cbi-button-negative {
|
|
color: var(--fs-danger); border-color: var(--fs-danger-line); background: transparent;
|
|
}
|
|
.td .cbi-button-negative:hover, .td .cbi-button-remove:hover,
|
|
.cbi-section-actions .cbi-button-negative:hover {
|
|
background: var(--fs-danger-soft); color: var(--fs-text);
|
|
border-color: var(--fs-danger-line-hi);
|
|
}
|
|
|
|
/* Page actions: Save & Apply is THE primary action, Save is demoted so only one button reads
|
|
* as primary. `--on-color` drives the spinner mask in styles/base. */
|
|
.cbi-page-actions .cbi-button-apply,
|
|
.cbi-page-actions .cbi-dropdown .cbi-button-apply {
|
|
background: var(--fs-accent);
|
|
background-image: none;
|
|
border: 1px solid transparent;
|
|
color: var(--fs-on-accent);
|
|
--on-color: var(--fs-on-accent);
|
|
text-shadow: none;
|
|
}
|
|
/* Save carries a TINT at rest, not a bare outline: beside the solid Save & Apply a fully
|
|
* transparent button reads as disabled rather than secondary. The steps come off the role ladder
|
|
* (02-tokens.css) — -soft (12%) at rest, -fill (18%) on hover, one step up the same ladder.
|
|
*
|
|
* The label is --fs-text, not --fs-accent: never put text of colour C on a translucent tint of C
|
|
* (docs/design-system.md). The tint drags the surface toward the text and eats its own contrast
|
|
* — accent on accent-soft measures 4.25:1, an AA failure. The role is carried by the fill and
|
|
* the border.
|
|
*
|
|
* `filter: none` cancels the shared hover's --fs-hover-lift: on a translucent tint a brightness
|
|
* shift washes the fill out instead of deepening it. */
|
|
.cbi-page-actions .cbi-button-save,
|
|
.cbi-page-actions .cbi-button-apply + .cbi-button-save,
|
|
.cbi-page-actions .cbi-button-negative + .cbi-button-save {
|
|
background: var(--fs-accent-soft);
|
|
background-image: none;
|
|
border: 1px solid var(--fs-accent-line);
|
|
color: var(--fs-text);
|
|
--on-color: var(--fs-on-accent);
|
|
text-shadow: none;
|
|
}
|
|
.cbi-page-actions .cbi-button-save:hover {
|
|
background: var(--fs-accent-fill);
|
|
border-color: var(--fs-accent-line-hi);
|
|
filter: none;
|
|
}
|
|
|
|
/* Buttons that are part of a compound field widget. */
|
|
.cbi-dynlist > .add-item > button,
|
|
.cbi-dynlist > .add-item > .cbi-button-add {
|
|
/* min-width was a bare 38px — the same number --fs-ctl-h already states. A restated value
|
|
* is the drift this ladder exists to prevent, and it also sat out the Density axis. */
|
|
flex: 0 0 auto; width: auto; min-width: var(--fs-ctl-h);
|
|
min-height: var(--fs-ctl-h-sm); padding: 0 var(--fs-space-3); margin: 0;
|
|
border: 0; border-inline-start: var(--fs-hairline);
|
|
border-radius: 0;
|
|
background: transparent; color: var(--fs-dim);
|
|
}
|
|
.cbi-dynlist > .add-item > button:hover,
|
|
.cbi-dynlist > .add-item > .cbi-button-add:hover { color: var(--fs-accent); }
|
|
.control-group:has(> .cbi-input-password) > button.cbi-button-neutral {
|
|
border: 0; border-radius: 0; margin: 0;
|
|
background: transparent; color: var(--fs-dim);
|
|
padding: 0 var(--fs-space-3); min-height: var(--fs-ctl-h);
|
|
border-inline-start: var(--fs-hairline);
|
|
}
|
|
.control-group:has(> .cbi-input-password) > button.cbi-button-neutral:hover { color: var(--fs-accent); }
|
|
/* SQUARE, ON THE LADDER: these were the only buttons in the theme set in bare pixels
|
|
* (40 wide x 34 high), and 34 was the one control height that appears in no scale — the ladder
|
|
* is --fs-ctl-h-sm/-h/-h-lg = 30/38/44, plus the derived FIELD height (--fs-control-h, 32).
|
|
* A literal also opts out of the Density axis: every other control shrinks with
|
|
* --fs-density-box and the pager did not. Square because the glyph is an arrow, not a label. */
|
|
.pager .btn.prev, .pager .btn.next {
|
|
flex: 0 0 auto; width: var(--fs-ctl-h); min-width: var(--fs-ctl-h); height: var(--fs-ctl-h); padding: 0;
|
|
border-radius: var(--fs-radius); border: var(--fs-hairline);
|
|
background: var(--fs-panel2); color: var(--fs-text);
|
|
display: inline-flex; align-items: center; justify-content: center;
|
|
}
|
|
.pager .btn.prev:hover, .pager .btn.next:hover { border-color: var(--fs-accent); color: var(--fs-accent); }
|
|
}
|