Niveus/niveus/themes/light.css
Nexoniarz 369d1cd8f7 Add Glaze material, pickers, slider and design guidelines
Glaze is glass with a dial: --nv-glass-frost (0 clear .. 1 heavy) moves
backdrop blur, tint and saturation together, read where the surface is
drawn so any subtree or slider can change it. A masked rim and a specular
bloom follow the pointer via niveus.js; without it the light sits at its
default and the material still reads. Falls back to the elevated solid
under reduced transparency or without backdrop-filter.

New components: .nv-slider (binds to a token with data-nv-var),
.nv-calendar, .nv-timepicker (24-hour by default, optional 24H / AM/PM
switch, value always HH:MM), and .nv-picker with .nv-menu--listbox (search,
type-ahead, keys). All render without niveus.js except where noted.

GUIDELINES.md is the normative rulebook for people and AI agents.

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

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

190 lines
10 KiB
CSS

/* ==========================================================================
Niveus — Light appearance
--------------------------------------------------------------------------
Values for the light side of every semantic role. Nothing here is consumed
by components directly; tokens/color.css resolves these through light-dark().
Almost nothing below is a literal color. Each role is *derived* from
--nv-brand, so setting one value re-colours the entire system: the accent
and its states, what text goes on top of it, how the status colors lean, and
how much of the brand bleeds into the greys. See tokens/color.css for the
inputs.
Light Niveus reads as soft paper and enamel on a warm grey desk: surfaces
that hold content stay near-white, so documents never look yellowed; the
canvas around them is a few steps warmer and darker, so a window sits *on*
something. Raised controls are bevelled — a bright top edge, a soft shade
along the bottom, light falling across the face — and shadows are warm,
the color of shade on paper rather than of a grey filter.
========================================================================== */
:root {
/* Follow the environment unless the application states an appearance. */
color-scheme: light dark;
}
:root[data-theme="light"] {
color-scheme: light;
}
/* [data-nv-brand] re-derives the whole set for a subtree, so one region can
carry a different brand without a second theme. */
:root,
[data-nv-brand] {
/* --- Canvas and surfaces -----------------------------------------------
Paper stays paper: the two surfaces that hold content are left untinted so
text keeps its contrast. Everything the surfaces sit *in* picks up a trace
of the brand, which is what stops an accent from looking stuck on. */
--nv-light-background: color-mix(in oklab, #e8e5de, var(--nv-brand) var(--nv-brand-tint));
--nv-light-background-subtle: color-mix(in oklab, #eeece6, var(--nv-brand) var(--nv-brand-tint));
--nv-light-surface: #fbfbfa;
--nv-light-surface-elevated: #f8f8f6;
--nv-light-surface-inset: color-mix(in oklab, #eeede8, var(--nv-brand) var(--nv-brand-tint));
--nv-light-surface-hover: color-mix(in oklab, #f1f0ec, var(--nv-brand) var(--nv-brand-tint));
--nv-light-surface-active: color-mix(in oklab, #e6e5e0, var(--nv-brand) var(--nv-brand-tint));
--nv-light-scrim: rgb(28 24 18 / 0.34);
/* --- Text ---------------------------------------------------------------
Primary text stays neutral — it carries the reading contrast. The quieter
two lean towards the brand, where a tint reads as warmth rather than as a
loss of legibility. */
--nv-light-text-primary: var(--nv-palette-neutral-900);
--nv-light-text-secondary: color-mix(in oklab, var(--nv-palette-neutral-600), var(--nv-brand) 8%);
--nv-light-text-tertiary: color-mix(in oklab, var(--nv-palette-neutral-500), var(--nv-brand) 8%);
--nv-light-text-disabled: var(--nv-palette-neutral-400);
--nv-light-text-inverted: var(--nv-palette-neutral-0);
/* --- Borders ------------------------------------------------------------ */
--nv-light-border-subtle: rgb(28 24 18 / 0.07);
--nv-light-border: rgb(28 24 18 / 0.13);
--nv-light-border-strong: rgb(28 24 18 / 0.22);
/* --- Accent --------------------------------------------------------------
Every filled color is pushed away from the middle of the lightness range
before it is used. A mid-tone is the one place where neither white nor
black text reaches 4.5:1, so a brand that lands there is darkened until
white works — or, if it is already light, lightened until black does. The
brand's hue and chroma are untouched; only its lightness moves, and only
as far as it has to. */
--nv-light-accent: oklch(from var(--nv-brand) calc(
clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1) * min(l, var(--nv-contrast-dark)) +
(1 - clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1)) * max(l, var(--nv-contrast-light))
) clamp(0.02, c, 0.25) h);
--nv-light-accent-hover: oklch(from var(--nv-light-accent) calc(l - 0.045) c h);
--nv-light-accent-active: oklch(from var(--nv-light-accent) calc(l - 0.09) c h);
--nv-light-accent-subtle: oklch(from var(--nv-light-accent) 0.965 calc(c * 0.16) h);
--nv-light-accent-subtle-hover: oklch(from var(--nv-light-accent) 0.93 calc(c * 0.22) h);
--nv-light-accent-border: oklch(from var(--nv-light-accent) l c h / 0.32);
--nv-light-accent-text: oklch(from var(--nv-light-accent) clamp(0.3, calc(l - 0.12), 0.5) c h);
/* Text that lands on the color itself. The clamp is a step function: below
the lightness threshold it resolves to 1 and the text goes near-white,
above it to 0 and the text goes near-black — so a yellow brand gets dark
labels and a navy one gets light labels, with no second token to set. */
--nv-light-on-accent: oklch(from var(--nv-light-accent)
calc(0.14 + clamp(0, (0.68 - l) * 100, 1) * 0.84)
calc(c * 0.2) h);
/* --- Success ------------------------------------------------------------- */
--nv-light-success: oklch(from
color-mix(in oklab, var(--nv-palette-success-500), var(--nv-brand) var(--nv-brand-harmony))
calc(
clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1) * min(l, var(--nv-contrast-dark)) +
(1 - clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1)) * max(l, var(--nv-contrast-light))
) clamp(0.02, c, 0.25) h);
--nv-light-success-hover: oklch(from var(--nv-light-success) calc(l - 0.05) c h);
--nv-light-success-subtle: oklch(from var(--nv-light-success) 0.965 calc(c * 0.16) h);
--nv-light-success-border: oklch(from var(--nv-light-success) l c h / 0.3);
--nv-light-success-text: oklch(from var(--nv-light-success) clamp(0.3, calc(l - 0.1), 0.48) c h);
--nv-light-on-success: oklch(from var(--nv-light-success)
calc(0.14 + clamp(0, (0.68 - l) * 100, 1) * 0.84)
calc(c * 0.2) h);
/* --- Warning -------------------------------------------------------------- */
/* Amber starts above the pivot on purpose. Pushed the other way it lands as
brown, which reads as neither a warning nor a color anyone chose. */
--nv-light-warning: oklch(from
color-mix(in oklab, var(--nv-palette-warning-400), var(--nv-brand) var(--nv-brand-harmony))
calc(
clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1) * min(l, var(--nv-contrast-dark)) +
(1 - clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1)) * max(l, var(--nv-contrast-light))
) clamp(0.02, c, 0.25) h);
--nv-light-warning-hover: oklch(from var(--nv-light-warning) calc(l - 0.05) c h);
--nv-light-warning-subtle: oklch(from var(--nv-light-warning) 0.965 calc(c * 0.16) h);
--nv-light-warning-border: oklch(from var(--nv-light-warning) l c h / 0.32);
--nv-light-warning-text: oklch(from var(--nv-light-warning) clamp(0.3, calc(l - 0.1), 0.48) c h);
--nv-light-on-warning: oklch(from var(--nv-light-warning)
calc(0.14 + clamp(0, (0.68 - l) * 100, 1) * 0.84)
calc(c * 0.2) h);
/* --- Danger ---------------------------------------------------------------- */
--nv-light-danger: oklch(from
color-mix(in oklab, var(--nv-palette-danger-500), var(--nv-brand) var(--nv-brand-harmony))
calc(
clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1) * min(l, var(--nv-contrast-dark)) +
(1 - clamp(0, (var(--nv-contrast-pivot) - l) * 100, 1)) * max(l, var(--nv-contrast-light))
) clamp(0.02, c, 0.25) h);
--nv-light-danger-hover: oklch(from var(--nv-light-danger) calc(l - 0.05) c h);
--nv-light-danger-active: oklch(from var(--nv-light-danger) calc(l - 0.1) c h);
--nv-light-danger-subtle: oklch(from var(--nv-light-danger) 0.965 calc(c * 0.16) h);
--nv-light-danger-border: oklch(from var(--nv-light-danger) l c h / 0.3);
--nv-light-danger-text: oklch(from var(--nv-light-danger) clamp(0.3, calc(l - 0.1), 0.48) c h);
--nv-light-on-danger: oklch(from var(--nv-light-danger)
calc(0.14 + clamp(0, (0.68 - l) * 100, 1) * 0.84)
calc(c * 0.2) h);
/* --- Depth and material -------------------------------------------------
Three shadow colors build every elevation level: an ambient contact
shadow, a soft penumbra, and a deeper umbra for high elevations. */
--nv-light-shadow-ambient: rgb(40 32 20 / 0.08);
--nv-light-shadow-penumbra: rgb(40 32 20 / 0.1);
--nv-light-shadow-umbra: rgb(40 32 20 / 0.16);
--nv-light-shadow-inset: rgb(40 32 20 / 0.14);
--nv-light-highlight: rgb(255 255 255 / 0.9);
--nv-light-highlight-strong: rgb(255 255 255 / 1);
/* Paper already has a border doing the contact work, so the outline only
softens it; the bottom shade is faint for the same reason. */
--nv-light-lowlight: rgb(40 32 20 / 0.09);
--nv-light-outline: rgb(40 32 20 / 0.05);
--nv-light-sheen-top: rgb(255 255 255 / 0.9);
--nv-light-sheen-bottom: rgb(40 32 20 / 0.07);
--nv-light-sheen-strong-top: rgb(255 255 255 / 0.32);
--nv-light-sheen-strong-bottom: rgb(20 16 10 / 0.16);
--nv-light-glow: oklch(from var(--nv-light-accent) l c h / 0.3);
--nv-light-knob: var(--nv-palette-neutral-0);
--nv-light-knob-shade: rgb(40 32 20 / 0.13);
/* Light surfaces are already lighter than their canvas; the shadow alone
carries elevation, so there is nothing to add. */
--nv-light-elevation-tint-1: transparent;
--nv-light-elevation-tint-2: transparent;
--nv-light-elevation-tint-3: transparent;
--nv-light-elevation-tint-4: transparent;
--nv-light-elevation-tint-5: transparent;
--nv-light-material-frosted: rgb(252 252 250 / 0.56);
--nv-light-material-glass: rgb(252 252 250 / 0.4);
--nv-light-material-border: rgb(255 255 255 / 0.72);
/* Glaze: the tint it is made of, the rim that catches light on the side
facing the pointer, the rim on the far side, and the specular bloom. */
--nv-light-material-glaze: rgb(255 255 255 / 1);
--nv-light-glaze-rim-lit: rgb(255 255 255 / 1);
--nv-light-glaze-rim-far: rgb(255 255 255 / 0.5);
--nv-light-glaze-specular: rgb(255 255 255 / 0.6);
/* --- Window controls --------------------------------------------------------- */
--nv-light-window-close: var(--nv-palette-window-close);
--nv-light-window-minimize: var(--nv-palette-window-minimize);
--nv-light-window-zoom: var(--nv-palette-window-zoom);
--nv-light-window-inactive: var(--nv-palette-neutral-300);
--nv-light-window-control-ring: rgb(0 0 0 / 0.16);
--nv-light-window-glyph: rgb(0 0 0 / 0.55);
/* --- Interaction ----------------------------------------------------------- */
--nv-light-focus-ring: oklch(from var(--nv-light-accent) l c h / 0.4);
--nv-light-focus-ring-contrast: rgb(255 255 255 / 0.9);
--nv-light-selection: oklch(from var(--nv-light-accent) l c h / 0.2);
}