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

220 lines
14 KiB
CSS

@layer page {
/* The Appearance controls, appended to System -> System by fs-appearance.js.
*
* This was a popover on <body>, 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 — so the placement, the max-height clamp, the fade-in and the
* [hidden] pair a floating card needed are all gone.
*
* What is left in the theme's own namespace — the `.fs-ap-*` folds, sections, previews and
* version line below — lives in the page layer rather than the theme layer, because this is
* the only page that draws it: an axis control is not chrome. */
.fs-ap { display: flex; flex-direction: column; gap: var(--fs-space-5); }
/* The page note (fs-appearance.js). Only the outer margins: `.alert-message p` carries
* --fs-space-1 top and bottom, which at the box's own edges adds to the panel's padding and
* makes a two-sentence note look loose against every other alert on the theme. */
.fs-ap-note p:first-child { margin-top: 0; }
.fs-ap-note p:last-child { margin-bottom: 0; }
/* 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 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 pinned as @mirror chevron/* so the copies cannot drift.
*
* It turns on the SAME attribute that tells a screen reader the state, so the two cannot
* disagree. Rotated with `transform` rather than the individual `rotate` property, because that
* is what the overview's copy does and the pair must look identical mid-animation. */
.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. */
/* `hidden` is what keeps a closed group out of the tab order and the a11y tree, and
* base/10-reset.css is what makes the attribute win over a class's `display` — for every
* element, not just this one, so there is nothing to restate here. */
/* 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: var(--fs-hairline); }
/* The rows are stock `.cbi-value` 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 rules every other LuCI form is drawn by. What stood here 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 that grid is the change — do not restate a layout
* for these rows. */
/* 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); }
/* Save and the two resets differ by one word and sit in one row, which is the shape a
* destructive click lands on by accident. The gap says they are two groups; both resets also
* ask for a second click. Zero-width, so it costs no height when the row wraps on a phone. */
.fs-ap-actgap { flex: 0 0 var(--fs-space-4); }
.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: var(--fs-hairline); 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 in an ordinary
* `.cbi-value-field`. 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 — full width makes 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: var(--fs-hairline); 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; }
/* The JS hides a row that does not apply to the chosen wallpaper, the empty preview and a
* not-yet-uploaded Remove with `el.hidden`; base/10-reset.css makes that attribute beat the
* `display` a class sets, so none of it needs restating here any more. */
/* 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. (0,3,0) out-ranks the stock row's own display. Not named after Density: that 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: var(--fs-hairline);
}
.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 line that names the missing catalogue package. Same weight as the version beside it —
* it is a fact about this router, not a warning: nothing is broken, one package is absent.
* Wraps, because the package name is 24 characters and the column is narrow at 390px. */
.fs-ap-i18n {
flex-wrap: wrap;
font-size: var(--fs-type-xs);
color: var(--fs-dim);
}
/* the chip is base/20-typography.css's `code`; only the size follows the line it sits in, which
* is a step smaller than body text */
.fs-ap-i18n code { font-size: inherit; }
/* No rules here for a segmented control or a range slider of our own: the page draws its enums
* with `ui.Select` and its numbers with `ui.RangeSlider`, which this theme already styles for
* every other LuCI form. Two copies of a slider, one of them ours, was the thing to delete. */
/* No rules here for a hue slider either: the control is gone from fs-widgets' colorControl, two
* controls for one value reading 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 saved
* value goes on painting — but CSS for a control nobody renders is dead CSS in our own
* namespace, which `npm run css-orphans` exists to keep out. */
}