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:
Nexoniarz 2026-10-03 17:47:03 +02:00
commit 23b8dd376d
51 changed files with 15664 additions and 0 deletions

519
examples/it-shop/index.html Normal file
View file

@ -0,0 +1,519 @@
<!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 &amp; 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>