Files
op-packages/luci-theme-footstrap/styles/pages/80-appearance.css
T

213 lines
14 KiB
CSS

@layer page {
/* ==================================================================
* The Appearance controls, appended to System -> System by fs-appearance.js.
*
* This was a popover on <body>, placed against a chrome button by widgets.placePopover and
* capped to the viewport because eighteen axes plus an upload panel do not fit a floating card
* on a phone in landscape. As part of a page none of that applies: the sections are ordinary
* .cbi-section cards, the width is the content column's, and the scroll is the document's. The
* placement, the max-height clamp, the fade-in and the [hidden] pair went with the popover.
*
* The CONTROLS are still the theme's own primitives (fs-widgets.js), not LuCI form widgets:
* .fs-seg is a radiogroup with a roving tabindex, .fs-range is the Rounding slider. They live in
* the page layer rather than the theme layer because this is the only page that draws them — an
* axis control is not chrome any more.
* ================================================================== */
.fs-ap { display: flex; flex-direction: column; gap: var(--fs-space-5); }
/* Grouping headings INSIDE the tab pane, not cards: the pane is already inside the stock
* .cbi-section card, and a second border around each group is a box in a box. A hairline above
* each group does the same job — except for the first, which sits under the tab strip. */
/* the heading row. On a plain group it holds the title; on a FOLDED one the whole row is the
* disclosure button (fs-appearance.js) — full width, so the target is the heading rather than a
* chevron nobody can hit on a phone. */
/* the heading row spans the section on its own now that the rows stack */
button.fs-ap-fold {
display: flex; align-items: center; justify-content: space-between; gap: var(--fs-space-3);
inline-size: 100%; padding: 0; margin: 0;
background: none; border: 0; cursor: pointer; text-align: start; color: inherit;
}
button.fs-ap-fold:hover h4 { color: var(--fs-accent); }
button.fs-ap-fold:focus-visible { outline: none; box-shadow: var(--fs-focus-ring-solo); border-radius: var(--fs-radius-sm); }
/* The chevron: the same 26px box with a two-border ::after that the overview's card toggles
* use (styles/pages/20-overview.css) — one chevron for "this panel opens", drawn here and there
* rather than an SVG in one place and borders in the other, with the glyph itself pinned as
* @mirror chevron/* so the two copies cannot drift.
*
* It turns on the SAME attribute that tells a screen reader the state, so the two cannot
* disagree. Rotated with `transform` and not the individual `rotate` property, because that is
* what the overview's copy does and the pair is meant to look identical mid-animation too. */
.fs-ap-chev {
flex: 0 0 auto; inline-size: 26px; block-size: 26px;
display: inline-flex; align-items: center; justify-content: center;
color: var(--fs-dim); border-radius: var(--fs-radius-sm);
}
.fs-ap-chev::after {
/* @mirror chevron/closed */
content: "";
width: 7px;
height: 7px;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
border-radius: 1px;
transform: rotate(45deg); /* closed: chevron down */
margin-top: -2px; /* optical centring of the down chevron */
transition: transform var(--fs-dur-move);
/* @endmirror */
}
button.fs-ap-fold[aria-expanded="true"] .fs-ap-chev::after {
/* @mirror chevron/open */
transform: rotate(-135deg); /* open: chevron up */
margin-top: var(--fs-space-0-5);
/* @endmirror */
}
button.fs-ap-fold:hover .fs-ap-chev { color: var(--fs-accent); }
/* Stock `.cbi-value` rows stack one per line and carry their own hairline, so a folded group needs
* no layout of its own — it lays its rows out exactly like an unfolded section by doing nothing. */
/* an explicit `display` DEFEATS the `hidden` attribute (the UA rule is a bare 0,0,1), and hidden
* is what keeps a closed group out of the tab order and the a11y tree */
.fs-ap-body[hidden] { display: none; }
/* the second heading inside the Colours fold — Surfaces shares the fold, not the heading. It
* wears the SAME .fs-ap-head h4 as every other section title now; a third heading style on one
* page (h4, folded h4, uppercase eyebrow h5) was a third of the raggedness. Only the space
* above it is its own. */
.fs-ap-sub { margin-top: var(--fs-space-4); }
.fs-ap-head h4 {
margin: 0;
font-size: var(--fs-type); font-weight: var(--fs-weight-bold); color: var(--fs-text);
}
.fs-ap-section + .fs-ap-section { padding-top: var(--fs-space-4); border-top: 1px solid var(--fs-border); }
/* THE ROWS ARE STOCK `.cbi-value` NOW and this page states no layout for them at all — caption
* column, field column and the hairline between rows come from base/30-forms.css and
* theme/60-inputs.css, the same rules every other LuCI form is drawn by. What was here before was
* a responsive two-column grid of stacked cards with an uppercase eyebrow above each control: it
* fitted a floating popover, and beside General Settings / Language and Style it made the theme's
* own tab the one page in LuCI that did not look like LuCI. Deleting the grid is the change. */
/* The accordion switch is meaningless in the top layout — its sections are hover dropdowns,
* already exclusive. Hidden HERE, not by a branch in the JS: the page is built once, so a branch
* froze the control to whatever layout it loaded in and the live toggle could never change it. */
:root[data-layout="top"] .fs-ap-submenus { display: none; }
/* Save-as-default / Reset row: two equal buttons, no status text — the Save button's own
* label/disabled state is the status. They take their look from .btn / .cbi-button-action
* (base + theme/55-buttons.css); a disabled .cbi-button-action already greys via base. */
/* Buttons take the width of their own label, which is what every button on every other LuCI page
* does — they were `flex: 1 1 9em` and stretched to fill the field, so three of them made one
* long bar and "Choose SVG"/"Remove" each ran half the card. `wrap` keeps a narrow card honest:
* an item that cannot fit moves to the next line instead of hanging past the edge, which on a
* flex row is silent. */
.fs-ap-actrow { display: flex; flex-wrap: wrap; gap: var(--fs-space-2); }
.fs-ap-actrow .btn { font-size: var(--fs-type); padding: var(--fs-space-2) var(--fs-space-2-5); cursor: pointer; }
.fs-ap-actrow .btn:disabled { cursor: default; }
/* the primed Reset (second click confirms) reads as the destructive action it is */
.fs-ap-armed { color: var(--fs-on-danger); background: var(--fs-danger); border-color: transparent; }
/* Save's only visible failure line (a rejected save rpc — usually a stale session) — shown in
* place of the absent status text, hidden until a save actually fails. */
.fs-ap-err { margin-top: var(--fs-space-2); font-size: var(--fs-type-xs); line-height: 1.35; color: var(--fs-danger); }
/* ---- the colour axes ----
* One row is a swatch, a hex field and the button back to the palette's own colour. */
/* the same ceiling every other stretching control in a form row takes (02-tokens.css), so the
* swatch + hex + Palette button end where the selects and the sliders end */
.fs-colorctl { display: flex; flex-direction: column; gap: var(--fs-space-1-5); max-width: var(--fs-field-max); }
.fs-color-row { display: flex; align-items: center; gap: var(--fs-space-2); }
/* The native colour swatch. type="color" is drawn by the browser and cannot be restyled beyond
* its box, so the box is what is styled: the inner well is stripped to the frame's edge on both
* engines (WebKit nests two elements, Firefox one) or it paints a white margin inside our
* border. Fixed 34px square so it lines up with the field beside it at every density. */
input[type="color"].fs-color-swatch {
flex: 0 0 auto; inline-size: 34px; block-size: 34px; padding: 0; cursor: pointer;
background: none; border: 1px solid var(--fs-border); border-radius: var(--fs-radius-sm);
}
input[type="color"].fs-color-swatch::-webkit-color-swatch-wrapper { padding: 0; }
input[type="color"].fs-color-swatch::-webkit-color-swatch { border: 0; border-radius: calc(var(--fs-radius-sm) - 1px); }
input[type="color"].fs-color-swatch::-moz-color-swatch { border: 0; border-radius: calc(var(--fs-radius-sm) - 1px); }
/* the hex field is a VALUE, so it is mono and tabular like every other value in the theme —
* and #0969da is exactly the kind of string a proportional face makes hard to read back */
input[type="text"].fs-color-hex {
flex: 1 1 auto; min-inline-size: 0;
font-family: var(--fs-font-mono); font-size: var(--fs-type-xs); font-variant-numeric: tabular-nums;
text-transform: lowercase;
}
.fs-color-clear { flex: 0 0 auto; font-size: var(--fs-type-xs); padding: var(--fs-space-1-5) var(--fs-space-2); cursor: pointer; }
.fs-color-clear:disabled { cursor: default; }
/* What the chosen colour COSTS, measured live against the surface it is read on (fs-widgets'
* contrastRatio). It is a readout, not a validation: the theme derives readable ink over a fill
* but never silently corrects a colour the admin asked for, so this says the number and grades
* it. Three grades, three colours — and the failing one is --fs-danger, which the admin may have
* just recoloured, which is exactly the honest outcome. */
/* It is LuCI's own help-text line now — `.cbi-value-description`, styled in base/95-luci.css,
* which is where a sentence under a field belongs on every other page. What stays here is the
* only thing stock has no opinion about: the GRADE's colour, and the tabular figures that keep
* the ratio from jittering as it updates. */
.fs-color-contrast { font-variant-numeric: tabular-nums; }
.fs-contrast-aa { color: var(--fs-good); }
.fs-contrast-aa-large { color: var(--fs-warn); }
.fs-contrast-low { color: var(--fs-danger); }
/* THE PRESET CHIPS' rules were here — a wrapping row of pills, each painted in the accent it
* would set, with ink derived from that colour's own lightness. The control is gone from
* fs-appearance.js: it was the one thing on the page that looked like nothing else in LuCI, it
* started at the card's edge instead of at the field column, and everything it did the Accent
* row does with any colour rather than eight. */
/* The uploaded image's PREVIEW and the two buttons under it. Both live in an ordinary
* `.cbi-value-field` now — the sub-panel that used to wrap them is gone, along with the second
* caption column it nested inside the first.
*
* The preview is a thumbnail, not a banner: capped to the same field ceiling as every other
* stretching control and to a fixed height, so the row keeps one shape whatever the image's
* aspect is. It ran the full width of the card before, which made an SVG tile look like a
* letterboxed photograph. */
.fs-ap-bgprev {
display: block; width: 100%; max-width: var(--fs-field-max); height: 84px; object-fit: cover;
border: 1px solid var(--fs-border); border-radius: var(--fs-radius);
/* the preview follows the buttons in the field, so the gap belongs ABOVE it */
margin-top: var(--fs-space-2);
}
.fs-ap-bgrow { display: flex; flex-wrap: wrap; gap: var(--fs-space-2); }
.fs-ap-bgrow .btn { font-size: var(--fs-type); padding: var(--fs-space-2) var(--fs-space-2-5); cursor: pointer; }
.fs-ap-bgrow .btn:disabled { cursor: default; }
/* an explicit `display` DEFEATS the `hidden` attribute (the UA's `[hidden]{display:none}` is a
* bare 0,0,1 rule these out-rank), so the JS's row.hidden / preview.hidden / removeBtn.hidden
* would not hide a row that does not apply to the chosen wallpaper, the empty preview, or a
* not-yet-uploaded Remove. Restate hidden at a specificity that wins — `.cbi-value` is the one
* that matters most here, since base gives it `display: flex`. */
.cbi-value[hidden], .fs-ap-bgprev[hidden], .fs-ap-bgrow .btn[hidden] { display: none; }
/* Under the File wallpaper only the photo Dim is relevant — the router-identity Tint colours the
* canvas the photo covers, so hide the Tint hue AND its strength there (fs-ap-tint marks both). */
:root[data-wallpaper="file"] .fs-ap-tint { display: none; }
/* and the strength is a multiplier on a chroma WE choose, so it only means anything in hue mode:
* with the tint off there is nothing to scale, and on a hex canvas the colour is the one the
* admin entered, with no chroma of ours in it. (0,3,0) out-ranks the stock row's own display.
* The class was `fs-ap-density` while this axis was called Tint density; the name moved with the
* label, because Density is the UI-density select four rows above and one name for two rows is
* how the label itself went wrong. */
:root:not([data-tint="hue"]) .fs-ap-tintstr { display: none; }
/* the version line at the foot of the page */
.fs-ap-footer {
display: flex; flex-direction: column; gap: var(--fs-space-2);
margin-top: var(--fs-space-0-5); padding-top: var(--fs-space-2-5); border-top: 1px solid var(--fs-border);
}
.fs-ap-verrow { display: flex; align-items: center; justify-content: space-between; gap: var(--fs-space-2-5); }
/* links to the project repo — reads as muted text until hovered */
.fs-ap-version { font-size: var(--fs-type-xs); color: var(--fs-dim); font-variant-numeric: tabular-nums; text-decoration: none; }
.fs-ap-version:hover { color: var(--fs-accent); text-decoration: underline; }
/* THE SEGMENTED CONTROL AND THE RANGE SLIDER had their rules here — .fs-seg, .fs-rangewrap,
* .fs-range and its readout. Both controls are gone from fs-widgets.js: the page draws its enums
* with `ui.Select` and its numbers with `ui.RangeSlider`, which this theme already styles for
* every other LuCI form (`select` in theme/60-inputs.css, `.cbi-range-slider` and
* `input[type="range"]` in theme/60-inputs.css). Two copies of a slider, one of them ours, was
* the thing to delete — not a third. */
/* THE HUE SLIDER'S rules were here — a rainbow track under a thumb carrying the axis's live
* colour. The slider is gone from the control (fs-widgets' colorControl): two controls for one
* value read as two settings, and the half that could not reach a grey was the slider. The axis
* still STORES a hue and 03-palettes.css still rotates by one, so a value saved before this goes
* on painting — but nothing draws a wheel any more, and CSS for a control nobody renders is dead
* CSS in our own namespace, which `npm run css-orphans` is there to keep out. */
}