site: rebuilt on Niveus, and the palette stops being a copy
Niveus is the interface language the project uses from here on -- this site
first, applications after it. Rebuilding the site on it is what proves the
system is one, rather than a folder of CSS with a README.
**`style.css` is gone.** 21 KB that transcribed include/theme.h by hand:
palette, type scale, shadows, spacing, every component. The intent was right --
"a site describing an operating system should look like the operating system"
-- and the mechanism was a copy, which means two files that have to be kept in
step by remembering to. What replaced it is `app.css`, which is layout and
nothing else, and one declaration in each page:
:root { --nv-brand: #7C5CFF; }
That is SCH_ACCENT. Niveus derives the accent and its states, the link colour,
the focus ring, the tint in the greys and how far the status colours lean, for
light and dark both. One number to keep true instead of forty.
**The site is now adaptive.** Dark by default, because the OS is dark, with a
Light/Dark/Auto group in the bar that is real. The choice is kept in
localStorage and re-applied by an inline script before first paint, so the page
never renders in one appearance and corrects itself while you watch.
**PSOGM keeps its one purple, and now says so structurally.** #4B1D9A is
spelled the way Niveus spells a subtree with its own colour -- `data-nv-brand`
with its own `--nv-brand` on that section -- so every role inside re-derives
from it rather than being painted on. Still exactly one place, which is what
branding/README.md asks for.
Content is unchanged; what changed is what it is made of. Cards are `.nv-card`,
tags are `.nv-badge`, the support table is `.nv-table`, the crash section is a
`.nv-bed`, and the docs reader's two rails are `.nv-nav--vertical` with the
current document marked by `aria-current` rather than a class of our own.
`docs.js` emits `.nv-table` and `.nv-code`, so a change to the system reaches
the documentation too.
Three things were found by looking at it in a browser rather than by reading
it, and all three are the same mistake: **a `.nv-card` carries no padding of
its own -- its parts do.** The stat tiles, the "One ISO, one stick" band and
the card headers were all built from bare children, so numbers ran to the card
edge, the USB mark hung off it, and a title and its badge sat at opposite ends
of a row meant for a title and a trailing slot. Where the component had a right
way to say it, the markup now says it that way; where it did not -- a tile has
no parts -- `app.css` supplies the padding and says why.
Checked: every nv- class used across the three pages and docs.js exists in the
CSS, every token referenced by app.css is defined, and app.css contains no hex
value at all. Niveus's own tools/check.py passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9ab8a7a426
commit
6490e5c759
89 changed files with 20804 additions and 1363 deletions
209
docs/Website/niveus/niveus/continuity.css
Normal file
209
docs/Website/niveus/niveus/continuity.css
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/* ==========================================================================
|
||||
Niveus — Continuity
|
||||
--------------------------------------------------------------------------
|
||||
Every interaction should read as a continuation of the previous state, not
|
||||
as a replacement of it. When something moves, grows, collapses or changes
|
||||
appearance, the change itself should be perceptible: where it came from,
|
||||
where it went, and why.
|
||||
|
||||
This layer holds the cross-cutting machinery that individual components
|
||||
then opt into:
|
||||
|
||||
· one duration and easing for everything the browser animates between
|
||||
two states via a view transition
|
||||
· emphasis that travels between elements instead of disappearing from one
|
||||
and reappearing on another (--nv-selection-name)
|
||||
· direction, so moving forward and moving back do not look identical
|
||||
· appearance changes that cross-fade rather than cut
|
||||
|
||||
The goal is not to animate everything. It is that nothing pops into a new
|
||||
state the user could have watched it reach.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/* Lets block-size: auto participate in transitions, which is what allows
|
||||
content to expand from where it already is instead of appearing at full
|
||||
height. Without it, every disclosure in the system would jump. */
|
||||
interpolate-size: allow-keywords;
|
||||
|
||||
/* The default name for travelling emphasis. A page with more than one
|
||||
selection group must give each group its own name — two visible elements
|
||||
sharing a view-transition-name abort the transition entirely. */
|
||||
--nv-selection-name: nv-selection;
|
||||
}
|
||||
|
||||
/* --- View transitions -------------------------------------------------------
|
||||
Anything the application wraps in document.startViewTransition() inherits
|
||||
the system's travel duration and easing, so a state change animates like
|
||||
the rest of Niveus without the application choosing timings. */
|
||||
|
||||
::view-transition-group(*) {
|
||||
animation-duration: var(--nv-duration-travel);
|
||||
animation-timing-function: var(--nv-ease-emphasized);
|
||||
}
|
||||
|
||||
/* The page behind a travelling element cross-fades quickly — it is context,
|
||||
not the subject of the change, and a slow fade would make the whole
|
||||
interface feel as though it were being replaced. */
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation-duration: var(--nv-duration-quick);
|
||||
animation-timing-function: var(--nv-ease-standard);
|
||||
}
|
||||
|
||||
/* --- Travelling emphasis -------------------------------------------------------
|
||||
A selection marker moves between two elements, which no single element can
|
||||
transition — so it is captured and interpolated. The problem that creates is
|
||||
layering: transition layers paint above the page snapshot, so a marker that
|
||||
sits *behind* its label at rest would slide over the top of every label it
|
||||
passed, washing the text as it went.
|
||||
|
||||
The fix is to capture the labels too and order the groups explicitly. The
|
||||
marker travels below; the items ride above it, exactly as they do at rest.
|
||||
niveus.js assigns each item its name — they have to be unique per document,
|
||||
which no stylesheet can guarantee — and items it has not named simply stay in
|
||||
the page snapshot.
|
||||
|
||||
Because the layering is now correct, the marker needs no trick to stay out of
|
||||
the way: it travels at full opacity, like the solid object it is. */
|
||||
|
||||
::view-transition-group(.nv-travelling) {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
::view-transition-group(.nv-carried) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* A carried item does not move — only its color changes when selection leaves
|
||||
or arrives. That resolves quickly so the label does not sit half-way between
|
||||
two colors for the length of the travel. */
|
||||
::view-transition-old(.nv-carried),
|
||||
::view-transition-new(.nv-carried) {
|
||||
animation-duration: var(--nv-duration-fast);
|
||||
}
|
||||
|
||||
/* --- Appearance ---------------------------------------------------------------
|
||||
A theme change is the one moment the entire surface is the subject, so the
|
||||
root cross-fade slows down and every color arrives together. The attribute
|
||||
is set for the duration of the change by nv.setTheme(). */
|
||||
|
||||
:root[data-nv-transition="theme"]::view-transition-old(root),
|
||||
:root[data-nv-transition="theme"]::view-transition-new(root) {
|
||||
animation-duration: var(--nv-duration-theme);
|
||||
animation-timing-function: var(--nv-ease-standard);
|
||||
}
|
||||
|
||||
/* Without the view-transition path — no script, or a browser without the API —
|
||||
surfaces still glide, because their own transitions cover color. */
|
||||
|
||||
/* --- Direction ------------------------------------------------------------------
|
||||
Moving forward and moving back should not look the same. The application
|
||||
sets data-nv-direction on the root for the length of the transition; content
|
||||
then leaves towards where the user came from and enters from where they are
|
||||
going. Without a direction, content simply rises into place. */
|
||||
|
||||
::view-transition-old(nv-content) {
|
||||
animation: nv-fade-out var(--nv-duration-quick) var(--nv-ease-exit) both;
|
||||
}
|
||||
|
||||
::view-transition-new(nv-content) {
|
||||
animation:
|
||||
nv-fade-in var(--nv-duration-normal) var(--nv-ease-entrance) both,
|
||||
nv-rise var(--nv-duration-normal) var(--nv-ease-entrance) both;
|
||||
}
|
||||
|
||||
:root[data-nv-direction="forward"]::view-transition-old(nv-content) {
|
||||
animation:
|
||||
nv-fade-out var(--nv-duration-quick) var(--nv-ease-exit) both,
|
||||
nv-exit-to-start var(--nv-duration-normal) var(--nv-ease-emphasized) both;
|
||||
}
|
||||
|
||||
:root[data-nv-direction="forward"]::view-transition-new(nv-content) {
|
||||
animation:
|
||||
nv-fade-in var(--nv-duration-normal) var(--nv-ease-entrance) both,
|
||||
nv-enter-from-end var(--nv-duration-normal) var(--nv-ease-emphasized) both;
|
||||
}
|
||||
|
||||
:root[data-nv-direction="backward"]::view-transition-old(nv-content) {
|
||||
animation:
|
||||
nv-fade-out var(--nv-duration-quick) var(--nv-ease-exit) both,
|
||||
nv-exit-to-end var(--nv-duration-normal) var(--nv-ease-emphasized) both;
|
||||
}
|
||||
|
||||
:root[data-nv-direction="backward"]::view-transition-new(nv-content) {
|
||||
animation:
|
||||
nv-fade-in var(--nv-duration-normal) var(--nv-ease-entrance) both,
|
||||
nv-enter-from-start var(--nv-duration-normal) var(--nv-ease-emphasized) both;
|
||||
}
|
||||
|
||||
@keyframes nv-fade-in {
|
||||
from { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes nv-fade-out {
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes nv-rise {
|
||||
from { translate: 0 var(--nv-travel-sm); }
|
||||
}
|
||||
|
||||
@keyframes nv-enter-from-end {
|
||||
from { translate: var(--nv-travel-lg) 0; }
|
||||
}
|
||||
|
||||
@keyframes nv-enter-from-start {
|
||||
from { translate: calc(var(--nv-travel-lg) * -1) 0; }
|
||||
}
|
||||
|
||||
@keyframes nv-exit-to-start {
|
||||
to { translate: calc(var(--nv-travel-md) * -1) 0; }
|
||||
}
|
||||
|
||||
@keyframes nv-exit-to-end {
|
||||
to { translate: var(--nv-travel-md) 0; }
|
||||
}
|
||||
|
||||
/* --- Utilities ---------------------------------------------------------------------
|
||||
.nv-continuous marks a region whose content should cross-fade in place when
|
||||
it changes — the "same surface, different content" relationship. */
|
||||
|
||||
.nv-continuous {
|
||||
view-transition-name: var(--nv-content-name, nv-content);
|
||||
}
|
||||
|
||||
/* Collapsible region for markup that is not a <details>. The 0fr -> 1fr grid
|
||||
row is the one technique that animates to an unknown content height in every
|
||||
browser, with no measurement and no script. */
|
||||
|
||||
.nv-collapse {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition:
|
||||
grid-template-rows var(--nv-duration-expand) var(--nv-ease-standard),
|
||||
opacity var(--nv-duration-expand) var(--nv-ease-standard);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nv-collapse > * {
|
||||
min-block-size: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nv-collapse[data-open],
|
||||
.nv-collapse:not([hidden])[open] {
|
||||
grid-template-rows: 1fr;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Reduced motion does not mean "no continuity" — a state change still has to
|
||||
resolve rather than be cut mid-way. The token layer already collapses every
|
||||
duration to 1ms; this removes the displacement so nothing slides. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
::view-transition-group(*),
|
||||
::view-transition-old(*),
|
||||
::view-transition-new(*) {
|
||||
animation-duration: 1ms;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue