Niveus/niveus/base.css
Nexoniarz 65d81481bd Fix Firefox: wheels, dark shadows, theme glass, smooth jumps, tri-state
Found by driving Firefox 158 with real pointer, wheel and key input.

- Time picker wheels used padding to reach the last rows. Firefox leaves
  bottom padding out of the scrollable area, so the last rows were
  unreachable and the 24H/AM/PM wheel could not scroll at all. Spacer
  pseudo-elements replace the padding.
- Wheels can now be dragged with a mouse or pen, with inertia, a rubber-band
  at the ends and a spring back. Touch is left to the platform.
- Theme changes no longer use a page-wide view transition. The snapshot
  could not blur what sits behind frosted glass, so every frosted surface
  went clear for the length of the cross-fade.
- Dark shadows were invisible: black on a near-black canvas, with a deep
  negative spread tucking the blur under the element. The dark canvas is
  lifted, the shadows are stronger and the spreads milder.
- Same-page links now travel (scroll-behavior: smooth, motion permitting).
- data-nv-tristate: a checkbox that cycles unchecked, checked, partial; the
  browser can display a partial state but a click can never choose it.
- Button groups show their pressed segment.

GUIDELINES.md gains the cross-engine rules these defects taught, and makes
Firefox the first browser to verify in.

check.py, build.py and build.py --check pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 09:50:40 +01:00

250 lines
7.6 KiB
CSS

