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>
519 lines
29 KiB
HTML
519 lines
29 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Northwire — an IT shop built with Niveus</title>
|
||
|
||
<link rel="stylesheet" href="../../dist/niveus.css">
|
||
<script src="../../niveus/niveus.js" defer></script>
|
||
<link rel="stylesheet" href="app.css">
|
||
|
||
<script>
|
||
try {
|
||
var saved = localStorage.getItem("northwire-theme");
|
||
if (saved && saved !== "system") document.documentElement.setAttribute("data-theme", saved);
|
||
} catch (error) {}
|
||
</script>
|
||
|
||
<!-- A shop, not an AI product. One declaration is the whole difference. -->
|
||
<style>
|
||
:root { --nv-brand: #ea580c; }
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<nav class="nv-nav" id="topnav" aria-label="Main" style="--nv-selection-name: nv-shop-nav">
|
||
<a class="nv-nav__brand" href="#top">
|
||
<svg class="nv-nav__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M4 7h16l-1.5 12h-13z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
|
||
<path d="M9 7V5.5a3 3 0 016 0V7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
||
</svg>
|
||
Northwire
|
||
</a>
|
||
|
||
<div class="nv-input-group search">
|
||
<span class="nv-input-group__affix">
|
||
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" style="inline-size: 1em; block-size: 1em">
|
||
<circle cx="11" cy="11" r="6.5" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M16 16l4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</span>
|
||
<label class="nv-visually-hidden" for="search">Search products</label>
|
||
<input class="nv-input nv-input--sm" id="search" type="search" placeholder="Search 4,200 products…">
|
||
</div>
|
||
|
||
<span class="nv-nav__spacer"></span>
|
||
|
||
<div class="nv-button-group" role="group" aria-label="Appearance">
|
||
<button class="nv-button nv-button--sm" data-nv-theme="light" aria-pressed="false">Light</button>
|
||
<button class="nv-button nv-button--sm" data-nv-theme="dark" aria-pressed="false">Dark</button>
|
||
<button class="nv-button nv-button--sm" data-nv-theme="system" aria-pressed="true">Auto</button>
|
||
</div>
|
||
|
||
<button class="nv-button nv-button--sm nv-button--primary" data-nv-dialog="basket">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M4 7h16l-1.5 12h-13z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
|
||
</svg>
|
||
Basket
|
||
<span class="nv-badge nv-badge--count nv-badge--solid" id="basket-count">3</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<main id="top">
|
||
|
||
<section class="shell section">
|
||
<div class="nv-bed nv-bed--single">
|
||
<div class="nv-cluster nv-cluster--between">
|
||
<div style="max-inline-size: var(--nv-measure)">
|
||
<span class="nv-overline">Clearance</span>
|
||
<h1 class="nv-heading" style="margin-block: var(--nv-space-2)">
|
||
Ex-demo workstations, checked and warrantied.
|
||
</h1>
|
||
<p class="nv-prose">
|
||
Everything here has been bench-tested and comes with the same
|
||
two-year return window as new stock.
|
||
</p>
|
||
</div>
|
||
<button class="nv-button nv-button--lg nv-button--primary">See all 74 deals</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="shell section">
|
||
<div class="section__head">
|
||
<div>
|
||
<span class="nv-overline">This week</span>
|
||
<h2 class="nv-heading">Reduced while stock lasts</h2>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nv-scroller-frame" data-nv-scroller>
|
||
<div class="nv-scroller" style="--nv-scroller-item: 15rem">
|
||
<article class="nv-card nv-card--interactive nv-card--flush">
|
||
<div class="nv-bed nv-bed--quiet product__art" style="aspect-ratio: 16/9"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 6.5h14v9H5z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M3 18.5h18" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<h3 class="nv-label">Forge 14 Developer</h3>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Ryzen 9 · 32 GB · 1 TB NVMe</p>
|
||
<p class="product__price" style="margin-block-start: var(--nv-space-2)">$1,849</p>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--interactive nv-card--flush">
|
||
<div class="nv-bed nv-bed--quiet product__art" style="aspect-ratio: 16/9"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="4.5" width="18" height="12" rx="1.8" stroke="currentColor" stroke-width="1.6"/><path d="M9.5 19.5h5M12 16.5v3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<h3 class="nv-label">Clarity 27 4K</h3>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">27″ · 3840×2160 · 144 Hz</p>
|
||
<p class="product__price" style="margin-block-start: var(--nv-space-2)">$629</p>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--interactive nv-card--flush">
|
||
<div class="nv-bed nv-bed--quiet product__art" style="aspect-ratio: 16/9"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="2.5" y="7" width="19" height="10" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M6 10.5h1M9 10.5h1M12 10.5h1M15 10.5h1M18 10.5h1M8 13.8h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<h3 class="nv-label">Anvil TKL</h3>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Tactile · hot-swap · USB-C</p>
|
||
<p class="product__price" style="margin-block-start: var(--nv-space-2)">$139</p>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--interactive nv-card--flush">
|
||
<div class="nv-bed nv-bed--quiet product__art" style="aspect-ratio: 16/9"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="2.5" y="7.5" width="19" height="9" rx="1.6" stroke="currentColor" stroke-width="1.6"/><circle cx="8.5" cy="12" r="2.4" stroke="currentColor" stroke-width="1.6"/><circle cx="15.5" cy="12" r="2.4" stroke="currentColor" stroke-width="1.6"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<h3 class="nv-label">Halo X900 16 GB</h3>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Triple fan · 2.6 GHz boost</p>
|
||
<p class="product__price" style="margin-block-start: var(--nv-space-2)">$1,099</p>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--interactive nv-card--flush">
|
||
<div class="nv-bed nv-bed--quiet product__art" style="aspect-ratio: 16/9"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="4" y="6" width="16" height="12" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 10h8M8 14h5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<h3 class="nv-label">Vault 4 TB NVMe</h3>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Gen4 · 7,400 MB/s read</p>
|
||
<p class="product__price" style="margin-block-start: var(--nv-space-2)">$289</p>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
<button class="nv-button nv-button--icon nv-button--float" data-nv-scroll="prev" aria-label="Previous">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M14 6l-6 6 6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<button class="nv-button nv-button--icon nv-button--float" data-nv-scroll="next" aria-label="Next">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M10 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="shell section" id="catalogue">
|
||
<div class="catalogue">
|
||
|
||
<aside class="filters" aria-label="Filters">
|
||
<div class="filter-group">
|
||
<span class="nv-overline">Category</span>
|
||
<label class="nv-choice nv-caption"><input class="nv-checkbox" type="checkbox" checked> Workstations</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-checkbox" type="checkbox" checked> Displays</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-checkbox" type="checkbox"> Networking</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-checkbox" type="checkbox"> Peripherals</label>
|
||
</div>
|
||
|
||
<hr class="nv-divider" style="margin: 0">
|
||
|
||
<div class="filter-group">
|
||
<span class="nv-overline">Price</span>
|
||
<div class="nv-cluster nv-cluster--tight" style="flex-wrap: nowrap">
|
||
<div class="nv-input-group">
|
||
<span class="nv-input-group__affix">$</span>
|
||
<label class="nv-visually-hidden" for="min">Minimum price</label>
|
||
<input class="nv-input nv-input--sm" id="min" inputmode="numeric" value="0">
|
||
</div>
|
||
<span class="nv-caption">to</span>
|
||
<div class="nv-input-group">
|
||
<span class="nv-input-group__affix">$</span>
|
||
<label class="nv-visually-hidden" for="max">Maximum price</label>
|
||
<input class="nv-input nv-input--sm" id="max" inputmode="numeric" value="2000">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="nv-divider" style="margin: 0">
|
||
|
||
<div class="filter-group">
|
||
<span class="nv-overline">Availability</span>
|
||
<label class="nv-choice nv-caption"><input class="nv-switch" type="checkbox" checked> In stock only</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-switch" type="checkbox"> Ex-demo</label>
|
||
</div>
|
||
|
||
<hr class="nv-divider" style="margin: 0">
|
||
|
||
<div class="filter-group">
|
||
<span class="nv-overline">Condition</span>
|
||
<label class="nv-choice nv-caption"><input class="nv-radio" type="radio" name="condition" checked> Any</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-radio" type="radio" name="condition"> New</label>
|
||
<label class="nv-choice nv-caption"><input class="nv-radio" type="radio" name="condition"> Refurbished</label>
|
||
</div>
|
||
|
||
<button class="nv-button nv-button--ghost nv-button--sm">Clear filters</button>
|
||
</aside>
|
||
|
||
<div>
|
||
<div class="section__head">
|
||
<div>
|
||
<h2 class="nv-title">Workstations & displays</h2>
|
||
<p class="nv-caption">8 of 412 products</p>
|
||
</div>
|
||
|
||
<button class="nv-button nv-button--sm nv-menu-trigger" popovertarget="sort-menu"
|
||
style="--nv-anchor: --sort">
|
||
Sort: <span id="sort-label">Relevance</span>
|
||
<span class="nv-disclosure__chevron" aria-hidden="true"
|
||
style="margin-inline-start: var(--nv-space-1)"></span>
|
||
</button>
|
||
|
||
<div class="nv-menu" popover id="sort-menu" style="--nv-anchor: --sort">
|
||
<button class="nv-menu__item" aria-checked="true" data-sort="Relevance">Relevance</button>
|
||
<button class="nv-menu__item" data-sort="Price, low to high">Price, low to high</button>
|
||
<button class="nv-menu__item" data-sort="Price, high to low">Price, high to low</button>
|
||
<button class="nv-menu__item" data-sort="Newest">Newest</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="products">
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 6.5h14v9H5z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M3 18.5h18" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Forge 14 Developer</h3>
|
||
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>In stock</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Ryzen 9 · 32 GB · 1 TB NVMe</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$1,849</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Forge 14 Developer">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="4.5" width="18" height="12" rx="1.8" stroke="currentColor" stroke-width="1.6"/><path d="M9.5 19.5h5M12 16.5v3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Clarity 27 4K</h3>
|
||
<span class="nv-badge nv-badge--warning"><span class="nv-badge__dot"></span>4 left</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">27″ · 3840×2160 · 144 Hz</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$629</span>
|
||
<span class="product__was">$749</span>
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Clarity 27 4K">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="2.5" y="7" width="19" height="10" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M6 10.5h1M9 10.5h1M12 10.5h1M15 10.5h1M18 10.5h1M8 13.8h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Anvil TKL</h3>
|
||
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>In stock</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Tactile · hot-swap · USB-C</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$139</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Anvil TKL">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="2.5" y="7.5" width="19" height="9" rx="1.6" stroke="currentColor" stroke-width="1.6"/><circle cx="8.5" cy="12" r="2.4" stroke="currentColor" stroke-width="1.6"/><circle cx="15.5" cy="12" r="2.4" stroke="currentColor" stroke-width="1.6"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Halo X900 16 GB</h3>
|
||
<span class="nv-badge nv-badge--danger"><span class="nv-badge__dot"></span>Backorder</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Triple fan · 2.6 GHz boost</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$1,099</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Halo X900 16 GB">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="4" y="6" width="16" height="12" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 10h8M8 14h5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Vault 4 TB NVMe</h3>
|
||
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>In stock</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Gen4 · 7,400 MB/s read</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$289</span>
|
||
<span class="product__was">$329</span>
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Vault 4 TB NVMe">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="13" width="18" height="6" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M7 16.5h.01M10 16.5h.01M8 9.5a5 5 0 018 0M11 6.2a8.5 8.5 0 012 0" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Mesh Six Pro</h3>
|
||
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>In stock</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Wi-Fi 6E · 3-pack · 2.5 GbE</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$399</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Mesh Six Pro">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 15v-3a7 7 0 0114 0v3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><rect x="3" y="14" width="4" height="6" rx="1.6" stroke="currentColor" stroke-width="1.6"/><rect x="17" y="14" width="4" height="6" rx="1.6" stroke="currentColor" stroke-width="1.6"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Quiet Field 2</h3>
|
||
<span class="nv-badge nv-badge--warning"><span class="nv-badge__dot"></span>2 left</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">ANC · 40 h · dual link</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$219</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Quiet Field 2">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
<article class="nv-card nv-card--flush nv-card--interactive">
|
||
<div class="nv-bed nv-bed--quiet product__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="3" y="9" width="18" height="7" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M7 12.5h2M11 12.5h2M15.5 12.5h2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
|
||
<div class="nv-card__body" style="padding: var(--nv-space-4)">
|
||
<div class="nv-cluster nv-cluster--between" style="gap: var(--nv-space-2)">
|
||
<h3 class="nv-label">Bridge 13-in-1</h3>
|
||
<span class="nv-badge nv-badge--success"><span class="nv-badge__dot"></span>In stock</span>
|
||
</div>
|
||
<p class="nv-caption" style="margin-block-start: var(--nv-space-1)">Thunderbolt 4 · 96 W PD</p>
|
||
<div class="nv-cluster" style="margin-block-start: var(--nv-space-3)">
|
||
<span class="product__price">$179</span>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="nv-card__footer nv-card__footer--plain" style="padding: 0 var(--nv-space-4) var(--nv-space-4)">
|
||
<button class="nv-button nv-button--primary nv-button--sm nv-button--block"
|
||
data-add="Bridge 13-in-1">Add to basket</button>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<footer class="shell site-footer">
|
||
<div class="nv-cluster nv-cluster--between">
|
||
<span class="nv-caption">Northwire — an example interface built with Niveus.</span>
|
||
<span class="nv-caption">Made by Nexoniarz</span>
|
||
</div>
|
||
</footer>
|
||
</main>
|
||
|
||
<dialog class="nv-dialog nv-dialog--wide" id="basket" aria-labelledby="basket-title">
|
||
<div class="nv-dialog__header">
|
||
<div>
|
||
<h2 class="nv-dialog__title" id="basket-title">Your basket</h2>
|
||
<p class="nv-dialog__description">Three items · delivered Thursday</p>
|
||
</div>
|
||
<button class="nv-button nv-button--ghost nv-button--icon nv-dialog__close"
|
||
aria-label="Close" data-nv-close>
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="nv-dialog__body">
|
||
<div class="nv-table-frame">
|
||
<table class="nv-table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">Item</th>
|
||
<th scope="col" class="nv-table__number">Qty</th>
|
||
<th scope="col" class="nv-table__number">Price</th>
|
||
<th scope="col"><span class="nv-visually-hidden">Remove</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>
|
||
<div class="nv-cluster" style="flex-wrap: nowrap">
|
||
<span class="cart-line__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 6.5h14v9H5z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M3 18.5h18" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
|
||
<span>
|
||
Forge 14 Developer
|
||
<span class="nv-caption" style="display: block">Ryzen 9 · 32 GB</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="nv-table__number">1</td>
|
||
<td class="nv-table__number">$1,849</td>
|
||
<td class="nv-table__actions">
|
||
<button class="nv-button nv-button--ghost nv-button--icon nv-button--sm" aria-label="Remove Forge 14 Developer">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<div class="nv-cluster" style="flex-wrap: nowrap">
|
||
<span class="cart-line__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="4" y="6" width="16" height="12" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 10h8M8 14h5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
|
||
<span>
|
||
Vault 4 TB NVMe
|
||
<span class="nv-caption" style="display: block">Gen4 NVMe</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="nv-table__number">1</td>
|
||
<td class="nv-table__number">$289</td>
|
||
<td class="nv-table__actions">
|
||
<button class="nv-button nv-button--ghost nv-button--icon nv-button--sm" aria-label="Remove Vault 4 TB NVMe">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<div class="nv-cluster" style="flex-wrap: nowrap">
|
||
<span class="cart-line__art"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="2.5" y="7" width="19" height="10" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M6 10.5h1M9 10.5h1M12 10.5h1M15 10.5h1M18 10.5h1M8 13.8h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
|
||
<span>
|
||
Anvil TKL
|
||
<span class="nv-caption" style="display: block">Tactile · hot-swap</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="nv-table__number">1</td>
|
||
<td class="nv-table__number">$139</td>
|
||
<td class="nv-table__actions">
|
||
<button class="nv-button nv-button--ghost nv-button--icon nv-button--sm" aria-label="Remove Anvil TKL">
|
||
<svg class="nv-button__icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="cart-totals">
|
||
<div class="cart-totals__row nv-caption"><span>Subtotal</span><span>$2,277.00</span></div>
|
||
<div class="cart-totals__row nv-caption"><span>Delivery</span><span>Free</span></div>
|
||
<div class="cart-totals__row nv-caption"><span>VAT (20%)</span><span>$455.40</span></div>
|
||
<div class="cart-totals__row cart-totals__row--due"><span>Due today</span><span>$2,732.40</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nv-dialog__footer nv-dialog__footer--divided">
|
||
<button class="nv-button" data-nv-close>Keep shopping</button>
|
||
<button class="nv-button nv-button--primary">Checkout</button>
|
||
</div>
|
||
</dialog>
|
||
|
||
<script>
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
document.documentElement.addEventListener("nv:theme", (event) => {
|
||
try { localStorage.setItem("northwire-theme", event.detail.theme); } catch {}
|
||
});
|
||
nv.syncThemeControls(document.documentElement.getAttribute("data-theme") || "system");
|
||
|
||
/* Adding to the basket: the count changes, so it should be watchable. */
|
||
const count = document.getElementById("basket-count");
|
||
document.querySelectorAll("[data-add]").forEach((button) => {
|
||
button.addEventListener("click", () => {
|
||
nv.transition(() => {
|
||
count.textContent = String(Number(count.textContent) + 1);
|
||
});
|
||
button.textContent = "Added";
|
||
setTimeout(() => { button.textContent = "Add to basket"; }, 1200);
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll("[data-sort]").forEach((option) => {
|
||
option.addEventListener("click", () => {
|
||
document.querySelectorAll("[data-sort]").forEach((other) => {
|
||
other.setAttribute("aria-checked", String(other === option));
|
||
});
|
||
document.getElementById("sort-label").textContent = option.dataset.sort;
|
||
document.getElementById("sort-menu").hidePopover();
|
||
});
|
||
});
|
||
|
||
const topnav = document.getElementById("topnav");
|
||
const onScroll = () => topnav.toggleAttribute("data-scrolled", scrollY > 8);
|
||
addEventListener("scroll", onScroll, { passive: true });
|
||
onScroll();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|