From 6490e5c759b59c6facfa06dafd619f072a26e28a Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Wed, 23 Sep 2026 14:40:09 +0200 Subject: [PATCH] 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 --- docs/Website/Assets/pride.svg | 116 +- docs/Website/README.md | 101 +- docs/Website/app.css | 529 +++++++ docs/Website/app.js | 82 +- docs/Website/docs.html | 126 +- docs/Website/docs.js | 20 +- docs/Website/download.html | 423 +++--- docs/Website/index.html | 909 ++++++----- docs/Website/niveus/README.md | 443 ++++++ docs/Website/niveus/REFERENCE.md | 393 +++++ docs/Website/niveus/examples/ai-chat/app.css | 234 +++ .../niveus/examples/ai-chat/index.html | 485 ++++++ .../niveus/examples/ai-landing/app.css | 186 +++ .../niveus/examples/ai-landing/index.html | 468 ++++++ .../Website/niveus/examples/dashboard/app.css | 153 ++ .../niveus/examples/dashboard/index.html | 578 +++++++ docs/Website/niveus/examples/index.html | 251 +++ docs/Website/niveus/examples/it-shop/app.css | 145 ++ .../niveus/examples/it-shop/index.html | 519 +++++++ docs/Website/niveus/index.html | 1350 +++++++++++++++++ docs/Website/niveus/niveus/base.css | 197 +++ .../niveus/niveus/components/badge.css | 101 ++ docs/Website/niveus/niveus/components/bed.css | 101 ++ .../niveus/niveus/components/button.css | 282 ++++ .../Website/niveus/niveus/components/card.css | 171 +++ .../niveus/niveus/components/dialog.css | 190 +++ .../niveus/niveus/components/disclosure.css | 130 ++ .../niveus/niveus/components/input.css | 402 +++++ .../Website/niveus/niveus/components/menu.css | 175 +++ .../niveus/niveus/components/navigation.css | 349 +++++ .../niveus/niveus/components/scroller.css | 131 ++ .../niveus/niveus/components/surface.css | 116 ++ .../niveus/niveus/components/table.css | 95 ++ docs/Website/niveus/niveus/continuity.css | 209 +++ docs/Website/niveus/niveus/niveus.css | 59 + docs/Website/niveus/niveus/niveus.js | 322 ++++ docs/Website/niveus/niveus/themes/dark.css | 120 ++ docs/Website/niveus/niveus/themes/light.css | 158 ++ docs/Website/niveus/niveus/tokens/color.css | 218 +++ .../niveus/niveus/tokens/elevation.css | 74 + .../niveus/niveus/tokens/materials.css | 77 + docs/Website/niveus/niveus/tokens/motion.css | 107 ++ docs/Website/niveus/niveus/tokens/radius.css | 23 + docs/Website/niveus/niveus/tokens/spacing.css | 54 + .../niveus/niveus/tokens/typography.css | 73 + docs/Website/niveus/niveus/utilities.css | 64 + docs/Website/niveus/tools/build.py | 113 ++ docs/Website/niveus/tools/check.py | 255 ++++ docs/Website/style.css | 701 --------- docs/niveus/README.md | 443 ++++++ docs/niveus/REFERENCE.md | 393 +++++ docs/niveus/examples/ai-chat/app.css | 234 +++ docs/niveus/examples/ai-chat/index.html | 485 ++++++ docs/niveus/examples/ai-landing/app.css | 186 +++ docs/niveus/examples/ai-landing/index.html | 468 ++++++ docs/niveus/examples/dashboard/app.css | 153 ++ docs/niveus/examples/dashboard/index.html | 578 +++++++ docs/niveus/examples/index.html | 251 +++ docs/niveus/examples/it-shop/app.css | 145 ++ docs/niveus/examples/it-shop/index.html | 519 +++++++ docs/niveus/index.html | 1350 +++++++++++++++++ docs/niveus/niveus/base.css | 197 +++ docs/niveus/niveus/components/badge.css | 101 ++ docs/niveus/niveus/components/bed.css | 101 ++ docs/niveus/niveus/components/button.css | 282 ++++ docs/niveus/niveus/components/card.css | 171 +++ docs/niveus/niveus/components/dialog.css | 190 +++ docs/niveus/niveus/components/disclosure.css | 130 ++ docs/niveus/niveus/components/input.css | 402 +++++ docs/niveus/niveus/components/menu.css | 175 +++ docs/niveus/niveus/components/navigation.css | 349 +++++ docs/niveus/niveus/components/scroller.css | 131 ++ docs/niveus/niveus/components/surface.css | 116 ++ docs/niveus/niveus/components/table.css | 95 ++ docs/niveus/niveus/continuity.css | 209 +++ docs/niveus/niveus/niveus.css | 59 + docs/niveus/niveus/niveus.js | 322 ++++ docs/niveus/niveus/themes/dark.css | 120 ++ docs/niveus/niveus/themes/light.css | 158 ++ docs/niveus/niveus/tokens/color.css | 218 +++ docs/niveus/niveus/tokens/elevation.css | 74 + docs/niveus/niveus/tokens/materials.css | 77 + docs/niveus/niveus/tokens/motion.css | 107 ++ docs/niveus/niveus/tokens/radius.css | 23 + docs/niveus/niveus/tokens/spacing.css | 54 + docs/niveus/niveus/tokens/typography.css | 73 + docs/niveus/niveus/utilities.css | 64 + docs/niveus/tools/build.py | 113 ++ docs/niveus/tools/check.py | 255 ++++ 89 files changed, 20795 insertions(+), 1354 deletions(-) create mode 100644 docs/Website/app.css create mode 100644 docs/Website/niveus/README.md create mode 100644 docs/Website/niveus/REFERENCE.md create mode 100644 docs/Website/niveus/examples/ai-chat/app.css create mode 100644 docs/Website/niveus/examples/ai-chat/index.html create mode 100644 docs/Website/niveus/examples/ai-landing/app.css create mode 100644 docs/Website/niveus/examples/ai-landing/index.html create mode 100644 docs/Website/niveus/examples/dashboard/app.css create mode 100644 docs/Website/niveus/examples/dashboard/index.html create mode 100644 docs/Website/niveus/examples/index.html create mode 100644 docs/Website/niveus/examples/it-shop/app.css create mode 100644 docs/Website/niveus/examples/it-shop/index.html create mode 100644 docs/Website/niveus/index.html create mode 100644 docs/Website/niveus/niveus/base.css create mode 100644 docs/Website/niveus/niveus/components/badge.css create mode 100644 docs/Website/niveus/niveus/components/bed.css create mode 100644 docs/Website/niveus/niveus/components/button.css create mode 100644 docs/Website/niveus/niveus/components/card.css create mode 100644 docs/Website/niveus/niveus/components/dialog.css create mode 100644 docs/Website/niveus/niveus/components/disclosure.css create mode 100644 docs/Website/niveus/niveus/components/input.css create mode 100644 docs/Website/niveus/niveus/components/menu.css create mode 100644 docs/Website/niveus/niveus/components/navigation.css create mode 100644 docs/Website/niveus/niveus/components/scroller.css create mode 100644 docs/Website/niveus/niveus/components/surface.css create mode 100644 docs/Website/niveus/niveus/components/table.css create mode 100644 docs/Website/niveus/niveus/continuity.css create mode 100644 docs/Website/niveus/niveus/niveus.css create mode 100644 docs/Website/niveus/niveus/niveus.js create mode 100644 docs/Website/niveus/niveus/themes/dark.css create mode 100644 docs/Website/niveus/niveus/themes/light.css create mode 100644 docs/Website/niveus/niveus/tokens/color.css create mode 100644 docs/Website/niveus/niveus/tokens/elevation.css create mode 100644 docs/Website/niveus/niveus/tokens/materials.css create mode 100644 docs/Website/niveus/niveus/tokens/motion.css create mode 100644 docs/Website/niveus/niveus/tokens/radius.css create mode 100644 docs/Website/niveus/niveus/tokens/spacing.css create mode 100644 docs/Website/niveus/niveus/tokens/typography.css create mode 100644 docs/Website/niveus/niveus/utilities.css create mode 100644 docs/Website/niveus/tools/build.py create mode 100644 docs/Website/niveus/tools/check.py delete mode 100644 docs/Website/style.css create mode 100644 docs/niveus/README.md create mode 100644 docs/niveus/REFERENCE.md create mode 100644 docs/niveus/examples/ai-chat/app.css create mode 100644 docs/niveus/examples/ai-chat/index.html create mode 100644 docs/niveus/examples/ai-landing/app.css create mode 100644 docs/niveus/examples/ai-landing/index.html create mode 100644 docs/niveus/examples/dashboard/app.css create mode 100644 docs/niveus/examples/dashboard/index.html create mode 100644 docs/niveus/examples/index.html create mode 100644 docs/niveus/examples/it-shop/app.css create mode 100644 docs/niveus/examples/it-shop/index.html create mode 100644 docs/niveus/index.html create mode 100644 docs/niveus/niveus/base.css create mode 100644 docs/niveus/niveus/components/badge.css create mode 100644 docs/niveus/niveus/components/bed.css create mode 100644 docs/niveus/niveus/components/button.css create mode 100644 docs/niveus/niveus/components/card.css create mode 100644 docs/niveus/niveus/components/dialog.css create mode 100644 docs/niveus/niveus/components/disclosure.css create mode 100644 docs/niveus/niveus/components/input.css create mode 100644 docs/niveus/niveus/components/menu.css create mode 100644 docs/niveus/niveus/components/navigation.css create mode 100644 docs/niveus/niveus/components/scroller.css create mode 100644 docs/niveus/niveus/components/surface.css create mode 100644 docs/niveus/niveus/components/table.css create mode 100644 docs/niveus/niveus/continuity.css create mode 100644 docs/niveus/niveus/niveus.css create mode 100644 docs/niveus/niveus/niveus.js create mode 100644 docs/niveus/niveus/themes/dark.css create mode 100644 docs/niveus/niveus/themes/light.css create mode 100644 docs/niveus/niveus/tokens/color.css create mode 100644 docs/niveus/niveus/tokens/elevation.css create mode 100644 docs/niveus/niveus/tokens/materials.css create mode 100644 docs/niveus/niveus/tokens/motion.css create mode 100644 docs/niveus/niveus/tokens/radius.css create mode 100644 docs/niveus/niveus/tokens/spacing.css create mode 100644 docs/niveus/niveus/tokens/typography.css create mode 100644 docs/niveus/niveus/utilities.css create mode 100644 docs/niveus/tools/build.py create mode 100644 docs/niveus/tools/check.py diff --git a/docs/Website/Assets/pride.svg b/docs/Website/Assets/pride.svg index be1b140..96ecebd 100644 --- a/docs/Website/Assets/pride.svg +++ b/docs/Website/Assets/pride.svg @@ -1,22 +1,106 @@ - - Progress Pride flag - The six-stripe rainbow with the Progress chevron: white, pink and light + + + + + Progress Pride flag + The six-stripe rainbow with the Progress chevron: white, pink and light blue for trans people, brown and black for queer people of colour and for those lost to AIDS. It sits in the top bar every day of the year, which is the whole point of it. - - - - - - - - + + + + + + - - - - - + + + + + diff --git a/docs/Website/README.md b/docs/Website/README.md index 05931a9..af474db 100644 --- a/docs/Website/README.md +++ b/docs/Website/README.md @@ -1,20 +1,73 @@ # The website -Six files and a folder of marks. No build step, no dependencies, no -framework — open `index.html` and it works, including from a `file://` URL. +Three pages, two scripts, one stylesheet of its own, and the design system +underneath. No build step, no dependencies, no framework — open `index.html` +and it works, including from a `file://` URL. ``` index.html the front page download.html get it, write it to a stick, or build it docs.html the documentation reader -style.css the palette, transcribed from include/theme.h -app.js the front page: the boot log types, sections fade in +app.css this site's layout — and nothing else, see below +app.js the boot log types, sections arrive, the bar takes a shadow docs.js the Markdown renderer and the doc index +niveus/ Niveus, vendored. The site links niveus/dist/niveus.css and + niveus/niveus/niveus.js Assets/ shift.svg (logo), uberblock.svg (SchFS), usb.svg (downloads), pride.svg ``` +## It is built on Niveus + +[Niveus](../niveus/) is the interface language the project uses for everything +with a screen — this site first, applications after it. The rule that makes it +worth having is the one it enforces on this directory: + +**`app.css` contains no colour, shadow, radius, type size or duration.** It is +layout: where things sit, how wide they get, when they stack. Everything else +is a token. A hex value in that file means the system is missing something, and +the answer is to add it to Niveus rather than to work around it here. + +What that replaced was a 21 KB `style.css` that transcribed +[`include/theme.h`](../../include/theme.h) by hand — palette, type scale, +shadows, the lot. The intent was right and the mechanism was a copy: two files +that had to be kept in step by remembering to. Now the whole identity is one +declaration, in the `` of each page: + +```css +:root { --nv-brand: #7C5CFF; } +``` + +That is `SCH_ACCENT`, the same violet the kernel draws with. 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. The site still +looks like the operating system, and now there is one number to keep true +instead of forty. + +**The appearance follows the system, and remembers what you pick.** Dark is the +default because the OS is dark; the Light/Dark/Auto group in the bar is real, +and `app.js` keeps the choice in `localStorage`. An inline script in each +`` reads it back before first paint, so the page never renders in one +appearance and corrects itself in front of you. + +**PSOGM purple appears exactly once.** `#4B1D9A` is on the crash-screen section +and nowhere else, because [../branding/README.md](../branding/README.md) says +it means *the machine stopped* and that a colour used everywhere means nothing. +It is spelled the way Niveus spells a subtree with its own colour — +`data-nv-brand` with its own `--nv-brand` — so every role inside that section +re-derives from it rather than being painted on. If it shows up in a second +place, that is a bug in the page. + +### Updating the system + +`niveus/` here is a copy, so that this directory can be deployed on its own. +The original lives in [../niveus/](../niveus/) and is where changes are made: + +```sh +rm -rf docs/Website/niveus && cp -r docs/niveus docs/Website/niveus +``` + ## The download page `download.html` links to `releases/schos.iso`, a path relative to this @@ -27,11 +80,12 @@ make iso mkdir -p docs/Website/releases && cp schos.iso docs/Website/releases/ ``` -The page is written so that this is the only thing that needs doing, and so that -nothing on it is a promise the tree cannot keep. In particular the support table -says **one real machine, under test** rather than listing hardware, because one -Chromebox is the entire bare-metal record and a compatibility list would be -fiction. When that changes, change the table; do not soften the wording. +The page is written so that this is the only thing that needs doing, and so +that nothing on it is a promise the tree cannot keep. In particular the support +table says **one real machine, under test** rather than listing hardware, +because one Chromebox is the entire bare-metal record and a compatibility list +would be fiction. When that changes, change the table; do not soften the +wording. The `dd` command is split across two lines on purpose. It was one long line first, and the card clipped it at `conv=` — a destructive command that can @@ -41,8 +95,13 @@ scroll half of itself out of view is a good way to take somebody's disk. `docs.html` renders the Markdown in `docs/` in the site's own theme, with the index down the left and a table of contents down the right. Links between -documents are rewritten to stay inside the reader, so `../history/schfs.md` -in an architecture page navigates rather than downloads. +documents are rewritten to stay inside the reader, so `../history/schfs.md` in +an architecture page navigates rather than downloads. + +Both rails are `.nv-nav--vertical`, the current document is marked with +`aria-current="page"` rather than a class of our own, and the renderer emits +`.nv-table` and `.nv-code` — the reader is built from the same system as the +rest of the site, so a change to Niveus reaches the documentation too. The parser is about 150 lines in `docs.js` and handles what `docs/` actually uses: headings, fenced code, tables, lists, quotes, rules and inline marks. @@ -79,22 +138,13 @@ is not. ## The flag -`Assets/pride.svg` is in the top bar and in the footer, on both pages, and it +`Assets/pride.svg` is in the top bar and in the footer, on every page, and it is not seasonal. Support with an end date is marketing; this is a project that belongs to whoever writes it. It is also deliberately small — the site is about an operating system, and the flag is a statement, not a subject. ## The rules it follows -**The palette is not a design decision.** It is `include/theme.h`, copied. A -site describing an operating system should look like the operating system, and -copying the header means the two cannot drift apart by accident. - -**PSOGM purple appears exactly once.** `#4B1D9A` is on the crash-screen section -and nowhere else, because [../branding/README.md](../branding/README.md) says -it means *the machine stopped* and that a colour used everywhere means nothing. -If it shows up in a second place, that is a bug in the page. - **The typefaces are the ones the OS ships.** Roboto for text and JetBrains Mono for the system voice — both already in `ports/`, both already in `NOTICE`. They are requested from the system rather than downloaded: a font file would be @@ -102,7 +152,8 @@ larger than the entire rest of the site. **The script is optional.** If `app.js` fails to load the page is still complete and still readable. Everything it does is decoration, and it honours -`prefers-reduced-motion` by doing none of it. +`prefers-reduced-motion` by doing none of it. `niveus.js` is optional in the +same way — without it, state changes arrive instead of travelling. **The boot log is real.** The lines in `app.js` are from an actual boot, not invented. The timer calibration line is there deliberately; see section 13 of @@ -118,6 +169,6 @@ find kernel include -name '*.c' -o -name '*.h' | xargs wc -l | tail -1 grep -c '^SYSCALL' kernel/sys/table.inc ``` -The feature cards carry a tag — `runs`, `in progress`, or `designed` — and the -tag is a promise. Anything marked `runs` is in the tree and works today. When -that stops being true, change the tag rather than the wording. +The feature cards carry a badge — `runs`, `in progress`, or `designed` — and +the badge is a promise. Anything marked `runs` is in the tree and works today. +When that stops being true, change the badge rather than the wording. diff --git a/docs/Website/app.css b/docs/Website/app.css new file mode 100644 index 0000000..ec01730 --- /dev/null +++ b/docs/Website/app.css @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* ========================================================================== + The SchOS site — layout only + -------------------------------------------------------------------------- + Everything here is *arrangement*: where things sit, how wide they get, when + they stack. Not one colour, shadow, radius, type size or duration is stated + in this file — those come from Niveus, which is the whole point of the + split. A hex value in here means the system is missing a token, and the + answer is to add one rather than to work around it. + + The one exception is `--nv-brand`, which is declared in the pages: it is + SCH_ACCENT from include/theme.h, and it is an input to the system rather + than a restatement of it. + ========================================================================== */ + +/* --- The page shell -------------------------------------------------------- */ + +.shell { + max-inline-size: var(--nv-layout-max-width); + margin-inline: auto; + padding-inline: var(--nv-layout-gutter); +} + +.section { + padding-block: var(--nv-space-20); +} + +.section__head { + max-inline-size: var(--nv-measure); + margin-block-end: var(--nv-space-8); + display: flex; + flex-direction: column; + gap: var(--nv-space-3); +} + +/* A closing remark under a figure. Narrower than the figure, so it reads as + commentary on it rather than as the next section. */ +.section__note { + max-inline-size: var(--nv-measure); + margin-block-start: var(--nv-space-5); +} + +/* The flag in the bar. Deliberately small: the site is about an operating + system, and the flag is a statement, not a subject. */ +.pride img { + display: block; + block-size: var(--nv-space-4); + inline-size: auto; + border-radius: var(--nv-radius-xs); +} + +@media (max-width: 60rem) { + .appearance { display: none; } +} + +/* --- Hero ------------------------------------------------------------------ */ + +.hero { + display: grid; + gap: var(--nv-space-12); + padding-block: var(--nv-space-16) var(--nv-space-8); + align-items: center; +} + +@media (min-width: 62rem) { + .hero { + grid-template-columns: 5fr 7fr; + gap: var(--nv-space-14); + } +} + +.hero__copy { + display: flex; + flex-direction: column; + gap: var(--nv-space-5); + align-items: flex-start; +} + +.hero__title { + display: flex; + align-items: center; + gap: var(--nv-space-4); + font-size: clamp(var(--nv-font-size-4xl), 7vw, var(--nv-font-size-5xl)); +} + +.hero__mark { + inline-size: 1.15em; + block-size: 1.15em; +} + +/* --- The console ----------------------------------------------------------- */ +/* + A terminal is a recess, not a card that happens to hold monospace: the + material says "this is a window into the machine" before the text does. +*/ + +.console { + overflow: hidden; +} + +.console__bar { + display: flex; + align-items: center; + gap: var(--nv-space-3); + padding: var(--nv-space-2) var(--nv-space-3); + border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle); +} + +.console__log { + margin: 0; + padding: var(--nv-space-4); + min-block-size: 18rem; + overflow-x: auto; + white-space: pre; + line-height: var(--nv-line-height-relaxed); + background: var(--nv-color-surface-inset); + box-shadow: var(--nv-elevation-inset-sm); +} + +/* The cursor the boot log types behind. */ +.console__log::after { + content: ""; + display: inline-block; + inline-size: 0.6em; + block-size: 1em; + vertical-align: text-bottom; + background: var(--nv-color-accent); + animation: console-blink 1.1s steps(2, start) infinite; +} + +@keyframes console-blink { 50% { opacity: 0; } } + +@media (prefers-reduced-motion: reduce) { + .console__log::after { animation: none; } +} + +/* --- Origin, as a timeline -------------------------------------------------- */ +/* + The rail is not decoration: these steps happened in this order, and the + order is the claim the section makes. A grid would lose it. +*/ + +.timeline { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: var(--nv-space-8); + border-inline-start: var(--nv-border-width) solid var(--nv-color-border); + padding-inline-start: var(--nv-space-6); +} + +.timeline__step { + display: flex; + flex-direction: column; + gap: var(--nv-space-2); + max-inline-size: var(--nv-measure); + position: relative; +} + +.timeline__step::before { + content: ""; + position: absolute; + inline-size: var(--nv-space-2); + block-size: var(--nv-space-2); + border-radius: var(--nv-radius-full); + background: var(--nv-color-accent); + inset-inline-start: calc(var(--nv-space-6) * -1 - var(--nv-space-1)); + inset-block-start: var(--nv-space-1); +} + +.timeline__when { + color: var(--nv-color-text-tertiary); +} + +/* --- The stack -------------------------------------------------------------- */ + +.stack { + display: flex; + flex-direction: column; +} + +.stack__layer { + display: grid; + grid-template-columns: 12rem 1fr; + gap: var(--nv-space-4); + align-items: baseline; + padding: var(--nv-space-3) var(--nv-space-5); + border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle); +} + +.stack__layer:last-child { border-block-end: 0; } + +.stack__name { + display: flex; + align-items: center; + gap: var(--nv-space-2); + font-family: var(--nv-font-family-mono); + color: var(--nv-color-text-primary); +} + +/* The three seams the section is about. They are the argument, so they carry + the accent ground; everything else is the arrangement between them. */ +.stack__layer--seam { + background: var(--nv-color-accent-subtle); + box-shadow: inset var(--nv-border-width-strong) 0 0 var(--nv-color-accent); +} + +@media (max-width: 48rem) { + .stack__layer { + grid-template-columns: 1fr; + gap: var(--nv-space-1); + } +} + +/* --- Hybrid: what a driver needs -------------------------------------------- */ + +.needs { + display: grid; + gap: var(--nv-space-4); + margin-block-end: var(--nv-space-8); +} + +@media (min-width: 48rem) { + .needs { grid-template-columns: repeat(2, 1fr); } +} + +/* --- Numbers ---------------------------------------------------------------- */ + +.figures { + display: grid; + gap: var(--nv-space-4); + grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); +} + +/* A card carries no padding of its own -- its parts do -- and a stat tile has + no parts. */ +.figure { + display: flex; + flex-direction: column; + gap: var(--nv-space-1); + align-items: flex-start; + padding: var(--nv-space-5); +} + +.figure__value { + font-variant-numeric: tabular-nums; + letter-spacing: var(--nv-letter-spacing-tight); +} + +/* --- PSOGM ------------------------------------------------------------------ */ + +.crash { + display: grid; + gap: var(--nv-space-8); + align-items: center; + padding: var(--nv-space-12); +} + +@media (min-width: 48rem) { + .crash { grid-template-columns: auto 1fr; } +} + +/* The bed's label is a caption for the whole region, not the first cell of + its grid. */ +.crash .nv-bed__label { + grid-column: 1 / -1; +} + +.crash__mark { + inline-size: var(--nv-space-20); + block-size: auto; +} + +.crash__copy { + display: flex; + flex-direction: column; + gap: var(--nv-space-4); +} + +/* --- The closing band -------------------------------------------------------- */ + +/* Padded here for the same reason .figure is: a card's padding lives on its + parts, and this band has none -- it is a mark, some copy and two buttons. */ +.getband { + display: grid; + gap: var(--nv-space-8); + align-items: center; + padding: var(--nv-space-8); +} + +@media (min-width: 48rem) { + .getband { grid-template-columns: auto 1fr; } +} + +.getband__mark { + inline-size: var(--nv-space-20); + block-size: auto; + justify-self: center; +} + +.getband__copy { + display: flex; + flex-direction: column; + gap: var(--nv-space-4); + align-items: flex-start; +} + +/* --- Cards ------------------------------------------------------------------- */ + +/* Niveus's reset makes images block-level, which is right nearly everywhere + and wrong for a mark that belongs beside a title. A flex row rather than + `display: inline`, so the two stay aligned whatever the title does. */ +.nv-card__title:has(.cardmark) { + display: flex; + align-items: center; + gap: var(--nv-space-2); +} + +.cardmark { + inline-size: 1.2em; + block-size: 1.2em; + flex: none; +} + +/* --- Footer ------------------------------------------------------------------ */ + +.site-footer { + display: flex; + flex-direction: column; + gap: var(--nv-space-5); + padding-block: var(--nv-space-8) var(--nv-space-16); +} + +.site-footer__row { + display: flex; + flex-wrap: wrap; + gap: var(--nv-space-2) var(--nv-space-6); + align-items: baseline; + justify-content: space-between; + padding-block-start: var(--nv-space-6); +} + +.pride-note { + display: flex; + align-items: center; + gap: var(--nv-space-3); +} + +.pride-note img { + block-size: var(--nv-space-4); + inline-size: auto; + border-radius: var(--nv-radius-xs); +} + +/* --- Reveal on scroll -------------------------------------------------------- */ +/* + app.js adds .is-revealed. Without the script every section is simply + visible, which is the only acceptable failure mode for decoration. +*/ + +.section[data-reveal] { + opacity: 0; + translate: 0 var(--nv-travel-md); + transition: + opacity var(--nv-duration-slow) var(--nv-ease-entrance), + translate var(--nv-duration-slow) var(--nv-ease-entrance); +} + +.section[data-reveal].is-revealed { + opacity: 1; + translate: 0 0; +} + +/* --- Cards that are entirely a link ------------------------------------------ */ +/* + The whole card is the target -- `.nv-card--interactive` says so by lifting + under the pointer -- so an underline on the title is a second affordance for + the same thing, and it reads as a link *inside* a link. +*/ + +.nv-card__link { + text-decoration: none; +} + +/* --- Command blocks ----------------------------------------------------------- */ +/* + A command is the one thing on these pages that must be copied exactly, so it + gets a recess of its own rather than sitting in the card's own material. + `white-space: pre` and not `pre-wrap`: a wrapped shell command is a command + that can be mistyped. +*/ + +.cmd { + margin: 0; + padding: var(--nv-space-3) var(--nv-space-4); + overflow-x: auto; + white-space: pre; + line-height: var(--nv-line-height-relaxed); + background: var(--nv-color-surface-inset); + border-radius: var(--nv-radius-nested-md); + box-shadow: var(--nv-elevation-inset-sm); +} + +/* --- The documentation reader ------------------------------------------------ */ +/* + Index, document, contents. Three columns that become one on a narrow screen, + in that order of importance: the document is the page, the index is how you + got there, and the contents panel is a convenience. +*/ + +.docshell { + max-inline-size: var(--nv-layout-max-width); + margin-inline: auto; + padding-inline: var(--nv-layout-gutter); + padding-block: var(--nv-space-8) var(--nv-space-16); + display: grid; + gap: var(--nv-space-8); + align-items: start; +} + +@media (min-width: 64rem) { + .docshell { + grid-template-columns: 15rem minmax(0, 1fr) 14rem; + gap: var(--nv-space-10); + } +} + +@media (max-width: 64rem) { + .docshell__rail { display: none; } +} + +/* Both panels follow the reader down the page, under the bar rather than + behind it. */ +.docshell__aside, +.docshell__rail { + position: sticky; + inset-block-start: var(--nv-space-16); + max-block-size: calc(100vh - var(--nv-space-24)); + overflow-y: auto; +} + +/* A vertical nav that is a panel in a page, not the chrome of an app: no + surface of its own, and no full-height rule down its edge. */ +.docnav { + position: static; /* the column is what sticks, not the nav */ + background: none; + border: 0; + box-shadow: none; + padding: 0; + inline-size: 100%; /* the sidebar component is a fixed 15rem */ + block-size: auto; +} + +.docnav .nv-nav__list { + flex-direction: column; + align-items: stretch; + gap: var(--nv-space-px); + margin-block-end: var(--nv-space-5); +} + +/* A nav item in a bar is one line by definition; a document title is not. The + contents panel is 14rem wide and some headings are longer than that, so they + wrap rather than being cut off mid-word -- a truncated entry is a + destination you cannot recognise. */ +.docnav .nv-nav__item { + justify-content: flex-start; + inline-size: 100%; + white-space: normal; + block-size: auto; + min-block-size: var(--nv-control-height-sm); + padding-block: var(--nv-space-1-5); + text-align: start; +} + +/* A contents panel is a shape: depth reads as indentation. */ +.toc-lvl3 .nv-nav__item { padding-inline-start: var(--nv-space-5); } +.toc-lvl4 .nv-nav__item { padding-inline-start: var(--nv-space-8); } + +/* --- The document itself ----------------------------------------------------- */ + +.doc { + max-inline-size: none; /* the column already sets the measure */ +} + +.doc > * + * { + margin-block-start: var(--nv-space-4); +} + +.doc h1, +.doc h2, +.doc h3, +.doc h4 { + margin-block-start: var(--nv-space-8); + scroll-margin-block-start: var(--nv-space-20); +} + +.doc h2 { + padding-block-end: var(--nv-space-2); + border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle); +} + +.doc__pre { + margin: 0; + padding: var(--nv-space-4); + overflow-x: auto; + white-space: pre; + line-height: var(--nv-line-height-relaxed); + background: var(--nv-color-surface-inset); + border-radius: var(--nv-radius-md); + box-shadow: var(--nv-elevation-inset-sm); +} + +.doc blockquote { + margin-inline: 0; + padding: var(--nv-space-1) var(--nv-space-5); + border-inline-start: var(--nv-border-width-strong) solid var(--nv-color-accent-border); + color: var(--nv-color-text-secondary); +} + +.doc ul, +.doc ol { + padding-inline-start: var(--nv-space-6); +} + +.doc li + li { + margin-block-start: var(--nv-space-2); +} diff --git a/docs/Website/app.js b/docs/Website/app.js index e1a7a06..a41aa0c 100644 --- a/docs/Website/app.js +++ b/docs/Website/app.js @@ -1,12 +1,16 @@ /* SPDX-License-Identifier: Apache-2.0 * - * Two behaviours, no framework: + * Three behaviours, no framework: * 1. the hero console types a real boot log * 2. sections fade in once, as they arrive + * 3. the bar takes a shadow once the page has scrolled under it * - * Both are decoration. If this file fails to load the page is still complete - * and still readable, which is the test any script on a documentation site - * should have to pass. + * All three are decoration. If this file fails to load the page is still + * complete and still readable, which is the test any script on a + * documentation site should have to pass. + * + * Niveus does the rest: its own niveus.js wires the appearance buttons, and + * everything visual comes from the stylesheet. Nothing here sets a style. */ (function () { "use strict"; @@ -34,6 +38,8 @@ var out = document.getElementById("bootlog"); + /* The classes are Niveus text roles, so the log is coloured by the same + tokens as the rest of the page and follows the appearance with it. */ function span(cls, text) { var e = document.createElement("span"); if (cls) e.className = cls; @@ -43,28 +49,19 @@ function emit(line) { if (line.t) { - out.appendChild(span("t", line.t + " ")); - out.appendChild(span("k", line.k)); + out.appendChild(span("nv-text-tertiary", line.t + " ")); + out.appendChild(span("nv-text-accent", line.k)); } - out.appendChild(span(line.g ? "g" : "", line.s)); + out.appendChild(span(line.g ? "nv-text-success" : "", line.s)); out.appendChild(document.createTextNode("\n")); } - function cursor() { - var c = document.createElement("i"); - c.className = "cursor"; - out.appendChild(c); - } - - if (!out) { - /* nothing to do */ - } else if (reduced) { + if (out && reduced) { LINES.forEach(emit); // the whole log at once, no animation - cursor(); - } else { + } else if (out) { var i = 0; (function step() { - if (i >= LINES.length) { cursor(); return; } + if (i >= LINES.length) return; emit(LINES[i++]); /* Uneven on purpose: a boot that ticks out at a metronome's pace reads as a progress bar rather than as a machine doing different work. */ @@ -74,20 +71,45 @@ /* ---- 2. reveal on scroll ------------------------------------------ */ - var targets = document.querySelectorAll(".reveal"); + var targets = document.querySelectorAll("[data-reveal]"); if (reduced || !("IntersectionObserver" in window)) { - Array.prototype.forEach.call(targets, function (el) { el.classList.add("in"); }); - return; + Array.prototype.forEach.call(targets, function (el) { + el.classList.add("is-revealed"); + }); + } else { + var io = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (!entry.isIntersecting) return; + entry.target.classList.add("is-revealed"); + io.unobserve(entry.target); // once, not on every scroll past + }); + }, { rootMargin: "0px 0px -12% 0px", threshold: 0.05 }); + + Array.prototype.forEach.call(targets, function (el) { io.observe(el); }); } - var io = new IntersectionObserver(function (entries) { - entries.forEach(function (entry) { - if (!entry.isIntersecting) return; - entry.target.classList.add("in"); - io.unobserve(entry.target); // once, not on every scroll past - }); - }, { rootMargin: "0px 0px -12% 0px", threshold: 0.05 }); + /* ---- 3. the bar, once something has passed under it ---------------- */ - Array.prototype.forEach.call(targets, function (el) { io.observe(el); }); + var bar = document.querySelector(".nv-nav"); + + if (bar) { + var onScroll = function () { + if (window.scrollY > 8) bar.setAttribute("data-scrolled", ""); + else bar.removeAttribute("data-scrolled"); + }; + addEventListener("scroll", onScroll, { passive: true }); + onScroll(); + } + + /* ---- and remember the appearance ----------------------------------- */ + /* + * Niveus changes the appearance; which one was chosen is this site's to + * keep. The inline script in reads it back before first paint. + */ + document.documentElement.addEventListener("nv:theme", function (event) { + try { + localStorage.setItem("schos-theme", (event.detail && event.detail.theme) || "system"); + } catch (e) { /* storage unavailable — the choice lasts this visit */ } + }); })(); diff --git a/docs/Website/docs.html b/docs/Website/docs.html index b97210a..d5f9e79 100644 --- a/docs/Website/docs.html +++ b/docs/Website/docs.html @@ -1,74 +1,104 @@ - + Docs — SchOS - + + + + + + + + - -
-
- - - SchOS - - - Progress Pride flag - - + + +
+ +
-
-