feat: redesign — one message, one audience, light pages
The site felt old, unfocused and bloated: 11 pages, a features page of 171 bullets, a homepage of 830 words in card grids around seven mockup-style screenshots, and no single thing it wanted a visitor to understand. - Message: control instead of drift. Audience: IT departments in small and mid-sized companies. Goal: buy a licence — netOrk itself is free, the licence adds vulnerability data and image updates. - Home is under 400 words: the drift comparison of a real access point, three steps, the hardware it runs on, vulnerabilities with a licence, what else is in the box, one closing band. Features, Drivers and Roadmap are gone; their URLs redirect (router and nginx 301). - New Pricing page: the free core, Starter / Pro / Enterprise on request with the plan differences from the licence server, four questions; buttons go to the licence portal. The unit-less KB request limit is left out. - Persona pages are one template; NIS2 and Plugins are cut to half or less. Impressum and Datenschutz exist as marked placeholders; the unsupported "MIT licence" claim is gone from the footer. - Look: light paper and ink, the dark product on a stage, Inter self-hosted, split sections and ruled lists instead of cards. Four real, cropped screenshots replace eight full-window ones. - Language follows the browser until someone chooses; <html lang> is set. Scroll-to-top on navigation, a catch-all route, no dead /docs/architecture. - CLAUDE.md, DESIGN.md, PAGES.md and PRODUCT.md describe the new rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
369f66afdc
commit
f70fec496a
+59
-285
@@ -1,304 +1,78 @@
|
||||
# netOrk — Visual Identity & Design System
|
||||
# netOrk website — design system
|
||||
|
||||
This document defines the visual identity of netOrk and must be followed
|
||||
exactly when building the website. The goal is zero visual discontinuity
|
||||
between the product UI and the marketing site.
|
||||
**Light pages, dark product.** The site is calm paper with ink; the only dark
|
||||
surfaces are real screenshots of netOrk and code, set on a "stage". References:
|
||||
Linear/Vercel for precision, Tailscale/Netbird for friendliness.
|
||||
|
||||
---
|
||||
Tokens live in `tailwind.config.js`, building blocks in `src/components/ui.tsx`.
|
||||
If a page needs something that is not there, it probably needs less instead.
|
||||
|
||||
## Logo Assets
|
||||
## Colour
|
||||
|
||||
The logo file is in `public/` — use it directly, do not recreate.
|
||||
|
||||
| File | Format | Size | Use |
|
||||
|---|---|---|---|
|
||||
| `public/logo.png` | PNG | 1024×1024, RGBA | Nav logo, OG image, hero, press kit, favicon fallback |
|
||||
|
||||
### Usage in `<img>` (nav, hero)
|
||||
|
||||
```tsx
|
||||
<img src="/logo.png" alt="netOrk" className="h-8 w-8" />
|
||||
```
|
||||
|
||||
For the nav, pair it with the wordmark:
|
||||
|
||||
```tsx
|
||||
<a href="/" className="flex items-center gap-2.5">
|
||||
<img src="/logo.png" alt="" className="h-7 w-7" aria-hidden="true" />
|
||||
<span className="font-semibold text-slate-100 tracking-tight">
|
||||
net<span className="text-sky-400">Ork</span>
|
||||
</span>
|
||||
</a>
|
||||
```
|
||||
|
||||
Use `net<span class="text-sky-400">Ork</span>` consistently — the `Ork` part
|
||||
in sky-400 ties the wordmark to the accent color.
|
||||
|
||||
### `<head>` references
|
||||
|
||||
```html
|
||||
<link rel="icon" href="/logo.png" />
|
||||
<meta property="og:image" content="/logo.png" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Principle
|
||||
|
||||
**The website looks like a dark-mode dev tool, not a SaaS landing page.**
|
||||
No gradients, no floating orbs, no animated hero blobs. The aesthetic is
|
||||
deliberate, minimal, and technical — consistent with the product itself.
|
||||
|
||||
---
|
||||
|
||||
## Color Palette
|
||||
|
||||
All colors are Tailwind CSS v3 classes. Do not use hex values directly —
|
||||
always use Tailwind class names to stay consistent.
|
||||
|
||||
### Backgrounds
|
||||
|
||||
| Layer | Class | Usage |
|
||||
| Token | Value | Use |
|
||||
|---|---|---|
|
||||
| Page / outermost | `bg-slate-950` | Body, full-bleed sections |
|
||||
| Card / panel | `bg-slate-900` | Content cards, code blocks, feature boxes |
|
||||
| Elevated element | `bg-slate-800` | Hover states, dropdowns, table rows on hover |
|
||||
| Border | `border-slate-700` | Between sections, card outlines |
|
||||
| Subtle border | `border-slate-800` | Inside cards, dividers |
|
||||
| `paper` / `paper-2` | `#FAFAF9` / `#F3F3F0` | page / band |
|
||||
| `line` / `line-strong` | `#E6E6E3` / `#D4D4D0` | hairlines |
|
||||
| `ink` / `ink-soft` / `ink-muted` / `ink-faint` | `#0E1116` … `#9AA0A8` | headings / body / secondary / quiet |
|
||||
| `night` | `#020617` | screenshot and code stage — the netOrk UI's own background |
|
||||
| `accent` (`hover`, `soft`) | `#0369A1` | links, eyebrows, numbers, focus ring (5.7:1 on paper) |
|
||||
| `drift` / `sync` | `#B45309` / `#15803D` | status: deviates / matches; never the only signal |
|
||||
|
||||
### Text
|
||||
No gradients, no glow, no second accent. Buttons are ink, not accent.
|
||||
|
||||
| Role | Class |
|
||||
## Type
|
||||
|
||||
Inter Variable, self-hosted through `@fontsource-variable/inter` (bundled by
|
||||
Vite; the site makes no request to anyone else). Scale:
|
||||
|
||||
| Class | Use |
|
||||
|---|---|
|
||||
| Primary | `text-slate-100` |
|
||||
| Secondary / muted | `text-slate-400` |
|
||||
| Tertiary / placeholder | `text-slate-500` |
|
||||
| Accent (interactive) | `text-sky-400` |
|
||||
| Danger | `text-red-400` |
|
||||
| `text-display` | the home headline only |
|
||||
| `text-h1` | one per page, in `PageHeader` |
|
||||
| `text-h2` | section headings |
|
||||
| `text-h3` | row terms, plan names |
|
||||
| `text-lead` | the paragraph under a heading |
|
||||
| `text-eyebrow` + `uppercase text-accent` | the small line above a heading |
|
||||
|
||||
### Accent (Interactive / CTA)
|
||||
Everything is left-aligned. Headings balance and hyphenate (`<html lang>` is
|
||||
set per language). Text columns stay within `max-w-measure` (38rem).
|
||||
|
||||
| State | Class |
|
||||
|---|---|
|
||||
| Default button | `bg-sky-600 text-white` |
|
||||
| Hover | `hover:bg-sky-500` |
|
||||
| Link / inline | `text-sky-400 hover:text-sky-300` |
|
||||
| Active indicator | `text-sky-400` |
|
||||
| Focus ring | `focus:ring-sky-500` |
|
||||
## Layout
|
||||
|
||||
### Semantic Colors
|
||||
- Container `max-w-page` (72rem), `px-5 sm:px-8`. Sections `py-20 md:py-28`.
|
||||
- **Split**: heading on the left five columns, content on the right. The
|
||||
default section.
|
||||
- **RuleList**: rows divided by hairlines, term and body; one or two columns.
|
||||
This replaces every card grid.
|
||||
- **Steps**: numbered rows (`01`, `02`, `03`) in mono accent.
|
||||
- **Band**: `bg-paper-2` with hairlines, for the hardware strip and the closing
|
||||
call to action (`CtaBand`).
|
||||
- **Stage**: `bg-night`, `rounded-2xl`, `shadow-stage` — screenshots (`Shot`)
|
||||
and code (`CodeBlock`).
|
||||
|
||||
| Meaning | Color |
|
||||
|---|---|
|
||||
| Success / active | `text-green-400`, `bg-green-500/20` |
|
||||
| Warning / caution | `text-yellow-400`, `bg-yellow-500/20` |
|
||||
| Danger / error | `text-red-400`, `bg-red-500/20` |
|
||||
| Info / neutral | `text-blue-400`, `bg-blue-500/20` |
|
||||
Not used: icon tiles, pills for names, cards, centred text blocks, fake browser
|
||||
windows, emoji. Motion is `transition-colors` only.
|
||||
|
||||
### Status Badge Pattern
|
||||
## Screenshots
|
||||
|
||||
```tsx
|
||||
// Active / online
|
||||
<span className="text-xs font-medium px-2 py-0.5 rounded-full bg-green-500/20 text-green-400">
|
||||
active
|
||||
</span>
|
||||
Only real screenshots of netOrk, from the anonymised demo copy
|
||||
(`scripts/demo`), taken by `scripts/screenshots/capture.py` and published as
|
||||
WebP in `public/screenshots/`. Their sizes are written to
|
||||
`src/data/screenshots.json`, which `Shot` reads.
|
||||
|
||||
// Offline
|
||||
<span className="text-xs font-medium px-2 py-0.5 rounded-full bg-red-500/20 text-red-400">
|
||||
offline
|
||||
</span>
|
||||
- At most four different images on the site. Each is cropped to the one thing
|
||||
the text next to it talks about (`clip` or `element` in `shots.py`).
|
||||
- Cropping shows less of a real screen; it never changes what is on it. No
|
||||
edited data, no clicks that fake a state, no mockups.
|
||||
- A crop that does not read on a phone gets a `-narrow` variant (`Shot narrow=`).
|
||||
- Every image has an alt text and a caption that says what is true in it.
|
||||
|
||||
// Warning
|
||||
<span className="text-xs font-medium px-2 py-0.5 rounded-full bg-yellow-500/20 text-yellow-400">
|
||||
warning
|
||||
</span>
|
||||
```
|
||||
## Wordmark
|
||||
|
||||
---
|
||||
Text only: `net<span class="text-accent">Ork</span>` in ink. `public/logo.png`
|
||||
is the favicon and OG image; it does not sit well on a light background.
|
||||
|
||||
## Typography
|
||||
## Glossary marks
|
||||
|
||||
Font stack: Tailwind default sans-serif (`font-sans`). **No Google Fonts.**
|
||||
The product uses system fonts; the website must match.
|
||||
|
||||
| Element | Classes |
|
||||
|---|---|
|
||||
| Hero heading | `text-4xl md:text-6xl font-bold text-slate-100 leading-tight` |
|
||||
| Section heading | `text-2xl md:text-3xl font-bold text-slate-100` |
|
||||
| Subsection heading | `text-xl font-semibold text-slate-200` |
|
||||
| Body text | `text-base text-slate-400 leading-relaxed` |
|
||||
| Small / label | `text-sm text-slate-400` |
|
||||
| Tiny / tag | `text-xs font-medium text-slate-500` |
|
||||
| Code / monospace | `font-mono text-sky-400` |
|
||||
| Accent text | `text-sky-400` |
|
||||
|
||||
---
|
||||
|
||||
## Spacing & Layout
|
||||
|
||||
- Max content width: `max-w-7xl mx-auto px-6`
|
||||
- Section padding: `py-24` (desktop), `py-16` (mobile)
|
||||
- Card padding: `p-6`
|
||||
- Gap between grid items: `gap-6` or `gap-8`
|
||||
- All layouts are mobile-first; use `md:` and `lg:` breakpoints.
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
### Primary CTA Button
|
||||
|
||||
```tsx
|
||||
<a
|
||||
href="/docs/getting-started"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg
|
||||
bg-sky-600 hover:bg-sky-500 text-white font-medium
|
||||
transition-colors"
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
```
|
||||
|
||||
### Secondary / Ghost Button
|
||||
|
||||
```tsx
|
||||
<a
|
||||
href="/features"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg
|
||||
border border-slate-700 hover:border-slate-500
|
||||
text-slate-300 hover:text-slate-100
|
||||
transition-colors"
|
||||
>
|
||||
See all features
|
||||
</a>
|
||||
```
|
||||
|
||||
### Feature Card
|
||||
|
||||
```tsx
|
||||
<div className="rounded-xl border border-slate-800 bg-slate-900 p-6">
|
||||
<div className="mb-4 flex h-10 w-10 items-center justify-center
|
||||
rounded-lg bg-sky-600/10">
|
||||
{/* Heroicon SVG, className="h-5 w-5 text-sky-400" */}
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-semibold text-slate-100">Feature name</h3>
|
||||
<p className="text-sm text-slate-400 leading-relaxed">
|
||||
Description of the feature in one to three sentences.
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Screenshot Frame
|
||||
|
||||
App screenshots must be wrapped in this frame to integrate naturally
|
||||
against the dark background:
|
||||
|
||||
```tsx
|
||||
<div className="rounded-xl border border-slate-700 overflow-hidden shadow-2xl">
|
||||
<img src="/screenshots/devices.webp" alt="Device inventory" className="w-full" />
|
||||
</div>
|
||||
```
|
||||
|
||||
**Only real screenshots of the running application.** No JSX mockups or
|
||||
drawn imitations of the UI. They come from an anonymized demo copy of a real
|
||||
installation and are taken with `scripts/screenshots/capture.py` (see
|
||||
`scripts/demo/README.md`), published as WebP in `public/screenshots/`.
|
||||
`Screenshot` in `src/pages/Home.tsx` is the frame.
|
||||
|
||||
Optionally add a browser chrome header above the image:
|
||||
|
||||
```tsx
|
||||
<div className="flex items-center gap-1.5 border-b border-slate-700 bg-slate-800 px-4 py-2.5">
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-red-500/70" />
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-yellow-500/70" />
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-green-500/70" />
|
||||
<span className="ml-4 text-xs text-slate-500 font-mono">netork / devices</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Code Block
|
||||
|
||||
```tsx
|
||||
<pre className="rounded-xl border border-slate-800 bg-slate-900 p-6
|
||||
font-mono text-sm text-slate-300 overflow-x-auto">
|
||||
<code>{`bash scripts/deploy.sh 192.168.1.1`}</code>
|
||||
</pre>
|
||||
```
|
||||
|
||||
### Driver / Integration Badge
|
||||
|
||||
```tsx
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full
|
||||
border border-slate-700 bg-slate-900
|
||||
text-xs font-medium text-slate-300">
|
||||
OpenWRT
|
||||
</span>
|
||||
```
|
||||
|
||||
### Section Divider
|
||||
|
||||
```tsx
|
||||
<div className="border-t border-slate-800" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation
|
||||
|
||||
- Sticky top nav: `sticky top-0 z-10 bg-slate-900/80 backdrop-blur
|
||||
border-b border-slate-800`
|
||||
- Logo: left-aligned. Product name in `font-semibold text-slate-100`,
|
||||
optionally prefixed with a small icon.
|
||||
- Nav links: `text-sm text-slate-400 hover:text-slate-100 transition-colors`
|
||||
- Active link: `text-slate-100`
|
||||
- CTA in nav: small primary button `px-4 py-1.5 text-sm`
|
||||
|
||||
---
|
||||
|
||||
## Animations & Transitions
|
||||
|
||||
- **Hover states:** always `transition-colors` (not `transition-all`).
|
||||
- **No JavaScript animations** on initial page load — no entrance animations,
|
||||
no scroll-triggered reveals via IntersectionObserver.
|
||||
- Scroll behavior: `scroll-smooth` on `<html>` for anchor links.
|
||||
- No parallax, no floating elements, no auto-playing videos.
|
||||
|
||||
---
|
||||
|
||||
## Logo / Wordmark
|
||||
|
||||
The netOrk wordmark uses the following convention in the product:
|
||||
- Lowercase `n`, uppercase `O`: **netOrk**
|
||||
- Monospace context: `font-mono text-sky-400`
|
||||
- Heading context: `font-bold text-slate-100` with `Ork` potentially in
|
||||
`text-sky-400` if desired for emphasis
|
||||
|
||||
---
|
||||
|
||||
## Iconography
|
||||
|
||||
Use Heroicons (inline SVG). Sizes:
|
||||
- Feature card icons: `h-5 w-5`
|
||||
- Nav / button icons: `h-4 w-4`
|
||||
- Hero / large decorative: `h-8 w-8` or `h-10 w-10`
|
||||
|
||||
All icons: `text-sky-400` in feature contexts, `text-slate-400` in
|
||||
secondary/muted contexts.
|
||||
|
||||
---
|
||||
|
||||
## tailwind.config.js
|
||||
|
||||
No custom theme extensions needed. The default Tailwind v3 slate + sky
|
||||
palette covers everything. The config only needs content paths:
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
Terms from `src/glossary/terms.ts` get a dotted underline and a dark tooltip
|
||||
(`linkify`). Not on the homepage — short copy there stays unmarked.
|
||||
|
||||
Reference in New Issue
Block a user