/* ==========================================================================
Niveus — Base
--------------------------------------------------------------------------
The minimum an application needs before any component renders: a predictable
box model, the canvas, the default type, and the system-wide focus and
selection treatment.
This layer styles elements, never classes with intent. Anything opinionated
enough to need a name belongs in components/.
========================================================================== */
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
body {
margin: 0;
min-height: 100svh;
background-color: var(--nv-color-background);
/* The canvas is lit, not painted: a wash of the accent falls in from the top
corner and a fainter one pools at the opposite foot. It is what gives
translucent surfaces something to be translucent against, and keeps a
page of solid panels from reading as one flat field. */
background-image:
radial-gradient(120% 80% at 0% 0%, color-mix(in oklab, var(--nv-color-accent) light-dark(10%, 16%), transparent), transparent 60%),
radial-gradient(90% 70% at 100% 100%, color-mix(in oklab, var(--nv-color-accent) light-dark(6%, 10%), transparent), transparent 60%);
background-attachment: fixed;
color: var(--nv-color-text-primary);
font: var(--nv-font-body);
letter-spacing: var(--nv-letter-spacing-normal);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-synthesis-weight: none;
/* The canvas glides into a new appearance with everything standing on it,
rather than cutting while the surfaces above it fade. */
transition: var(--nv-transition-colors);
}
/* --- Rhythm ---------------------------------------------------------------
Block elements carry no outer margin of their own; spacing between them is
the layout's decision, set with gap or the spacing tokens. */
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, pre {
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
font-weight: var(--nv-font-weight-semibold);
letter-spacing: var(--nv-letter-spacing-tight);
line-height: var(--nv-line-height-snug);
text-wrap: balance;
}
h1 { font-size: var(--nv-font-size-3xl); letter-spacing: var(--nv-letter-spacing-tighter); }
h2 { font-size: var(--nv-font-size-2xl); }
h3 { font-size: var(--nv-font-size-xl); }
h4 { font-size: var(--nv-font-size-lg); }
h5 { font-size: var(--nv-font-size-md); }
h6 { font-size: var(--nv-font-size-sm); }
p {
text-wrap: pretty;
}
small {
font-size: var(--nv-font-size-xs);
}
code, kbd, samp, pre {
font-family: var(--nv-font-family-mono);
font-size: var(--nv-font-size-sm);
}
/* --- Links ----------------------------------------------------------------- */
a {
color: var(--nv-color-accent-text);
text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
text-underline-offset: 0.18em;
transition: var(--nv-transition-colors);
}
a:hover {
text-decoration-color: currentColor;
}
/* --- Media ----------------------------------------------------------------- */
img, svg, video, canvas, audio, iframe, embed, object {
display: block;
max-width: 100%;
}
svg {
flex: none;
}
hr {
border: 0;
border-top: var(--nv-border-width) solid var(--nv-color-border-subtle);
margin: 0;
}
/* --- Forms -----------------------------------------------------------------
Form controls do not inherit type by default; without this every field in
the system would silently fall back to the browser's own font. */
button, input, optgroup, select, textarea {
font: inherit;
color: inherit;
margin: 0;
}
textarea {
resize: vertical;
}
:disabled {
cursor: not-allowed;
}
/* --- Focus -----------------------------------------------------------------
One focus treatment for the whole system. Components that need the ring to
follow their own shape override box-shadow, not outline-color. */
:focus-visible {
outline: var(--nv-focus-ring-width) solid var(--nv-color-focus-ring);
outline-offset: 2px;
border-radius: var(--nv-radius-xs);
}
/* A link to a place on the same page travels there instead of cutting. */
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}
/* Whatever the browser draws itself — a bare checkbox, a progress bar, the
text caret, a date field's glyphs — takes the brand, so no native colour
survives next to the system's own. */
:root {
accent-color: var(--nv-color-accent);
caret-color: var(--nv-color-accent);
}
::selection {
background-color: var(--nv-color-selection);
color: var(--nv-color-text-primary);
}
/* --- Scrollbars ------------------------------------------------------------ */
* {
scrollbar-color: var(--nv-color-border-strong) transparent;
scrollbar-width: thin;
}
/* ==========================================================================
Type roles
--------------------------------------------------------------------------
Named roles from tokens/typography.css, available as classes so hierarchy
stays consistent even when the markup cannot use a heading element.
========================================================================== */
.nv-display {
font: var(--nv-font-display);
letter-spacing: var(--nv-letter-spacing-tighter);
text-wrap: balance;
}
.nv-heading {
font: var(--nv-font-heading);
letter-spacing: var(--nv-letter-spacing-tight);
text-wrap: balance;
}
.nv-title {
font: var(--nv-font-title);
letter-spacing: var(--nv-letter-spacing-tight);
}
.nv-body {
font: var(--nv-font-body);
text-wrap: pretty;
}
.nv-label {
font: var(--nv-font-label);
}
.nv-caption {
font: var(--nv-font-caption);
color: var(--nv-color-text-secondary);
}
/* Engraved: small mono capitals that mark a region the way markings mark an
instrument panel. Not for anything that has to be read as a sentence. */
.nv-overline {
font: var(--nv-font-engraved);
letter-spacing: var(--nv-letter-spacing-engraved);
text-transform: uppercase;
color: var(--nv-color-text-tertiary);
}
.nv-code {
font: var(--nv-font-code);
}
/* Text color roles, for when the role is about emphasis rather than size. */
.nv-text-primary { color: var(--nv-color-text-primary); }
.nv-text-secondary { color: var(--nv-color-text-secondary); }
.nv-text-tertiary { color: var(--nv-color-text-tertiary); }
.nv-text-accent { color: var(--nv-color-accent-text); }
.nv-text-success { color: var(--nv-color-success-text); }
.nv-text-warning { color: var(--nv-color-warning-text); }
.nv-text-danger { color: var(--nv-color-danger-text); }
/* Lit text: type cut from the same material as the surfaces — bright along
the top, warming towards the accent below, the way light falls across a
raised face. Each line is lit on its own. For one display line on a page,
not for running text. */
.nv-text-lit {
background-image: linear-gradient(
180deg,
var(--nv-color-text-primary) 30%,
color-mix(in oklab, var(--nv-color-accent-text) 30%, var(--nv-color-text-primary))
);
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: var(--nv-color-accent-text);
}
/* The accent alone, lit from above: for an accented phrase inside a display
line, where flat accent text would read as a link. */
.nv-text-lit--accent {
background-image: linear-gradient(
180deg,
color-mix(in oklab, var(--nv-color-accent-text) 55%, var(--nv-color-text-primary)),
var(--nv-color-accent-text) 60%,
var(--nv-color-accent)
);
}