mirror of
https://github.com/Nexoniarz/Niveus.git
synced 2026-11-03 04:34:26 +00:00
Niveus: a tactile, adaptive interface language
Bevelled controls with light falling across them, fields as wells, solid windows, and glass kept for the chrome that floats over everything else. Warm graphite in dark, soft paper in light, one brand color driving both. Ships its own fonts (Instrument Sans, JetBrains Mono; OFL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
23b8dd376d
51 changed files with 15664 additions and 0 deletions
260
niveus/tokens/color.css
Normal file
260
niveus/tokens/color.css
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Color
|
||||
--------------------------------------------------------------------------
|
||||
Two levels of tokens live here:
|
||||
|
||||
1. Palette Raw, appearance-agnostic values. Never referenced by
|
||||
components. Prefixed --nv-palette-*.
|
||||
|
||||
2. Semantic The roles components actually consume (background, surface,
|
||||
text-primary, accent, danger, …). Each role resolves with
|
||||
light-dark(), pulling its value from the matching appearance
|
||||
file: themes/light.css (--nv-light-*) or themes/dark.css
|
||||
(--nv-dark-*).
|
||||
|
||||
The appearance is chosen by `color-scheme`, which themes/*.css bind to the
|
||||
[data-theme] attribute. A component therefore never knows — and must never
|
||||
ask — which appearance is active.
|
||||
========================================================================== */
|
||||
|
||||
/* ==========================================================================
|
||||
Brand
|
||||
--------------------------------------------------------------------------
|
||||
The whole color system has one input. Set --nv-brand and the accent, its
|
||||
hover and active states, the text that lands on it, the focus ring, the
|
||||
selection highlight, the tint in the greys and the lean of the status colors
|
||||
are all re-derived from it — in both appearances, with no second value to
|
||||
keep in sync.
|
||||
|
||||
:root { --nv-brand: #d92e3f; }
|
||||
|
||||
Set it on any element with [data-nv-brand] to re-brand a subtree.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--nv-brand: #4f6cff;
|
||||
|
||||
/* How much of the brand bleeds into the greys. Enough that the neutrals
|
||||
belong to the same family as the accent; not enough to read as colored.
|
||||
0% gives strictly neutral greys. */
|
||||
--nv-brand-tint: 4%;
|
||||
|
||||
/* How far the status colors lean towards the brand. Red still reads as
|
||||
danger and green still reads as success — they just stop looking borrowed
|
||||
from another system. 0% keeps them literal. */
|
||||
--nv-brand-harmony: 12%;
|
||||
|
||||
/* Where "too mid-toned to carry a label" begins, and how far either side a
|
||||
color is pushed to escape it. Raising the pivot sends more brands dark
|
||||
(white labels); lowering it sends more of them light (dark labels). */
|
||||
--nv-contrast-pivot: 0.7;
|
||||
--nv-contrast-dark: 0.53;
|
||||
--nv-contrast-light: 0.78;
|
||||
}
|
||||
|
||||
:root {
|
||||
/* --- Neutral ----------------------------------------------------------
|
||||
Warm graphite to warm paper. A grey that leans very slightly towards
|
||||
yellow reads as a material (metal, enamel, paper); a grey that leans blue
|
||||
reads as a screen. The lean is small enough that the brand tint still
|
||||
decides the family. */
|
||||
--nv-palette-neutral-0: #ffffff;
|
||||
--nv-palette-neutral-25: #fcfcfb;
|
||||
--nv-palette-neutral-50: #f8f8f6;
|
||||
--nv-palette-neutral-100: #f2f1ee;
|
||||
--nv-palette-neutral-150: #eae9e5;
|
||||
--nv-palette-neutral-200: #e1e0db;
|
||||
--nv-palette-neutral-300: #cfcec8;
|
||||
--nv-palette-neutral-400: #aeada7;
|
||||
--nv-palette-neutral-500: #87867f;
|
||||
--nv-palette-neutral-600: #63625d;
|
||||
--nv-palette-neutral-700: #4a4945;
|
||||
--nv-palette-neutral-800: #343337;
|
||||
--nv-palette-neutral-850: #2a2a2d;
|
||||
--nv-palette-neutral-900: #1d1d20;
|
||||
--nv-palette-neutral-925: #18181a;
|
||||
--nv-palette-neutral-950: #131315;
|
||||
--nv-palette-neutral-975: #0e0e10;
|
||||
--nv-palette-neutral-1000: #08080a;
|
||||
|
||||
/* --- Window controls ----------------------------------------------------
|
||||
The three lights at the start of a titlebar. They are the one place the
|
||||
system uses fixed colors instead of the brand: their meaning is learned
|
||||
from position and color together, and re-coloring them would unteach it. */
|
||||
--nv-palette-window-close: #ff5f57;
|
||||
--nv-palette-window-minimize: #febc2e;
|
||||
--nv-palette-window-zoom: #28c840;
|
||||
|
||||
/* --- Accent (indigo) --------------------------------------------------- */
|
||||
--nv-palette-accent-50: #eef2ff;
|
||||
--nv-palette-accent-100: #dfe6ff;
|
||||
--nv-palette-accent-200: #c4d0ff;
|
||||
--nv-palette-accent-300: #9fb3ff;
|
||||
--nv-palette-accent-400: #7691ff;
|
||||
--nv-palette-accent-500: #4f6cff;
|
||||
--nv-palette-accent-600: #3f5cf0;
|
||||
--nv-palette-accent-700: #3450d6;
|
||||
--nv-palette-accent-800: #2a3fa8;
|
||||
--nv-palette-accent-900: #22347f;
|
||||
|
||||
/* --- Success (green) --------------------------------------------------- */
|
||||
--nv-palette-success-50: #e8f9ef;
|
||||
--nv-palette-success-100: #ccf0dc;
|
||||
--nv-palette-success-200: #9ee2bc;
|
||||
--nv-palette-success-300: #62cd96;
|
||||
--nv-palette-success-400: #33b478;
|
||||
--nv-palette-success-500: #189a60;
|
||||
--nv-palette-success-600: #0f7d4d;
|
||||
--nv-palette-success-700: #0c6440;
|
||||
--nv-palette-success-800: #0a5034;
|
||||
--nv-palette-success-900: #08412b;
|
||||
|
||||
/* --- Warning (amber) --------------------------------------------------- */
|
||||
--nv-palette-warning-50: #fff6e6;
|
||||
--nv-palette-warning-100: #ffe9c2;
|
||||
--nv-palette-warning-200: #ffd68d;
|
||||
--nv-palette-warning-300: #fbbe52;
|
||||
--nv-palette-warning-400: #eda520;
|
||||
--nv-palette-warning-500: #cf8809;
|
||||
--nv-palette-warning-600: #a96d05;
|
||||
--nv-palette-warning-700: #855505;
|
||||
--nv-palette-warning-800: #6a4406;
|
||||
--nv-palette-warning-900: #563806;
|
||||
|
||||
/* --- Danger (red) ------------------------------------------------------ */
|
||||
--nv-palette-danger-50: #ffeff1;
|
||||
--nv-palette-danger-100: #ffdbde;
|
||||
--nv-palette-danger-200: #ffb9bf;
|
||||
--nv-palette-danger-300: #ff8b96;
|
||||
--nv-palette-danger-400: #f95b6c;
|
||||
--nv-palette-danger-500: #e2334a;
|
||||
--nv-palette-danger-600: #c01f36;
|
||||
--nv-palette-danger-700: #9c172b;
|
||||
--nv-palette-danger-800: #7e1524;
|
||||
--nv-palette-danger-900: #67141f;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Semantic roles
|
||||
========================================================================== */
|
||||
|
||||
/* The subtree selector has to be repeated here, not just on the theme files:
|
||||
var() inside a custom property is substituted where that property is
|
||||
*declared*, so a --nv-color-* declared only on :root would bake in the root
|
||||
brand and inherit the result unchanged. Re-declaring on [data-nv-brand]
|
||||
re-runs the substitution against the subtree's own brand. */
|
||||
:root,
|
||||
[data-nv-brand] {
|
||||
/* --- Canvas and surfaces ----------------------------------------------- */
|
||||
--nv-color-background: light-dark(var(--nv-light-background), var(--nv-dark-background));
|
||||
--nv-color-background-subtle: light-dark(var(--nv-light-background-subtle), var(--nv-dark-background-subtle));
|
||||
--nv-color-surface: light-dark(var(--nv-light-surface), var(--nv-dark-surface));
|
||||
--nv-color-surface-elevated: light-dark(var(--nv-light-surface-elevated), var(--nv-dark-surface-elevated));
|
||||
--nv-color-surface-inset: light-dark(var(--nv-light-surface-inset), var(--nv-dark-surface-inset));
|
||||
--nv-color-surface-hover: light-dark(var(--nv-light-surface-hover), var(--nv-dark-surface-hover));
|
||||
--nv-color-surface-active: light-dark(var(--nv-light-surface-active), var(--nv-dark-surface-active));
|
||||
--nv-color-scrim: light-dark(var(--nv-light-scrim), var(--nv-dark-scrim));
|
||||
|
||||
/* --- Text --------------------------------------------------------------- */
|
||||
--nv-color-text-primary: light-dark(var(--nv-light-text-primary), var(--nv-dark-text-primary));
|
||||
--nv-color-text-secondary: light-dark(var(--nv-light-text-secondary), var(--nv-dark-text-secondary));
|
||||
--nv-color-text-tertiary: light-dark(var(--nv-light-text-tertiary), var(--nv-dark-text-tertiary));
|
||||
--nv-color-text-disabled: light-dark(var(--nv-light-text-disabled), var(--nv-dark-text-disabled));
|
||||
--nv-color-text-inverted: light-dark(var(--nv-light-text-inverted), var(--nv-dark-text-inverted));
|
||||
|
||||
/* --- Borders ------------------------------------------------------------ */
|
||||
--nv-color-border-subtle: light-dark(var(--nv-light-border-subtle), var(--nv-dark-border-subtle));
|
||||
--nv-color-border: light-dark(var(--nv-light-border), var(--nv-dark-border));
|
||||
--nv-color-border-strong: light-dark(var(--nv-light-border-strong), var(--nv-dark-border-strong));
|
||||
|
||||
/* --- Accent ------------------------------------------------------------- */
|
||||
--nv-color-accent: light-dark(var(--nv-light-accent), var(--nv-dark-accent));
|
||||
--nv-color-accent-hover: light-dark(var(--nv-light-accent-hover), var(--nv-dark-accent-hover));
|
||||
--nv-color-accent-active: light-dark(var(--nv-light-accent-active), var(--nv-dark-accent-active));
|
||||
--nv-color-accent-subtle: light-dark(var(--nv-light-accent-subtle), var(--nv-dark-accent-subtle));
|
||||
--nv-color-accent-subtle-hover: light-dark(var(--nv-light-accent-subtle-hover), var(--nv-dark-accent-subtle-hover));
|
||||
--nv-color-accent-border: light-dark(var(--nv-light-accent-border), var(--nv-dark-accent-border));
|
||||
--nv-color-accent-text: light-dark(var(--nv-light-accent-text), var(--nv-dark-accent-text));
|
||||
--nv-color-on-accent: light-dark(var(--nv-light-on-accent), var(--nv-dark-on-accent));
|
||||
|
||||
/* --- Success ------------------------------------------------------------ */
|
||||
--nv-color-success: light-dark(var(--nv-light-success), var(--nv-dark-success));
|
||||
--nv-color-success-hover: light-dark(var(--nv-light-success-hover), var(--nv-dark-success-hover));
|
||||
--nv-color-success-subtle: light-dark(var(--nv-light-success-subtle), var(--nv-dark-success-subtle));
|
||||
--nv-color-success-border: light-dark(var(--nv-light-success-border), var(--nv-dark-success-border));
|
||||
--nv-color-success-text: light-dark(var(--nv-light-success-text), var(--nv-dark-success-text));
|
||||
--nv-color-on-success: light-dark(var(--nv-light-on-success), var(--nv-dark-on-success));
|
||||
|
||||
/* --- Warning ------------------------------------------------------------ */
|
||||
--nv-color-warning: light-dark(var(--nv-light-warning), var(--nv-dark-warning));
|
||||
--nv-color-warning-hover: light-dark(var(--nv-light-warning-hover), var(--nv-dark-warning-hover));
|
||||
--nv-color-warning-subtle: light-dark(var(--nv-light-warning-subtle), var(--nv-dark-warning-subtle));
|
||||
--nv-color-warning-border: light-dark(var(--nv-light-warning-border), var(--nv-dark-warning-border));
|
||||
--nv-color-warning-text: light-dark(var(--nv-light-warning-text), var(--nv-dark-warning-text));
|
||||
--nv-color-on-warning: light-dark(var(--nv-light-on-warning), var(--nv-dark-on-warning));
|
||||
|
||||
/* --- Danger ------------------------------------------------------------- */
|
||||
--nv-color-danger: light-dark(var(--nv-light-danger), var(--nv-dark-danger));
|
||||
--nv-color-danger-hover: light-dark(var(--nv-light-danger-hover), var(--nv-dark-danger-hover));
|
||||
--nv-color-danger-active: light-dark(var(--nv-light-danger-active), var(--nv-dark-danger-active));
|
||||
--nv-color-danger-subtle: light-dark(var(--nv-light-danger-subtle), var(--nv-dark-danger-subtle));
|
||||
--nv-color-danger-border: light-dark(var(--nv-light-danger-border), var(--nv-dark-danger-border));
|
||||
--nv-color-danger-text: light-dark(var(--nv-light-danger-text), var(--nv-dark-danger-text));
|
||||
--nv-color-on-danger: light-dark(var(--nv-light-on-danger), var(--nv-dark-on-danger));
|
||||
|
||||
/* --- Depth and material ------------------------------------------------- */
|
||||
--nv-color-shadow-ambient: light-dark(var(--nv-light-shadow-ambient), var(--nv-dark-shadow-ambient));
|
||||
--nv-color-shadow-penumbra: light-dark(var(--nv-light-shadow-penumbra), var(--nv-dark-shadow-penumbra));
|
||||
--nv-color-shadow-umbra: light-dark(var(--nv-light-shadow-umbra), var(--nv-dark-shadow-umbra));
|
||||
--nv-color-shadow-inset: light-dark(var(--nv-light-shadow-inset), var(--nv-dark-shadow-inset));
|
||||
--nv-color-highlight: light-dark(var(--nv-light-highlight), var(--nv-dark-highlight));
|
||||
--nv-color-highlight-strong: light-dark(var(--nv-light-highlight-strong), var(--nv-dark-highlight-strong));
|
||||
|
||||
/* The bevel's other half: the shade along a raised surface's bottom edge,
|
||||
and the contact line around anything that sits on another surface. Light
|
||||
along the top and shade along the bottom is what makes a control read as
|
||||
something pressable instead of a painted rectangle. */
|
||||
--nv-color-lowlight: light-dark(var(--nv-light-lowlight), var(--nv-dark-lowlight));
|
||||
--nv-color-outline: light-dark(var(--nv-light-outline), var(--nv-dark-outline));
|
||||
|
||||
/* Sheen: the light falling across a raised face, top to bottom. The plain
|
||||
pair is for neutral controls and surfaces; the strong pair is for filled
|
||||
color (primary buttons, checked marks), where it has to read through a
|
||||
saturated fill. Both are laid over a surface's own color, never instead
|
||||
of it. */
|
||||
--nv-color-sheen-top: light-dark(var(--nv-light-sheen-top), var(--nv-dark-sheen-top));
|
||||
--nv-color-sheen-bottom: light-dark(var(--nv-light-sheen-bottom), var(--nv-dark-sheen-bottom));
|
||||
--nv-color-sheen-strong-top: light-dark(var(--nv-light-sheen-strong-top), var(--nv-dark-sheen-strong-top));
|
||||
--nv-color-sheen-strong-bottom: light-dark(var(--nv-light-sheen-strong-bottom), var(--nv-dark-sheen-strong-bottom));
|
||||
|
||||
/* A knob: the thumb of a switch or a slider. It stays light in both
|
||||
appearances, the way a physical knob does not change color with the room. */
|
||||
--nv-color-knob: light-dark(var(--nv-light-knob), var(--nv-dark-knob));
|
||||
--nv-color-knob-shade: light-dark(var(--nv-light-knob-shade), var(--nv-dark-knob-shade));
|
||||
|
||||
/* Elevation tint: on a dark canvas a shadow has nothing to darken, so a
|
||||
raised surface also has to gain light. Transparent in light, where the
|
||||
shadow does all the work. */
|
||||
--nv-color-elevation-tint-1: light-dark(var(--nv-light-elevation-tint-1), var(--nv-dark-elevation-tint-1));
|
||||
--nv-color-elevation-tint-2: light-dark(var(--nv-light-elevation-tint-2), var(--nv-dark-elevation-tint-2));
|
||||
--nv-color-elevation-tint-3: light-dark(var(--nv-light-elevation-tint-3), var(--nv-dark-elevation-tint-3));
|
||||
--nv-color-elevation-tint-4: light-dark(var(--nv-light-elevation-tint-4), var(--nv-dark-elevation-tint-4));
|
||||
--nv-color-elevation-tint-5: light-dark(var(--nv-light-elevation-tint-5), var(--nv-dark-elevation-tint-5));
|
||||
|
||||
--nv-color-material-frosted: light-dark(var(--nv-light-material-frosted), var(--nv-dark-material-frosted));
|
||||
--nv-color-material-glass: light-dark(var(--nv-light-material-glass), var(--nv-dark-material-glass));
|
||||
--nv-color-material-border: light-dark(var(--nv-light-material-border), var(--nv-dark-material-border));
|
||||
|
||||
/* --- Window controls ------------------------------------------------------- */
|
||||
--nv-color-window-close: light-dark(var(--nv-light-window-close), var(--nv-dark-window-close));
|
||||
--nv-color-window-minimize: light-dark(var(--nv-light-window-minimize), var(--nv-dark-window-minimize));
|
||||
--nv-color-window-zoom: light-dark(var(--nv-light-window-zoom), var(--nv-dark-window-zoom));
|
||||
--nv-color-window-inactive: light-dark(var(--nv-light-window-inactive), var(--nv-dark-window-inactive));
|
||||
--nv-color-window-control-ring: light-dark(var(--nv-light-window-control-ring), var(--nv-dark-window-control-ring));
|
||||
--nv-color-window-glyph: light-dark(var(--nv-light-window-glyph), var(--nv-dark-window-glyph));
|
||||
|
||||
/* --- Interaction --------------------------------------------------------- */
|
||||
--nv-color-focus-ring: light-dark(var(--nv-light-focus-ring), var(--nv-dark-focus-ring));
|
||||
--nv-color-focus-ring-contrast: light-dark(var(--nv-light-focus-ring-contrast), var(--nv-dark-focus-ring-contrast));
|
||||
--nv-color-selection: light-dark(var(--nv-light-selection), var(--nv-dark-selection));
|
||||
}
|
||||
97
niveus/tokens/elevation.css
Normal file
97
niveus/tokens/elevation.css
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Elevation
|
||||
--------------------------------------------------------------------------
|
||||
Depth is a scale, not a free choice. Each level describes one relationship
|
||||
between a surface and what sits behind it:
|
||||
|
||||
0 flush painted on the canvas, no separation
|
||||
1 resting cards, list rows, static panels
|
||||
2 raised buttons, controls, anything that reacts to a pointer
|
||||
3 floating popovers, menus, dropdowns, tooltips
|
||||
4 lifted dialogs, sheets, detached panels
|
||||
5 overlay the topmost layer, used sparingly
|
||||
|
||||
Every level is built from the same three shadow colors so depth stays
|
||||
consistent between appearances: an ambient contact shadow that keeps the
|
||||
edge crisp, a soft penumbra for the spread, and an umbra that only appears
|
||||
once a surface is genuinely far from the canvas.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--nv-elevation-0: none;
|
||||
|
||||
--nv-elevation-1:
|
||||
0 1px 1px var(--nv-color-shadow-ambient),
|
||||
0 1px 3px -1px var(--nv-color-shadow-penumbra);
|
||||
|
||||
--nv-elevation-2:
|
||||
0 1px 1px var(--nv-color-shadow-ambient),
|
||||
0 2px 4px -1px var(--nv-color-shadow-penumbra),
|
||||
0 4px 8px -4px var(--nv-color-shadow-umbra);
|
||||
|
||||
--nv-elevation-3:
|
||||
0 1px 1px var(--nv-color-shadow-ambient),
|
||||
0 4px 8px -2px var(--nv-color-shadow-penumbra),
|
||||
0 10px 20px -6px var(--nv-color-shadow-umbra);
|
||||
|
||||
--nv-elevation-4:
|
||||
0 1px 2px var(--nv-color-shadow-ambient),
|
||||
0 8px 16px -4px var(--nv-color-shadow-penumbra),
|
||||
0 20px 40px -10px var(--nv-color-shadow-umbra);
|
||||
|
||||
--nv-elevation-5:
|
||||
0 1px 2px var(--nv-color-shadow-ambient),
|
||||
0 14px 28px -6px var(--nv-color-shadow-penumbra),
|
||||
0 32px 64px -14px var(--nv-color-shadow-umbra);
|
||||
|
||||
/* --- Inset ---------------------------------------------------------------
|
||||
Recessed surfaces: fields, wells, tracks, pressed controls. */
|
||||
--nv-elevation-inset-sm: inset 0 1px 2px var(--nv-color-shadow-inset);
|
||||
--nv-elevation-inset-md:
|
||||
inset 0 1px 1px var(--nv-color-shadow-inset),
|
||||
inset 0 2px 5px -1px var(--nv-color-shadow-inset);
|
||||
--nv-elevation-inset-lg:
|
||||
inset 0 2px 3px var(--nv-color-shadow-inset),
|
||||
inset 0 6px 12px -3px var(--nv-color-shadow-inset);
|
||||
|
||||
/* --- Highlight -----------------------------------------------------------
|
||||
The thin catch of light along the top edge of a raised surface. This is
|
||||
what makes Niveus read as material rather than as a rectangle with a
|
||||
shadow. Always paired with an elevation, never used alone. */
|
||||
--nv-highlight-top: inset 0 1px 0 var(--nv-color-highlight);
|
||||
--nv-highlight-top-strong: inset 0 1px 0 var(--nv-color-highlight-strong);
|
||||
--nv-highlight-edge:
|
||||
inset 0 1px 0 var(--nv-color-highlight),
|
||||
inset 0 0 0 1px var(--nv-color-border-subtle);
|
||||
|
||||
/* --- Bevel -----------------------------------------------------------------
|
||||
The highlight's other half. A raised face is lit along its top edge and
|
||||
shaded along its bottom one; together they are the bevel, and the bevel is
|
||||
what makes a Niveus control look like something a finger could press.
|
||||
Raised controls and surfaces take the full bevel; the highlight alone is
|
||||
for things that sit flush. */
|
||||
--nv-lowlight-bottom: inset 0 -1px 0 var(--nv-color-lowlight);
|
||||
--nv-bevel:
|
||||
inset 0 1px 0 var(--nv-color-highlight),
|
||||
inset 0 -1px 0 var(--nv-color-lowlight);
|
||||
|
||||
/* --- Outline ---------------------------------------------------------------
|
||||
The contact line: a hairline just outside a raised surface, where it meets
|
||||
whatever it rests on. In dark it is near-black and does most of the
|
||||
separating; in light it only softens the border beside it. */
|
||||
--nv-outline: 0 0 0 1px var(--nv-color-outline);
|
||||
|
||||
/* --- Ledge -----------------------------------------------------------------
|
||||
A recess catches light on its *lower* lip, the edge facing the viewer.
|
||||
Paired with an inset shadow, it turns a field from a dark rectangle into
|
||||
a well cut into the surface. */
|
||||
--nv-ledge: 0 1px 0 var(--nv-color-highlight);
|
||||
|
||||
/* --- Focus ---------------------------------------------------------------
|
||||
A contrast ring sits under the accent ring so focus stays visible on both
|
||||
light and dark surfaces, and on accent-filled controls. */
|
||||
--nv-focus-ring-width: 3px;
|
||||
--nv-focus-ring:
|
||||
0 0 0 1px var(--nv-color-focus-ring-contrast),
|
||||
0 0 0 calc(1px + var(--nv-focus-ring-width)) var(--nv-color-focus-ring);
|
||||
}
|
||||
97
niveus/tokens/materials.css
Normal file
97
niveus/tokens/materials.css
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Materials
|
||||
--------------------------------------------------------------------------
|
||||
A material is a surface treatment: what it is made of, not where it sits.
|
||||
Elevation answers "how far from the canvas"; material answers "what does
|
||||
light do when it hits this".
|
||||
|
||||
solid opaque, stable. The default. Content lives here.
|
||||
elevated solid with depth and a top highlight. Separated from context.
|
||||
inset recessed into its parent. Fields, wells, tracks.
|
||||
frosted translucent, moderately blurred, bevelled. Chrome that sits
|
||||
over content: bars, docks, launchers, notifications, menus.
|
||||
glass more translucent, lightly blurred. Contextual layers where the
|
||||
content behind should stay legible: overlays, hover cards.
|
||||
|
||||
Niveus is a solid system with glass used sparingly. Windows, cards, dialogs
|
||||
and everything that holds content are solid; translucency is reserved for
|
||||
the chrome that floats over them. Translucency has a cost — it tints
|
||||
whatever is behind it and forces the GPU to composite — and used everywhere
|
||||
it stops meaning "this floats".
|
||||
|
||||
Sheen is the light falling across a raised face. It is laid over a
|
||||
surface's own color as background-image, so a surface keeps its role color
|
||||
and gains the gradient; anything that does not draw gradients simply shows
|
||||
the role color.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/* --- Blur ---------------------------------------------------------------- */
|
||||
--nv-blur-sm: 8px;
|
||||
--nv-blur-md: 16px;
|
||||
--nv-blur-lg: 28px;
|
||||
--nv-blur-xl: 44px;
|
||||
|
||||
/* Backdrops lose saturation when blurred; a touch of saturate() puts the
|
||||
color back so frosted surfaces do not read as gray films. Moderate on
|
||||
purpose: the blur says "this floats", it is not the look itself. */
|
||||
--nv-backdrop-saturate: 160%;
|
||||
--nv-backdrop-frosted: blur(var(--nv-blur-md)) saturate(var(--nv-backdrop-saturate));
|
||||
--nv-backdrop-glass: blur(var(--nv-blur-sm)) saturate(var(--nv-backdrop-saturate));
|
||||
|
||||
/* --- Sheen ------------------------------------------------------------------
|
||||
raised neutral controls: buttons, the selected tab, a titlebar
|
||||
filled saturated fills: primary and danger buttons, checked marks
|
||||
surface large surfaces: windows, dialogs, cards. The light fades out
|
||||
within the first stretch, so it reads as a lit top edge
|
||||
rather than as a gradient over the content.
|
||||
knob a round thumb: lit from above, shaded towards its rim */
|
||||
--nv-sheen-raised: linear-gradient(var(--nv-color-sheen-top), var(--nv-color-sheen-bottom));
|
||||
--nv-sheen-filled: linear-gradient(var(--nv-color-sheen-strong-top), var(--nv-color-sheen-strong-bottom));
|
||||
--nv-sheen-surface: linear-gradient(var(--nv-color-sheen-top), transparent 6rem);
|
||||
--nv-sheen-knob: radial-gradient(circle at 50% 30%, transparent 35%, var(--nv-color-knob-shade));
|
||||
--nv-backdrop-scrim: blur(var(--nv-blur-sm)) saturate(120%);
|
||||
|
||||
/* --- Material recipes -----------------------------------------------------
|
||||
Each recipe is the full set of properties that defines the material.
|
||||
Applied by the .nv-material-* classes in components/surface.css. */
|
||||
--nv-material-solid-bg: var(--nv-color-surface);
|
||||
--nv-material-solid-border: var(--nv-color-border);
|
||||
--nv-material-solid-shadow: var(--nv-elevation-0);
|
||||
|
||||
--nv-material-elevated-bg: var(--nv-color-surface-elevated);
|
||||
--nv-material-elevated-border: var(--nv-color-border-subtle);
|
||||
--nv-material-elevated-shadow: var(--nv-bevel), var(--nv-outline), var(--nv-elevation-2);
|
||||
|
||||
--nv-material-inset-bg: var(--nv-color-surface-inset);
|
||||
--nv-material-inset-border: var(--nv-color-border-subtle);
|
||||
--nv-material-inset-shadow: var(--nv-elevation-inset-md), var(--nv-ledge);
|
||||
|
||||
--nv-material-frosted-bg: var(--nv-color-material-frosted);
|
||||
--nv-material-frosted-border: var(--nv-color-material-border);
|
||||
--nv-material-frosted-shadow: var(--nv-bevel), var(--nv-outline), var(--nv-elevation-3);
|
||||
|
||||
--nv-material-glass-bg: var(--nv-color-material-glass);
|
||||
--nv-material-glass-border: var(--nv-color-material-border);
|
||||
--nv-material-glass-shadow: var(--nv-highlight-top-strong), var(--nv-outline), var(--nv-elevation-4);
|
||||
}
|
||||
|
||||
/* Where translucency cannot be honoured — reduced transparency, or a browser
|
||||
without backdrop-filter — materials fall back to their solid equivalents
|
||||
rather than becoming unreadable washes. */
|
||||
@media (prefers-reduced-transparency: reduce) {
|
||||
:root {
|
||||
--nv-material-frosted-bg: var(--nv-color-surface-elevated);
|
||||
--nv-material-glass-bg: var(--nv-color-surface-elevated);
|
||||
--nv-backdrop-frosted: none;
|
||||
--nv-backdrop-glass: none;
|
||||
--nv-backdrop-scrim: none;
|
||||
}
|
||||
}
|
||||
|
||||
@supports not (backdrop-filter: blur(1px)) {
|
||||
:root {
|
||||
--nv-material-frosted-bg: var(--nv-color-surface-elevated);
|
||||
--nv-material-glass-bg: var(--nv-color-surface-elevated);
|
||||
}
|
||||
}
|
||||
107
niveus/tokens/motion.css
Normal file
107
niveus/tokens/motion.css
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Motion
|
||||
--------------------------------------------------------------------------
|
||||
Motion explains state and spatial relationship. It is short enough to feel
|
||||
immediate and eased enough to feel physical.
|
||||
|
||||
Rule of thumb: the smaller the element, the faster it moves. A button's
|
||||
hover is 120ms; a dialog's entrance is 240ms.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/* --- Duration ------------------------------------------------------------ */
|
||||
--nv-duration-instant: 0ms;
|
||||
--nv-duration-fast: 120ms; /* hover, press, color change */
|
||||
--nv-duration-quick: 160ms; /* small elements entering */
|
||||
--nv-duration-normal: 220ms; /* popovers, menus, expansion */
|
||||
--nv-duration-slow: 320ms; /* dialogs, sheets, large surfaces */
|
||||
--nv-duration-slower: 480ms; /* full-screen transitions */
|
||||
|
||||
/* --- Easing ---------------------------------------------------------------
|
||||
`standard` covers almost everything. Entrance decelerates into place, exit
|
||||
accelerates away, and `spring` is reserved for confirmations — never for
|
||||
anything a user triggers repeatedly. */
|
||||
--nv-ease-standard: cubic-bezier(0.2, 0.05, 0, 1);
|
||||
--nv-ease-entrance: cubic-bezier(0.05, 0.7, 0.1, 1);
|
||||
--nv-ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15);
|
||||
--nv-ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
|
||||
--nv-ease-linear: linear;
|
||||
|
||||
/* --- Continuity -------------------------------------------------------------
|
||||
Durations for changes the user is meant to *follow*, not merely notice.
|
||||
They are longer than an interaction response because the eye has further
|
||||
to travel: emphasis crossing a navigation bar, a panel growing open, a
|
||||
whole surface changing appearance. */
|
||||
--nv-duration-travel: 260ms; /* emphasis moving from one element to another */
|
||||
--nv-duration-expand: 280ms; /* content growing out of, or back into, place */
|
||||
--nv-duration-theme: 400ms; /* the entire surface changing appearance */
|
||||
|
||||
/* Leaves quickly, lands softly. The right curve for something travelling a
|
||||
distance the user is meant to follow with their eye. */
|
||||
--nv-ease-emphasized: cubic-bezier(0.32, 0.04, 0.06, 1);
|
||||
|
||||
/* --- Composites ------------------------------------------------------------
|
||||
`translate` and `scale` are listed rather than `transform`, because the
|
||||
components animate the individual properties — a transition on `transform`
|
||||
does not cover them, and the movement snaps. */
|
||||
--nv-transition-colors:
|
||||
color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
background-color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
border-color var(--nv-duration-fast) var(--nv-ease-standard);
|
||||
--nv-transition-depth:
|
||||
box-shadow var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
translate var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
scale var(--nv-duration-fast) var(--nv-ease-standard);
|
||||
--nv-transition-control:
|
||||
color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
background-color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
border-color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
box-shadow var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
translate var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
scale var(--nv-duration-fast) var(--nv-ease-standard);
|
||||
|
||||
/* Containers: the same as depth, plus the colors, so a surface glides into
|
||||
its new appearance when the theme changes instead of snapping. */
|
||||
--nv-transition-surface:
|
||||
color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
background-color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
border-color var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
box-shadow var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
translate var(--nv-duration-fast) var(--nv-ease-standard),
|
||||
scale var(--nv-duration-fast) var(--nv-ease-standard);
|
||||
|
||||
--nv-transition-expand:
|
||||
block-size var(--nv-duration-expand) var(--nv-ease-standard),
|
||||
grid-template-rows var(--nv-duration-expand) var(--nv-ease-standard),
|
||||
opacity var(--nv-duration-expand) var(--nv-ease-standard);
|
||||
|
||||
/* --- Displacement ----------------------------------------------------------
|
||||
How far things move. Presses sink by 1px; surfaces enter from 8px away. */
|
||||
--nv-lift-hover: -1px;
|
||||
--nv-press-depth: 1px;
|
||||
--nv-travel-sm: 4px;
|
||||
--nv-travel-md: 8px;
|
||||
--nv-travel-lg: 16px;
|
||||
}
|
||||
|
||||
/* Reduced motion is a system-wide contract, not a per-component opt-in: cut
|
||||
duration to near-zero and neutralise displacement, but keep transitions
|
||||
alive so state changes still resolve rather than snapping mid-flight. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root {
|
||||
--nv-duration-fast: 1ms;
|
||||
--nv-duration-quick: 1ms;
|
||||
--nv-duration-normal: 1ms;
|
||||
--nv-duration-slow: 1ms;
|
||||
--nv-duration-slower: 1ms;
|
||||
--nv-duration-travel: 1ms;
|
||||
--nv-duration-expand: 1ms;
|
||||
--nv-duration-theme: 1ms;
|
||||
--nv-ease-spring: var(--nv-ease-standard);
|
||||
--nv-lift-hover: 0px;
|
||||
--nv-press-depth: 0px;
|
||||
--nv-travel-sm: 0px;
|
||||
--nv-travel-md: 0px;
|
||||
--nv-travel-lg: 0px;
|
||||
}
|
||||
}
|
||||
24
niveus/tokens/radius.css
Normal file
24
niveus/tokens/radius.css
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Radius
|
||||
--------------------------------------------------------------------------
|
||||
Corner softness carries as much of the Niveus character as shadow does.
|
||||
Radii grow with the surface: controls stay modest and machined, containers
|
||||
get noticeably rounder — a window or a popover is a soft object, a button
|
||||
is a key — and only pills, knobs and avatars go fully round.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--nv-radius-none: 0;
|
||||
--nv-radius-xs: 0.25rem; /* 4px — tags, swatches, nested chips */
|
||||
--nv-radius-sm: 0.5rem; /* 8px — small controls, menu items */
|
||||
--nv-radius-md: 0.625rem; /* 10px — buttons, inputs, default control radius */
|
||||
--nv-radius-lg: 0.875rem; /* 14px — cards, panels, menus */
|
||||
--nv-radius-xl: 1.25rem; /* 20px — windows, dialogs, sheets, popovers */
|
||||
--nv-radius-2xl: 1.5rem; /* 24px — docks, hero surfaces */
|
||||
--nv-radius-full: 999rem; /* — pills, avatars, switches */
|
||||
|
||||
/* Nested radius: an inner surface inset by N should be N smaller than its
|
||||
parent, or the corners read as off-center. */
|
||||
--nv-radius-nested-md: calc(var(--nv-radius-lg) - var(--nv-space-2));
|
||||
--nv-radius-nested-lg: calc(var(--nv-radius-xl) - var(--nv-space-3));
|
||||
}
|
||||
54
niveus/tokens/spacing.css
Normal file
54
niveus/tokens/spacing.css
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Spacing and size
|
||||
--------------------------------------------------------------------------
|
||||
A 4px rhythm. The numeric suffix is the value in quarter-steps, so
|
||||
--nv-space-4 is 16px and --nv-space-6 is 24px. Anything not on the scale is
|
||||
application layout, not design-system spacing.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--nv-space-0: 0;
|
||||
--nv-space-px: 1px;
|
||||
--nv-space-0-5: 0.125rem; /* 2px */
|
||||
--nv-space-1: 0.25rem; /* 4px */
|
||||
--nv-space-1-5: 0.375rem; /* 6px */
|
||||
--nv-space-2: 0.5rem; /* 8px */
|
||||
--nv-space-2-5: 0.625rem; /* 10px */
|
||||
--nv-space-3: 0.75rem; /* 12px */
|
||||
--nv-space-4: 1rem; /* 16px */
|
||||
--nv-space-5: 1.25rem; /* 20px */
|
||||
--nv-space-6: 1.5rem; /* 24px */
|
||||
--nv-space-7: 1.75rem; /* 28px */
|
||||
--nv-space-8: 2rem; /* 32px */
|
||||
--nv-space-10: 2.5rem; /* 40px */
|
||||
--nv-space-12: 3rem; /* 48px */
|
||||
--nv-space-14: 3.5rem; /* 56px */
|
||||
--nv-space-16: 4rem; /* 64px */
|
||||
--nv-space-20: 5rem; /* 80px */
|
||||
--nv-space-24: 6rem; /* 96px */
|
||||
--nv-space-32: 8rem; /* 128px */
|
||||
|
||||
/* --- Control sizing -------------------------------------------------------
|
||||
Shared by every interactive control so a button, an input and a select
|
||||
line up on the same row without per-component tuning. */
|
||||
--nv-control-height-sm: 1.75rem; /* 28px */
|
||||
--nv-control-height-md: 2.25rem; /* 36px */
|
||||
--nv-control-height-lg: 2.75rem; /* 44px */
|
||||
|
||||
--nv-control-padding-sm: var(--nv-space-2-5);
|
||||
--nv-control-padding-md: var(--nv-space-3);
|
||||
--nv-control-padding-lg: var(--nv-space-4);
|
||||
|
||||
--nv-control-gap-sm: var(--nv-space-1-5);
|
||||
--nv-control-gap-md: var(--nv-space-2);
|
||||
--nv-control-gap-lg: var(--nv-space-2-5);
|
||||
|
||||
/* Minimum pointer target, independent of visual control height. */
|
||||
--nv-target-min: 2.75rem; /* 44px */
|
||||
|
||||
/* --- Layout ---------------------------------------------------------------- */
|
||||
--nv-layout-gutter: var(--nv-space-6);
|
||||
--nv-layout-max-width: 72rem;
|
||||
--nv-border-width: 1px;
|
||||
--nv-border-width-strong: 2px;
|
||||
}
|
||||
82
niveus/tokens/typography.css
Normal file
82
niveus/tokens/typography.css
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Typography
|
||||
--------------------------------------------------------------------------
|
||||
One type scale, used everywhere. Sizes are in rem so they respect the user's
|
||||
browser font size; line heights are unitless so they inherit correctly.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/* --- Families ----------------------------------------------------------- */
|
||||
/* Both ship with Niveus (fonts.css); the rest of each stack only covers the
|
||||
moment before they load, or a context that cannot load them. */
|
||||
--nv-font-family-sans:
|
||||
"Instrument Sans", "Inter var", "Inter", -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
||||
--nv-font-family-mono:
|
||||
"JetBrains Mono", "SF Mono", "IBM Plex Mono", ui-monospace, "Menlo",
|
||||
"Consolas", monospace;
|
||||
|
||||
/* --- Size scale ---------------------------------------------------------- */
|
||||
--nv-font-size-2xs: 0.6875rem; /* 11px — dense metadata, never body text */
|
||||
--nv-font-size-xs: 0.75rem; /* 12px */
|
||||
--nv-font-size-sm: 0.8125rem; /* 13px */
|
||||
--nv-font-size-md: 0.9375rem; /* 15px — default body size */
|
||||
--nv-font-size-lg: 1.0625rem; /* 17px */
|
||||
--nv-font-size-xl: 1.25rem; /* 20px */
|
||||
--nv-font-size-2xl: 1.5rem; /* 24px */
|
||||
--nv-font-size-3xl: 1.875rem; /* 30px */
|
||||
--nv-font-size-4xl: 2.375rem; /* 38px */
|
||||
--nv-font-size-5xl: 3rem; /* 48px */
|
||||
|
||||
/* --- Weight -------------------------------------------------------------- */
|
||||
--nv-font-weight-regular: 400;
|
||||
--nv-font-weight-medium: 500;
|
||||
--nv-font-weight-semibold: 600;
|
||||
--nv-font-weight-bold: 700;
|
||||
|
||||
/* --- Line height --------------------------------------------------------- */
|
||||
--nv-line-height-tight: 1.18; /* display and large headings */
|
||||
--nv-line-height-snug: 1.32; /* headings, single-line controls */
|
||||
--nv-line-height-normal: 1.5; /* body copy */
|
||||
--nv-line-height-relaxed: 1.68; /* long-form reading */
|
||||
|
||||
/* --- Letter spacing -------------------------------------------------------
|
||||
Large text needs negative tracking to stay optically even; small caps-ish
|
||||
labels need positive tracking to stay readable. */
|
||||
--nv-letter-spacing-tighter: -0.022em;
|
||||
--nv-letter-spacing-tight: -0.012em;
|
||||
--nv-letter-spacing-normal: 0em;
|
||||
--nv-letter-spacing-wide: 0.02em;
|
||||
--nv-letter-spacing-wider: 0.06em;
|
||||
/* Engraved labels: small mono capitals, spaced like markings on a panel. */
|
||||
--nv-letter-spacing-engraved: 0.1em;
|
||||
|
||||
/* --- Composite roles ------------------------------------------------------
|
||||
Applied as classes in base.css (.nv-display, .nv-heading, …). Kept as
|
||||
tokens so applications can compose their own elements from the same
|
||||
values instead of guessing. */
|
||||
--nv-font-display: var(--nv-font-weight-semibold) var(--nv-font-size-4xl) /
|
||||
var(--nv-line-height-tight) var(--nv-font-family-sans);
|
||||
--nv-font-heading: var(--nv-font-weight-semibold) var(--nv-font-size-2xl) /
|
||||
var(--nv-line-height-snug) var(--nv-font-family-sans);
|
||||
--nv-font-title: var(--nv-font-weight-semibold) var(--nv-font-size-lg) /
|
||||
var(--nv-line-height-snug) var(--nv-font-family-sans);
|
||||
--nv-font-body: var(--nv-font-weight-regular) var(--nv-font-size-md) /
|
||||
var(--nv-line-height-normal) var(--nv-font-family-sans);
|
||||
--nv-font-label: var(--nv-font-weight-medium) var(--nv-font-size-sm) /
|
||||
var(--nv-line-height-snug) var(--nv-font-family-sans);
|
||||
--nv-font-caption: var(--nv-font-weight-regular) var(--nv-font-size-xs) /
|
||||
var(--nv-line-height-normal) var(--nv-font-family-sans);
|
||||
--nv-font-code: var(--nv-font-weight-regular) var(--nv-font-size-sm) /
|
||||
var(--nv-line-height-normal) var(--nv-font-family-mono);
|
||||
/* The engraved label: section markers, the clock, anything that names a
|
||||
region rather than being read. Paired with uppercase and the engraved
|
||||
tracking (see .nv-overline in base.css). */
|
||||
--nv-font-engraved: var(--nv-font-weight-medium) var(--nv-font-size-2xs) /
|
||||
var(--nv-line-height-snug) var(--nv-font-family-mono);
|
||||
|
||||
/* --- Measure --------------------------------------------------------------- */
|
||||
--nv-measure-narrow: 44ch;
|
||||
--nv-measure: 68ch;
|
||||
--nv-measure-wide: 82ch;
|
||||
}
|
||||
Loading…
Reference in a new issue