redraw the desk as cartoon skeuomorphism: CSS-only materials (no textures), light cream desk, per-item hues, cork board, receipt, scoreboard
This commit is contained in:
parent
a71cfb1731
commit
1880a5b0db
14 changed files with 1165 additions and 989 deletions
50
README.md
50
README.md
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
> The source code for my personal website, **[dvorakmortonxd.dev](https://dvorakmortonxd.dev)**.
|
> The source code for my personal website, **[dvorakmortonxd.dev](https://dvorakmortonxd.dev)**.
|
||||||
|
|
||||||
It's a **skeuomorphic personal site**: a near-black desk with anodised hardware, machined
|
It's a **cartoon-skeuomorphic personal site**: a warm cream desk with chunky, brightly coloured
|
||||||
aluminium, clean printed labels and a single orange accent, with the whole KDE desktop
|
hardware resting on it, drawn entirely in CSS — no textures, no images — with the whole KDE
|
||||||
simulation kept behind a physical power switch. Two modes, one content set:
|
desktop simulation kept behind a physical power switch. Two modes, one content set:
|
||||||
|
|
||||||
- **Web** (default) — the desk. A monitor carries the headline with a mechanical keyboard in
|
- **Web** (default) — the desk. A monitor carries the headline with a candy-keycap keyboard in
|
||||||
front of it and a small rack beside it; services are 1U rack units with health LEDs, the 18
|
front of it and a mini rack beside it; services are 1U rack units with coloured strips and
|
||||||
coding projects are labelled drives in a drawer, the machines are barcoded asset labels on a
|
health LEDs, the 18 coding projects are labelled drives in a drawer, the machines are cards
|
||||||
pegboard, the contribution heatmap is printed on a label-printer sheet, friends are business
|
pinned to a cork board, the contribution heatmap is printed on a receipt, friends are business
|
||||||
cards, the gym log is a clipboard and the socials are a patch panel. Textures are CC0 — see
|
cards, the gym numbers are a scoreboard and the socials are a patch panel with coloured ports.
|
||||||
[`/credits`](https://dvorakmortonxd.dev/credits).
|
[`/credits`](https://dvorakmortonxd.dev/credits) lists the type and nothing else — there are no
|
||||||
|
asset licences because there are no assets.
|
||||||
- **Desktop mode** — the KDE Plasma / Windows-style **desktop simulation**: draggable
|
- **Desktop mode** — the KDE Plasma / Windows-style **desktop simulation**: draggable
|
||||||
snap-to-grid icons, resizable/floating windows, a Start menu, taskbar, live clock and a
|
snap-to-grid icons, resizable/floating windows, a Start menu, taskbar, live clock and a
|
||||||
working in-browser terminal (Alacritty). Reached from the power switch and hidden on phones.
|
working in-browser terminal (Alacritty). Reached from the power switch and hidden on phones.
|
||||||
|
|
@ -24,10 +25,12 @@ heatmap**, gym SBD and links to friends' sites.
|
||||||
|
|
||||||
- **Web theme is the landing view** — the site always opens on the desk; the theme is
|
- **Web theme is the landing view** — the site always opens on the desk; the theme is
|
||||||
deliberately not persisted, so a reload never drops you into the desktop.
|
deliberately not persisted, so a reload never drops you into the desktop.
|
||||||
- **Skeuomorphic materials, modern finish** — near-black desk, dark anodised faceplates,
|
- **CSS-only skeuomorphism** — every surface is a flat gradient with a thick bottom edge, a soft
|
||||||
machined aluminium plaques, matte printed labels and mint/orange LEDs, all CSS over five CC0
|
cast shadow and `color-mix()` tints: slate chassis, cream plastic, paper, cork, black glass,
|
||||||
textures. Every section is a physical object with a contact shadow; nothing is pure white on
|
LEDs, screws, pushpins. The web theme ships no images at all.
|
||||||
black or grey on black.
|
- **Not afraid of colour** — a hue per item (orange / teal / blue / lilac / mint / coral /
|
||||||
|
yellow) cycles through every list, so the desk reads as separate pieces of hardware instead of
|
||||||
|
one grey wall.
|
||||||
- **Desktop mode (desktop-only)** — a physical power switch in the top rail (and in the footer)
|
- **Desktop mode (desktop-only)** — a physical power switch in the top rail (and in the footer)
|
||||||
flips to the KDE-style desktop for the session. Phones never get it: the switch is hidden, so
|
flips to the KDE-style desktop for the session. Phones never get it: the switch is hidden, so
|
||||||
the old iOS-style phone home screen is gone.
|
the old iOS-style phone home screen is gone.
|
||||||
|
|
@ -67,15 +70,16 @@ heatmap**, gym SBD and links to friends' sites.
|
||||||
- **Framework:** Next.js 16 (App Router, Turbopack)
|
- **Framework:** Next.js 16 (App Router, Turbopack)
|
||||||
- **Language:** TypeScript, React 19 (client components everywhere interactivity lives)
|
- **Language:** TypeScript, React 19 (client components everywhere interactivity lives)
|
||||||
- **Styling:** `src/app/globals.css` holds the palette tokens, base styles, the Tailwind v4
|
- **Styling:** `src/app/globals.css` holds the palette tokens, base styles, the Tailwind v4
|
||||||
`@theme`/`@utility` classes and the scroll reveals. `src/app/web.css` is the material system
|
`@theme`/`@utility` classes and the scroll reveals. `src/app/web.css` is the whole desk: the
|
||||||
(near-black desk, anodised metal, machined aluminium, printed labels, black glass, LEDs, rack
|
materials (slate chassis, cream plastic, paper, cork, black glass), the details (screws, LEDs,
|
||||||
rails, rocker switch) and the object layouts. `page.css` keeps the shared component styles the
|
pushpins, rack rails, barcode, torn receipt edge) and every object layout, all CSS with
|
||||||
desktop apps still use, and `skeuo.css` is the KDE desktop.
|
`color-mix()` for tints and shades. `page.css` keeps the shared component styles the desktop
|
||||||
- **Fonts:** IBM Plex Sans/Mono for screens, machine labels and headings; Newsreader for things
|
apps still use, and `skeuo.css` is the KDE desktop.
|
||||||
printed on paper; via `next/font/google`. The desktop keeps its system sans stack.
|
- **Fonts:** Nunito for hardware labels and headings, Newsreader for anything printed on paper,
|
||||||
- **Textures:** five CC0 materials from ambientCG — `desk` (near-black wood), `metal`, `paper`,
|
IBM Plex Sans/Mono for body copy and terminal text — all via `next/font/google`. The desktop
|
||||||
`carbon`, `plastic` — downscaled to 512 px in `public/textures`. Attribution is on `/credits`.
|
keeps its system sans stack.
|
||||||
- **Version:** `3.3.0` (see `package.json`)
|
- **Assets:** the web theme ships **no images**. The desktop keeps its own wallpaper and icons.
|
||||||
|
- **Version:** `4.0.0` (see `package.json`)
|
||||||
|
|
||||||
## 🚀 Running Locally
|
## 🚀 Running Locally
|
||||||
|
|
||||||
|
|
@ -182,7 +186,7 @@ src/app/
|
||||||
page.css → shared component styles still used by the desktop apps
|
page.css → shared component styles still used by the desktop apps
|
||||||
skeuo.css → KDE desktop styles
|
skeuo.css → KDE desktop styles
|
||||||
data.ts → socials, codingProjects, techProjects, friends, wallpapers
|
data.ts → socials, codingProjects, techProjects, friends, wallpapers
|
||||||
credits/page.tsx → credit page for the textures and fonts
|
credits/page.tsx → credit page (type only — the desk ships no assets)
|
||||||
revival-inc/page.tsx → embeds the bundled game at /public/revival-inc/index.html
|
revival-inc/page.tsx → embeds the bundled game at /public/revival-inc/index.html
|
||||||
api/
|
api/
|
||||||
heatmap/route.ts → contribution heatmap proxy
|
heatmap/route.ts → contribution heatmap proxy
|
||||||
|
|
|
||||||
46
agents.md
46
agents.md
|
|
@ -34,20 +34,24 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a
|
||||||
## What this project is
|
## What this project is
|
||||||
|
|
||||||
Personal portfolio site for **dvorakmortonxd.dev**. The default view is a
|
Personal portfolio site for **dvorakmortonxd.dev**. The default view is a
|
||||||
**skeuomorphic desk**: a near-black surface with a monitor, mechanical keyboard,
|
**cartoon-skeuomorphic desk**: a warm cream surface with a monitor, candy-keycap
|
||||||
rack, drive drawer, pegboard of asset labels, label-printer report, business
|
keyboard, mini rack, drive drawer, cork board of pinned cards, printed receipt,
|
||||||
cards, clipboard and patch panel resting on it. A physical power switch flips to
|
business cards, scoreboard and patch panel resting on it — all drawn in CSS, with
|
||||||
a **KDE Plasma desktop simulation**. Phones only ever get the desk.
|
no image assets. A physical power switch flips to a **KDE Plasma desktop
|
||||||
|
simulation**. Phones only ever get the desk.
|
||||||
|
|
||||||
- Framework: **Next.js 16** (App Router, Turbopack), **React 19**, TypeScript.
|
- Framework: **Next.js 16** (App Router, Turbopack), **React 19**, TypeScript.
|
||||||
- Styling: **Tailwind v4** for layout plus **`web.css`**, the material system
|
- Styling: **Tailwind v4** for layout plus **`web.css`**, which is the whole desk
|
||||||
(near-black desk, anodised metal, machined aluminium, printed labels, black
|
— materials (slate chassis, cream plastic, paper, cork, black glass), details
|
||||||
glass, LEDs, rack rails, rocker switch) over five CC0 textures in
|
(screws, LEDs, pushpins, rails, barcode, torn receipt edge) and object layouts,
|
||||||
`public/textures` (credited on `/credits`). `globals.css` holds the tokens,
|
all CSS with `color-mix()` tints. `globals.css` holds the tokens, base styles
|
||||||
base styles and the reveals; `skeuo.css` and `page.css` are the desktop and
|
and the reveals; `skeuo.css` and `page.css` are the desktop and the shared
|
||||||
the shared desktop-app styles.
|
desktop-app styles. **The web theme ships no images.**
|
||||||
|
- Hue system: list items take a `--tone` custom property from a shared palette
|
||||||
|
(orange, teal, blue, lilac, mint, coral, yellow), set inline, so objects are
|
||||||
|
colourful without per-item CSS.
|
||||||
- Path alias: `@/*` → `src/*`.
|
- Path alias: `@/*` → `src/*`.
|
||||||
- Version: `3.3.0` (see `package.json`).
|
- Version: `4.0.0` (see `package.json`).
|
||||||
|
|
||||||
### Scripts
|
### Scripts
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -85,7 +89,7 @@ src/app/
|
||||||
page.css → shared component styles used by the desktop apps
|
page.css → shared component styles used by the desktop apps
|
||||||
skeuo.css → KDE Plasma desktop styles
|
skeuo.css → KDE Plasma desktop styles
|
||||||
data.ts → socials, codingProjects, techProjects, friends, wallpapers
|
data.ts → socials, codingProjects, techProjects, friends, wallpapers
|
||||||
credits/page.tsx → credits for the CC0 textures and fonts
|
credits/page.tsx → credits (type only — the desk ships no assets)
|
||||||
revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html
|
revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html
|
||||||
api/
|
api/
|
||||||
heatmap/route.ts → proxy for git.dvorakmortonxd.dev contribution heatmap
|
heatmap/route.ts → proxy for git.dvorakmortonxd.dev contribution heatmap
|
||||||
|
|
@ -124,14 +128,14 @@ scripts/
|
||||||
desktop is up so its scroll position survives; the desktop mounts on demand
|
desktop is up so its scroll position survives; the desktop mounts on demand
|
||||||
(phones can never reach `skeuo`).
|
(phones can never reach `skeuo`).
|
||||||
|
|
||||||
Modes: `web` = the skeuomorphic desk (default/landing), `skeuo` = KDE Plasma
|
Modes: `web` = the cartoon-skeuomorphic desk (default/landing), `skeuo` = KDE
|
||||||
desktop.
|
Plasma desktop.
|
||||||
|
|
||||||
The desk uses **five CC0 textures** from ambientCG (`public/textures`: near-black
|
The desk ships **no image assets** — every surface is CSS. The only external
|
||||||
desk wood, metal, paper, carbon, plastic), downscaled to 512 px and credited on
|
visual files are the social SVGs, used as monochrome CSS masks (`.social-mask`);
|
||||||
`/credits`. The social SVGs are used as monochrome CSS masks (`.social-mask`);
|
|
||||||
`linkedin.svg` is a filled badge that masks to a plain square, so `web-ui.tsx`
|
`linkedin.svg` is a filled badge that masks to a plain square, so `web-ui.tsx`
|
||||||
renders an inline mark for it instead. The desktop still uses the image assets.
|
renders an inline mark for it instead. The desktop still uses its own wallpaper
|
||||||
|
and icons.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -242,6 +246,12 @@ not free placement. Don't confuse the two.
|
||||||
- Angles and offsets that hover states need to override live in CSS custom
|
- Angles and offsets that hover states need to override live in CSS custom
|
||||||
properties (e.g. `--tilt` on `.w-drive`), never in inline `style` transforms —
|
properties (e.g. `--tilt` on `.w-drive`), never in inline `style` transforms —
|
||||||
an inline transform wins over `:hover`.
|
an inline transform wins over `:hover`.
|
||||||
|
- **The web desk is CSS-only by design.** No textures, no images, no `url()`
|
||||||
|
beyond the social masks. Build a surface from a flat gradient plus a thick
|
||||||
|
bottom edge (`0 5px 0 <edge-colour>`) and a soft cast shadow, and shade with
|
||||||
|
`color-mix(in srgb, var(--tone) N%, …)` instead of adding new hues to the
|
||||||
|
stylesheet. New list items take a `--tone` from the shared `TONES` palette in
|
||||||
|
`WebSite.tsx` / `web-ui.tsx` rather than hard-coding a colour.
|
||||||
- The desk reveals blocks with `.reveal` (see `Reveal` in `web-ui.tsx`): hidden by
|
- The desk reveals blocks with `.reveal` (see `Reveal` in `web-ui.tsx`): hidden by
|
||||||
default, `.is-in` shows. On load the first screenful cascades top-to-bottom
|
default, `.is-in` shows. On load the first screenful cascades top-to-bottom
|
||||||
(90ms per block, in DOM order); the rest reveal when they scroll in. **Wrap
|
(90ms per block, in DOM order); the rest reveal when they scroll in. **Wrap
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dvorakmortonxd",
|
"name": "dvorakmortonxd",
|
||||||
"version": "3.3.0",
|
"version": "4.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev-server.mjs",
|
"dev": "node scripts/dev-server.mjs",
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB |
|
|
@ -27,6 +27,17 @@ const NAV = [
|
||||||
{ href: "#contact", label: "contact" },
|
{ href: "#contact", label: "contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** One hue per item, so every list reads as separate pieces of hardware. */
|
||||||
|
const TONES = [
|
||||||
|
"var(--orange)",
|
||||||
|
"var(--teal)",
|
||||||
|
"var(--blue)",
|
||||||
|
"var(--lilac)",
|
||||||
|
"var(--mint)",
|
||||||
|
"var(--coral)",
|
||||||
|
"var(--yellow)",
|
||||||
|
];
|
||||||
|
|
||||||
const SERVICES: {
|
const SERVICES: {
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
|
|
@ -72,11 +83,11 @@ export default function WebSite() {
|
||||||
return (
|
return (
|
||||||
<div className="w-page">
|
<div className="w-page">
|
||||||
{/* -------------------------------------------------------------- rail */}
|
{/* -------------------------------------------------------------- rail */}
|
||||||
<header className="w-rail w-mt-metal-flush" style={{ borderRadius: 0 }}>
|
<header className="w-rail">
|
||||||
<div className="mx-auto flex h-16 w-full max-w-[1120px] items-center gap-5 px-5 sm:px-8">
|
<div className="mx-auto flex h-16 w-full max-w-[1120px] items-center gap-5 px-5 sm:px-8">
|
||||||
<a href="#top" className="flex shrink-0 items-center gap-2.5" aria-label="Back to top">
|
<a href="#top" className="flex shrink-0 items-center gap-2.5" aria-label="Back to top">
|
||||||
<Led kind="on" pulse slow />
|
<Led kind="on" pulse slow />
|
||||||
<span className="w-wordmark w-engrave">dvorakmortonxd</span>
|
<span className="w-wordmark">dvorakmortonxd</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav className="ml-auto hidden items-center gap-6 md:flex" aria-label="Main">
|
<nav className="ml-auto hidden items-center gap-6 md:flex" aria-label="Main">
|
||||||
|
|
@ -95,7 +106,7 @@ export default function WebSite() {
|
||||||
className="desktop-toggle flex items-center gap-3"
|
className="desktop-toggle flex items-center gap-3"
|
||||||
title="Flip to Desktop mode"
|
title="Flip to Desktop mode"
|
||||||
>
|
>
|
||||||
<span className="w-mono hidden text-[0.6rem] tracking-[0.2em] text-[var(--ftext-faint)] sm:inline">
|
<span className="w-mono hidden text-[0.6rem] tracking-[0.2em] text-[var(--ink-soft)] sm:inline">
|
||||||
DESKTOP
|
DESKTOP
|
||||||
</span>
|
</span>
|
||||||
<span className="w-rocker">
|
<span className="w-rocker">
|
||||||
|
|
@ -110,7 +121,7 @@ export default function WebSite() {
|
||||||
onClick={() => setMenuOpen((v) => !v)}
|
onClick={() => setMenuOpen((v) => !v)}
|
||||||
aria-expanded={menuOpen}
|
aria-expanded={menuOpen}
|
||||||
aria-label={menuOpen ? "Close menu" : "Open menu"}
|
aria-label={menuOpen ? "Close menu" : "Open menu"}
|
||||||
className="w-keybtn !px-3 !py-2"
|
className="w-keybtn"
|
||||||
>
|
>
|
||||||
{menuOpen ? "close" : "menu"}
|
{menuOpen ? "close" : "menu"}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -120,7 +131,7 @@ export default function WebSite() {
|
||||||
|
|
||||||
{menuOpen ? (
|
{menuOpen ? (
|
||||||
<nav
|
<nav
|
||||||
className="w-mt-metal-flush mx-auto mb-3 flex w-[calc(100%-2.5rem)] max-w-[1120px] flex-col gap-1 rounded-lg p-3 md:hidden"
|
className="w-plastic mx-auto mb-3 flex w-[calc(100%-2.5rem)] max-w-[1120px] flex-col gap-1 p-3 md:hidden"
|
||||||
aria-label="Main"
|
aria-label="Main"
|
||||||
>
|
>
|
||||||
{NAV.map((item) => (
|
{NAV.map((item) => (
|
||||||
|
|
@ -128,7 +139,7 @@ export default function WebSite() {
|
||||||
key={item.href}
|
key={item.href}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
onClick={() => setMenuOpen(false)}
|
onClick={() => setMenuOpen(false)}
|
||||||
className="w-mono border-b border-[rgba(255,226,180,0.07)] py-3 text-[0.78rem] text-[var(--ftext-dim)] last:border-b-0"
|
className="w-display border-b-2 border-dashed border-[var(--plastic-edge)] py-3 text-[0.9rem] text-[var(--plastic-ink)] last:border-b-0"
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -149,18 +160,18 @@ export default function WebSite() {
|
||||||
<span className="a">martin@dvorakmortonxd</span>:~$ whoami
|
<span className="a">martin@dvorakmortonxd</span>:~$ whoami
|
||||||
<span className="w-caret ml-2" />
|
<span className="w-caret ml-2" />
|
||||||
</p>
|
</p>
|
||||||
<h1 className="w-display mt-5 text-[clamp(1.8rem,4.1vw,2.75rem)] text-[var(--ftext)]">
|
<h1 className="w-display mt-5 text-[clamp(1.8rem,4.1vw,2.75rem)] text-[var(--glass-ink)]">
|
||||||
My own little internet — one rack, zero subscriptions.
|
My own little internet — one rack, zero subscriptions.
|
||||||
</h1>
|
</h1>
|
||||||
<p className="w-prose mt-5 max-w-[54ch] text-[var(--ftext-dim)]">
|
<p className="w-prose mt-5 max-w-[54ch] text-[var(--glass-dim)]">
|
||||||
I’m Martin Dvořák. Git, media, music, an AI box and a Minecraft server all run on a
|
I’m Martin Dvořák. Git, media, music, an AI box and a Minecraft server all run on a
|
||||||
Proxmox machine in my room — plus the hardware graveyard I keep rescuing from e-waste.
|
Proxmox machine in my room — plus the hardware graveyard I keep rescuing from e-waste.
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-7 flex flex-wrap gap-x-8 gap-y-3">
|
<p className="mt-7 flex flex-wrap gap-3">
|
||||||
<a href="#services" className="w-cmd">
|
<a href="#services" className="w-cmd">
|
||||||
$ open rack --services
|
$ open rack --services
|
||||||
</a>
|
</a>
|
||||||
<a href="#projects" className="w-cmd">
|
<a href="#projects" className="w-cmd w-cmd--quiet">
|
||||||
$ open drawer --projects
|
$ open drawer --projects
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -175,10 +186,10 @@ export default function WebSite() {
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<MiniRack />
|
<MiniRack />
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<Plaque value={String(SERVICES.length)} label="services" />
|
<Plaque value={String(SERVICES.length)} label="services" tone="var(--orange)" />
|
||||||
<Plaque value={String(codingProjects.length)} label="projects" />
|
<Plaque value={String(codingProjects.length)} label="projects" tone="var(--teal)" />
|
||||||
<Plaque value={String(techProjects.length)} label="machines" />
|
<Plaque value={String(techProjects.length)} label="machines" tone="var(--blue)" />
|
||||||
<Plaque value="401" label="kg best SBD" />
|
<Plaque value="401" label="kg best SBD" tone="var(--coral)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
@ -193,7 +204,7 @@ export default function WebSite() {
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.55fr)_minmax(0,1fr)] lg:items-start">
|
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.55fr)_minmax(0,1fr)] lg:items-start">
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<article className="w-mt-paper w-sheet">
|
<article className="w-paper w-sheet">
|
||||||
<div className="w-sheet__head">
|
<div className="w-sheet__head">
|
||||||
<span>manifest.txt</span>
|
<span>manifest.txt</span>
|
||||||
<span className="ml-auto">printed on the desk</span>
|
<span className="ml-auto">printed on the desk</span>
|
||||||
|
|
@ -224,15 +235,13 @@ export default function WebSite() {
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<Reveal delay={150}>
|
<Reveal delay={150}>
|
||||||
<aside className="w-mt-paper w-cardindex lg:mt-12">
|
<aside className="w-paper w-cardindex lg:mt-14" style={{ "--tone": "var(--teal)" } as CSSProperties}>
|
||||||
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[var(--ink-soft)]">
|
<p className="w-kicker">index card</p>
|
||||||
INDEX CARD
|
<p className="w-serif mt-3 text-[1.2rem] text-[var(--ink)]">
|
||||||
</p>
|
|
||||||
<p className="w-serif mt-3 text-[1.15rem] text-[var(--ink)]">
|
|
||||||
“If it has a fan, I’ll find a use for it.”
|
“If it has a fan, I’ll find a use for it.”
|
||||||
</p>
|
</p>
|
||||||
<div className="w-rule my-4" />
|
<div className="w-rule my-4" />
|
||||||
<ul className="w-mono space-y-2 text-[0.66rem] text-[var(--ink-soft)]">
|
<ul className="w-mono space-y-2 text-[0.68rem] text-[var(--ink-soft)]">
|
||||||
<li>school · sssvt.cz</li>
|
<li>school · sssvt.cz</li>
|
||||||
<li>gym · PPL 3–5×/week</li>
|
<li>gym · PPL 3–5×/week</li>
|
||||||
<li>games · Terraria, Minecraft</li>
|
<li>games · Terraria, Minecraft</li>
|
||||||
|
|
@ -251,14 +260,13 @@ export default function WebSite() {
|
||||||
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)] lg:items-start">
|
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)] lg:items-start">
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-note">
|
<div className="w-note">
|
||||||
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#6b7075]">
|
<span className="w-pin" aria-hidden="true" />
|
||||||
NOW.TXT · UPDATED 09/2026
|
<p className="w-kicker !text-[#8a7420]">now.txt · updated 09/2026</p>
|
||||||
</p>
|
<p className="w-prose mt-4 text-[#5c4a1f]">
|
||||||
<p className="w-prose mt-4 text-[#34383b]">
|
|
||||||
I’m hosting an SMP — message me on Discord if you want in. I’m also cooking most
|
I’m hosting an SMP — message me on Discord if you want in. I’m also cooking most
|
||||||
days, bulking through the non-summer seasons.
|
days, bulking through the non-summer seasons.
|
||||||
</p>
|
</p>
|
||||||
<p className="w-prose mt-3 text-[#34383b]">
|
<p className="w-prose mt-3 text-[#5c4a1f]">
|
||||||
Me and my friend{" "}
|
Me and my friend{" "}
|
||||||
<a className="w-link" href="https://skopec.dev" target="_blank" rel="noopener noreferrer">
|
<a className="w-link" href="https://skopec.dev" target="_blank" rel="noopener noreferrer">
|
||||||
Pepa
|
Pepa
|
||||||
|
|
@ -278,24 +286,25 @@ export default function WebSite() {
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<Reveal delay={140}>
|
<Reveal delay={140}>
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="w-mt-paper w-cardindex w-cardindex--alt">
|
<div className="w-paper w-cardindex w-cardindex--alt" style={{ "--tone": "var(--coral)" } as CSSProperties}>
|
||||||
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[var(--ink-soft)]">
|
<p className="w-kicker">bench status</p>
|
||||||
BENCH STATUS
|
<ul className="w-display mt-4 space-y-2.5 text-[0.82rem] text-[var(--ink)]">
|
||||||
</p>
|
<li className="flex items-center gap-2.5">
|
||||||
<ul className="w-mono mt-3 space-y-2 text-[0.7rem] text-[var(--ink)]">
|
|
||||||
<li className="flex items-center gap-2">
|
|
||||||
<Led kind="on" /> smp · accepting players
|
<Led kind="on" /> smp · accepting players
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2.5">
|
||||||
<Led kind="amber" pulse /> maturita.c · in development
|
<Led kind="amber" pulse /> maturita.c · in development
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2.5">
|
||||||
<Led kind="on" /> gym · PPL 3–5×/week
|
<Led kind="on" /> gym · PPL 3–5×/week
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<i className="w-pen ml-6 w-44" aria-hidden="true" />
|
<div className="flex items-end gap-6">
|
||||||
|
<i className="w-pen mb-3 w-40" aria-hidden="true" />
|
||||||
|
<span className="w-mug mr-6" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -307,11 +316,13 @@ export default function WebSite() {
|
||||||
<SectionHead index="03" label="rack" title="Six machines, one shelf" />
|
<SectionHead index="03" label="rack" title="Six machines, one shelf" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-mt-metal w-rack">
|
<div className="w-chassis w-rack">
|
||||||
<div className="w-rack__top">
|
<div className="w-rack__top">
|
||||||
<Led kind="on" pulse slow />
|
<Led kind="on" pulse slow />
|
||||||
<span className="w-engrave text-[0.62rem] tracking-[0.2em]">RACK 01 · SERVICES</span>
|
<span className="w-display text-[0.7rem] tracking-[0.18em] text-[var(--chassis-ink)]">
|
||||||
<span className="w-mono ml-auto hidden text-[0.6rem] tracking-[0.14em] text-[var(--ftext-faint)] sm:inline">
|
RACK 01 · SERVICES
|
||||||
|
</span>
|
||||||
|
<span className="w-mono ml-auto hidden text-[0.62rem] tracking-[0.12em] text-[var(--chassis-faint)] sm:inline">
|
||||||
health checked from this page
|
health checked from this page
|
||||||
</span>
|
</span>
|
||||||
<span className="w-rack__vent w-vents" />
|
<span className="w-rack__vent w-vents" />
|
||||||
|
|
@ -319,8 +330,8 @@ export default function WebSite() {
|
||||||
<div className="w-rack__grid">
|
<div className="w-rack__grid">
|
||||||
<span className="w-rails" />
|
<span className="w-rails" />
|
||||||
<div>
|
<div>
|
||||||
{SERVICES.map((s) => (
|
{SERVICES.map((s, i) => (
|
||||||
<ServiceUnit key={s.name} {...s} />
|
<ServiceUnit key={s.name} tone={TONES[i % TONES.length]} {...s} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<span className="w-rails" />
|
<span className="w-rails" />
|
||||||
|
|
@ -335,8 +346,8 @@ export default function WebSite() {
|
||||||
<SectionHead index="04" label="drawer" title="Eighteen drives, all labelled" />
|
<SectionHead index="04" label="drawer" title="Eighteen drives, all labelled" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-mt-metal w-drawer">
|
<div className="w-chassis w-drawer">
|
||||||
<div className="w-mt-plastic w-drawer__inner w-scroll">
|
<div className="w-well w-drawer__inner w-scroll">
|
||||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||||
{codingProjects.map((p, i) => {
|
{codingProjects.map((p, i) => {
|
||||||
const { name, status } = splitStatus(p.title);
|
const { name, status } = splitStatus(p.title);
|
||||||
|
|
@ -347,7 +358,12 @@ export default function WebSite() {
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="w-drive"
|
className="w-drive"
|
||||||
style={{ "--tilt": `${i % 2 ? 0.5 : -0.6}deg` } as CSSProperties}
|
style={
|
||||||
|
{
|
||||||
|
"--tilt": `${i % 2 ? 0.6 : -0.7}deg`,
|
||||||
|
"--tone": TONES[i % TONES.length],
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
title={p.desc}
|
title={p.desc}
|
||||||
>
|
>
|
||||||
<span className="w-drive__port" aria-hidden="true" />
|
<span className="w-drive__port" aria-hidden="true" />
|
||||||
|
|
@ -364,7 +380,7 @@ export default function WebSite() {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-mt-metal-flush w-drawer__front">
|
<div className="w-chassis-2 w-drawer__front">
|
||||||
<i className="w-screw" aria-hidden="true" />
|
<i className="w-screw" aria-hidden="true" />
|
||||||
<span className="w-drawer__handle" aria-hidden="true" />
|
<span className="w-drawer__handle" aria-hidden="true" />
|
||||||
<i className="w-screw" aria-hidden="true" />
|
<i className="w-screw" aria-hidden="true" />
|
||||||
|
|
@ -376,18 +392,22 @@ export default function WebSite() {
|
||||||
{/* ------------------------------------------------------ hardware */}
|
{/* ------------------------------------------------------ hardware */}
|
||||||
<section id="hardware" className="scroll-mt-28">
|
<section id="hardware" className="scroll-mt-28">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<SectionHead index="05" label="machines" title="Tagged on the pegboard" />
|
<SectionHead index="05" label="machines" title="Pinned to the cork board" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-mt-metal w-pegboard">
|
<div className="w-pegboard">
|
||||||
<div className="w-pegboard__face">
|
<div className="w-pegboard__face">
|
||||||
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-5 sm:grid-cols-2 xl:grid-cols-3">
|
||||||
{techProjects.map((t) => (
|
{techProjects.map((t, i) => (
|
||||||
<article key={t.title} className="w-mt-paper w-hangtag">
|
<article
|
||||||
<span className="w-hangtag__clip" aria-hidden="true" />
|
key={t.title}
|
||||||
<h3 className="w-display text-[1rem] text-[var(--ink)]">{t.title}</h3>
|
className="w-paper w-hangtag"
|
||||||
<p className="w-prose mb-4 mt-2 text-[0.8rem] text-[var(--ink-soft)]">{t.desc}</p>
|
style={{ "--tone": TONES[i % TONES.length] } as CSSProperties}
|
||||||
<span className="w-barcode w-hangtag__bar" aria-hidden="true" />
|
>
|
||||||
|
<span className="w-pin" aria-hidden="true" />
|
||||||
|
<h3 className="w-display text-[1.05rem] text-[var(--ink)]">{t.title}</h3>
|
||||||
|
<span className="w-hangtag__rule" aria-hidden="true" />
|
||||||
|
<p className="w-prose text-[0.84rem] text-[var(--ink-soft)]">{t.desc}</p>
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -399,7 +419,7 @@ export default function WebSite() {
|
||||||
{/* -------------------------------------------------- activity feed */}
|
{/* -------------------------------------------------- activity feed */}
|
||||||
<section id="gitdxd" className="scroll-mt-28">
|
<section id="gitdxd" className="scroll-mt-28">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<SectionHead index="06" label="gitdxd" title="The printer spits out the forge" />
|
<SectionHead index="06" label="gitdxd" title="The receipt for this year's commits" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<Report />
|
<Report />
|
||||||
|
|
@ -418,11 +438,16 @@ export default function WebSite() {
|
||||||
href={f.realUrl || f.url}
|
href={f.realUrl || f.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="w-mt-paper w-bizcard"
|
className="w-bizcard"
|
||||||
style={{ "--tilt": `${i === 1 ? 0.9 : i === 2 ? -1 : -1.4}deg` } as CSSProperties}
|
style={
|
||||||
|
{
|
||||||
|
"--tilt": `${i === 1 ? 0.9 : i === 2 ? -1 : -1.4}deg`,
|
||||||
|
"--tone": TONES[(i + 1) % TONES.length],
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<p className="w-serif text-[1.15rem] text-[var(--ink)]">{f.name}</p>
|
<p className="w-serif text-[1.2rem] text-[var(--ink)]">{f.name}</p>
|
||||||
<p className="w-mono mt-1 text-[0.66rem] text-[var(--ink-soft)]">{f.desc}</p>
|
<p className="w-mono mt-1 text-[0.68rem] text-[var(--ink-soft)]">{f.desc}</p>
|
||||||
</a>
|
</a>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
|
|
@ -432,29 +457,32 @@ export default function WebSite() {
|
||||||
{/* ----------------------------------------------------------- gym */}
|
{/* ----------------------------------------------------------- gym */}
|
||||||
<section id="gym" className="scroll-mt-28">
|
<section id="gym" className="scroll-mt-28">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<SectionHead index="08" label="strength" title="Numbers on the clipboard" />
|
<SectionHead index="08" label="strength" title="Numbers on the scoreboard" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-clipboard">
|
<div className="w-chassis w-score">
|
||||||
<div className="w-mt-metal w-clipboard__clip" aria-hidden="true" />
|
<div className="w-score__head">
|
||||||
<div className="w-mt-paper w-logsheet">
|
<Led kind="on" pulse slow />
|
||||||
<div className="w-sheet__head">
|
<span>TRAINING LOG · 73 KG BODYWEIGHT</span>
|
||||||
<span>training log</span>
|
<span className="ml-auto hidden sm:inline">3 LIFTS</span>
|
||||||
<span className="ml-auto">73 kg bodyweight</span>
|
|
||||||
</div>
|
|
||||||
<div className="mt-3">
|
|
||||||
{LIFTS.map((l) => (
|
|
||||||
<div key={l.lift} className="w-logrow">
|
|
||||||
<span className="w-logrow__lift">{l.lift}</span>
|
|
||||||
<span className="w-logrow__weight">{l.weight} kg</span>
|
|
||||||
<span className="w-logrow__date">{l.date}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<p className="w-mono mt-4 text-[0.62rem] tracking-[0.14em] text-[var(--ink-soft)]">
|
|
||||||
BEST SBD TOTAL · 401 KG · 165 + 111 + 125
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-score__tiles">
|
||||||
|
{LIFTS.map((l, i) => (
|
||||||
|
<div
|
||||||
|
key={l.lift}
|
||||||
|
className="w-score__tile"
|
||||||
|
style={{ "--tone": TONES[(i + 2) % TONES.length] } as CSSProperties}
|
||||||
|
>
|
||||||
|
<span className="w-score__num">
|
||||||
|
{l.weight}
|
||||||
|
<small>kg</small>
|
||||||
|
</span>
|
||||||
|
<span className="w-score__lift">{l.lift}</span>
|
||||||
|
<span className="w-score__date">{l.date}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="w-score__foot">BEST SBD TOTAL · 401 KG · 165 + 111 + 125</p>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -465,16 +493,23 @@ export default function WebSite() {
|
||||||
<SectionHead index="09" label="patch panel" title="Twelve ports, all live" />
|
<SectionHead index="09" label="patch panel" title="Twelve ports, all live" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={60}>
|
<Reveal delay={60}>
|
||||||
<div className="w-mt-metal w-patch">
|
<div className="w-chassis w-patch">
|
||||||
<div className="w-mt-metal-flush w-patch__face">
|
<div className="w-chassis-2 w-patch__face">
|
||||||
<div className="w-patch__head">
|
<div className="w-patch__head">
|
||||||
<i className="w-screw" aria-hidden="true" />
|
<i className="w-screw" aria-hidden="true" />
|
||||||
<span className="w-engrave text-[0.62rem] tracking-[0.2em]">PATCH 01 · SOCIALS</span>
|
<span>PATCH 01 · SOCIALS</span>
|
||||||
<i className="w-screw ml-auto" aria-hidden="true" />
|
<i className="w-screw ml-auto" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-2.5 sm:grid-cols-2 xl:grid-cols-3">
|
<div className="grid gap-2.5 sm:grid-cols-2 xl:grid-cols-3">
|
||||||
{socials.map((s) => (
|
{socials.map((s, i) => (
|
||||||
<a key={s.name} href={s.url} target="_blank" rel="noopener noreferrer" className="w-port">
|
<a
|
||||||
|
key={s.name}
|
||||||
|
href={s.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="w-port"
|
||||||
|
style={{ "--tone": TONES[i % TONES.length] } as CSSProperties}
|
||||||
|
>
|
||||||
<span className="w-port__jack" aria-hidden="true">
|
<span className="w-port__jack" aria-hidden="true">
|
||||||
<span className="w-port__slot" />
|
<span className="w-port__slot" />
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -485,7 +520,7 @@ export default function WebSite() {
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="w-mono mt-5 text-[0.64rem] tracking-[0.06em] text-[var(--ftext-faint)]">
|
<p className="w-mono mt-5 text-[0.66rem] text-[var(--chassis-faint)]">
|
||||||
fastest reply is Discord or Instagram · mail{" "}
|
fastest reply is Discord or Instagram · mail{" "}
|
||||||
<a className="w-link" href="mailto:dvorak@dvorakmortonxd.dev">
|
<a className="w-link" href="mailto:dvorak@dvorakmortonxd.dev">
|
||||||
dvorak@dvorakmortonxd.dev
|
dvorak@dvorakmortonxd.dev
|
||||||
|
|
@ -501,11 +536,13 @@ export default function WebSite() {
|
||||||
{/* ------------------------------------------------------------ footer */}
|
{/* ------------------------------------------------------------ footer */}
|
||||||
<footer className="mx-auto w-full max-w-[1120px] px-5 pb-16 sm:px-8">
|
<footer className="mx-auto w-full max-w-[1120px] px-5 pb-16 sm:px-8">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div className="w-mt-alu relative flex flex-col gap-5 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
<div className="w-plastic relative flex flex-col gap-5 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="w-mono text-[0.72rem] tracking-[0.2em]">DVORAKMORTONXD.LOCAL</p>
|
<p className="w-display text-[0.8rem] tracking-[0.16em] text-[var(--plastic-ink)]">
|
||||||
<p className="w-mono mt-1.5 text-[0.62rem] opacity-80">
|
DVORAKMORTONXD.LOCAL
|
||||||
v3.3.0 · GPL-3.0-only · built with Next.js · textures CC0 via{" "}
|
</p>
|
||||||
|
<p className="w-mono mt-1.5 text-[0.64rem] text-[var(--ink-soft)]">
|
||||||
|
v4.0.0 · GPL-3.0-only · built with Next.js · no textures, all CSS ·{" "}
|
||||||
<a className="w-link" href="/credits">
|
<a className="w-link" href="/credits">
|
||||||
credits
|
credits
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -514,15 +551,15 @@ export default function WebSite() {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setTheme("skeuo")}
|
onClick={() => setTheme("skeuo")}
|
||||||
className="w-keybtn desktop-toggle shrink-0 self-start sm:self-auto"
|
className="w-keybtn w-btn-accent desktop-toggle shrink-0 self-start sm:self-auto"
|
||||||
title="Flip to Desktop mode"
|
title="Flip to Desktop mode"
|
||||||
>
|
>
|
||||||
<Led kind="amber" /> desktop mode
|
<Led kind="on" /> desktop mode
|
||||||
</button>
|
</button>
|
||||||
<i className="w-screw w-screw--alu w-corner-tr" aria-hidden="true" />
|
<i className="w-screw w-corner-tr" aria-hidden="true" />
|
||||||
<i className="w-screw w-screw--alu w-corner-bl" aria-hidden="true" />
|
<i className="w-screw w-corner-bl" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
<p className="w-mono mt-4 text-[0.62rem] text-[var(--ftext-faint)]">
|
<p className="w-mono mt-4 text-[0.64rem] text-[var(--ink-soft)]">
|
||||||
© 2025–2026 Martin Dvořák — follow the licence or don’t, I don’t mind
|
© 2025–2026 Martin Dvořák — follow the licence or don’t, I don’t mind
|
||||||
</p>
|
</p>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
|
||||||
|
|
@ -79,22 +79,34 @@ export function Led({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Screws({ count = 2, alu = false, className = "" }: { count?: number; alu?: boolean; className?: string }) {
|
export function Screws({ count = 2, className = "" }: { count?: number; className?: string }) {
|
||||||
return (
|
return (
|
||||||
<span className={`w-unit__screws ${className}`}>
|
<span className={`w-unit__screws ${className}`}>
|
||||||
{Array.from({ length: count }).map((_, i) => (
|
{Array.from({ length: count }).map((_, i) => (
|
||||||
<i key={i} className={`w-screw${alu ? " w-screw--alu" : ""}`} />
|
<i key={i} className="w-screw" />
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Section heading: an etched aluminium tab on the desk + painted title. */
|
/** Section heading: a coloured chip on the desk + a painted title. */
|
||||||
export function SectionHead({ index, label, title }: { index: string; label: string; title: string }) {
|
export function SectionHead({ index, label, title }: { index: string; label: string; title: string }) {
|
||||||
|
const tones = [
|
||||||
|
"var(--orange)",
|
||||||
|
"var(--teal)",
|
||||||
|
"var(--blue)",
|
||||||
|
"var(--lilac)",
|
||||||
|
"var(--mint)",
|
||||||
|
"var(--coral)",
|
||||||
|
"var(--yellow)",
|
||||||
|
];
|
||||||
|
const n = Number.parseInt(index, 10);
|
||||||
|
const tone = tones[Number.isFinite(n) ? (n - 1) % tones.length : 0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-sechead">
|
<div className="w-sechead">
|
||||||
<span className="w-mt-alu w-sechead__tab">
|
<span className="w-chip w-sechead__tab" style={{ "--tone": tone } as CSSProperties}>
|
||||||
<b>{index}</b> {label}
|
{index} · {label}
|
||||||
</span>
|
</span>
|
||||||
<h2 className="w-display w-paint w-sechead__title">{title}</h2>
|
<h2 className="w-display w-paint w-sechead__title">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -107,9 +119,9 @@ export function Monitor({ children, className = "" }: { children: ReactNode; cla
|
||||||
return (
|
return (
|
||||||
<div className={`w-monitor ${className}`}>
|
<div className={`w-monitor ${className}`}>
|
||||||
<div className="w-monitor__frame">
|
<div className="w-monitor__frame">
|
||||||
<div className="w-mt-glass w-monitor__glass">{children}</div>
|
<div className="w-glass w-monitor__glass">{children}</div>
|
||||||
<div className="w-monitor__chin">
|
<div className="w-monitor__chin">
|
||||||
<Led kind="amber" />
|
<Led kind="on" />
|
||||||
<span className="w-monitor__brand">DXD</span>
|
<span className="w-monitor__brand">DXD</span>
|
||||||
<span className="w-monitor__osd" aria-hidden="true">
|
<span className="w-monitor__osd" aria-hidden="true">
|
||||||
<i className="w-osdkey" />
|
<i className="w-osdkey" />
|
||||||
|
|
@ -127,6 +139,7 @@ export function Monitor({ children, className = "" }: { children: ReactNode; cla
|
||||||
}
|
}
|
||||||
|
|
||||||
const KEY_ROWS = ["1234567890-=".split(""), "qwertyuiop[]".split(""), "asdfghjkl;'".split("")];
|
const KEY_ROWS = ["1234567890-=".split(""), "qwertyuiop[]".split(""), "asdfghjkl;'".split("")];
|
||||||
|
const CAP_TONES = ["var(--mint)", "var(--blue)", "var(--yellow)", "var(--coral)", "var(--lilac)", "var(--teal)"];
|
||||||
|
|
||||||
export function Keyboard() {
|
export function Keyboard() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -134,16 +147,20 @@ export function Keyboard() {
|
||||||
<div className="w-keyboard__deck">
|
<div className="w-keyboard__deck">
|
||||||
<div className="w-keyboard__row">
|
<div className="w-keyboard__row">
|
||||||
<span className="w-key w-key--accent">esc</span>
|
<span className="w-key w-key--accent">esc</span>
|
||||||
{KEY_ROWS[0].map((k) => (
|
{KEY_ROWS[0].map((k, i) => (
|
||||||
<span key={k} className="w-key">
|
<span key={k} className="w-key" style={{ "--tone": CAP_TONES[i % CAP_TONES.length] } as CSSProperties}>
|
||||||
{k}
|
{k}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{KEY_ROWS.slice(1).map((row, i) => (
|
{KEY_ROWS.slice(1).map((row, r) => (
|
||||||
<div key={i} className="w-keyboard__row">
|
<div key={r} className="w-keyboard__row">
|
||||||
{row.map((k) => (
|
{row.map((k, i) => (
|
||||||
<span key={k} className="w-key">
|
<span
|
||||||
|
key={k}
|
||||||
|
className="w-key"
|
||||||
|
style={{ "--tone": CAP_TONES[(i + r + 2) % CAP_TONES.length] } as CSSProperties}
|
||||||
|
>
|
||||||
{k}
|
{k}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
|
@ -158,24 +175,27 @@ export function Keyboard() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RACK_TONES = ["var(--mint)", "var(--blue)", "var(--yellow)", "var(--coral)"];
|
||||||
|
|
||||||
/** A small rack that sits on the desk in the hero. */
|
/** A small rack that sits on the desk in the hero. */
|
||||||
export function MiniRack({ label = "DXD-01" }: { label?: string }) {
|
export function MiniRack({ label = "DXD-01" }: { label?: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="w-rack w-mt-metal" aria-hidden="true">
|
<div className="w-chassis w-rack" aria-hidden="true">
|
||||||
<div className="w-rack__top">
|
<div className="w-rack__top">
|
||||||
<Led kind="on" pulse />
|
<Led kind="on" pulse />
|
||||||
<span className="w-engrave text-[0.62rem] tracking-[0.2em]">{label}</span>
|
<span className="w-display text-[0.68rem] tracking-[0.2em] text-[var(--chassis-ink)]">{label}</span>
|
||||||
<span className="w-rack__vent w-vents" />
|
<span className="w-rack__vent w-vents" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-rack__grid">
|
<div className="w-rack__grid">
|
||||||
<span className="w-rails" />
|
<span className="w-rails" />
|
||||||
<div>
|
<div>
|
||||||
{[0, 1, 2, 3].map((i) => (
|
{[0, 1, 2, 3].map((i) => (
|
||||||
<div key={i} className="w-unit !min-h-[46px] !gap-[10px] !py-2">
|
<div key={i} className="w-unit !min-h-[48px] !gap-[10px] !py-2" style={{ "--tone": RACK_TONES[i] } as CSSProperties}>
|
||||||
<span className="w-unit__grille !h-6 !w-9" />
|
<span className="w-unit__grip !h-7 !w-3" />
|
||||||
|
<span className="w-unit__grille !h-7 !w-10" />
|
||||||
<Led kind="on" pulse style={{ animationDelay: `${i * 0.5}s` }} />
|
<Led kind="on" pulse style={{ animationDelay: `${i * 0.5}s` }} />
|
||||||
<Led kind="amber" pulse slow style={{ animationDelay: `${i * 0.3}s` }} />
|
<Led kind="amber" pulse slow style={{ animationDelay: `${i * 0.3}s` }} />
|
||||||
<span className="ml-auto w-engrave text-[0.58rem] tracking-[0.14em]">
|
<span className="w-display ml-auto text-[0.6rem] tracking-[0.14em] text-[var(--chassis-dim)]">
|
||||||
{["pxe", "aio", "nas", "web"][i]}
|
{["pxe", "aio", "nas", "web"][i]}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -187,13 +207,13 @@ export function MiniRack({ label = "DXD-01" }: { label?: string }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A machined aluminium plate with one number etched into it. */
|
/** A big coloured sticker with one number on it. */
|
||||||
export function Plaque({ value, label }: { value: string; label: string }) {
|
export function Plaque({ value, label, tone = "var(--orange)" }: { value: string; label: string; tone?: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="w-mt-alu w-plaque">
|
<div className="w-sticker w-plaque" style={{ "--tone": tone } as CSSProperties}>
|
||||||
<span className="w-plaque__value">{value}</span>
|
<span className="w-plaque__value">{value}</span>
|
||||||
<span className="w-plaque__label">{label}</span>
|
<span className="w-plaque__label">{label}</span>
|
||||||
<Led kind="amber" className="w-plaque__dot" />
|
<i className="w-plaque__dot" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -209,6 +229,7 @@ export function ServiceUnit({
|
||||||
access,
|
access,
|
||||||
checkUrl,
|
checkUrl,
|
||||||
disabled,
|
disabled,
|
||||||
|
tone = "var(--teal)",
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
|
|
@ -216,6 +237,7 @@ export function ServiceUnit({
|
||||||
access?: ServiceAccess;
|
access?: ServiceAccess;
|
||||||
checkUrl?: string;
|
checkUrl?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
tone?: string;
|
||||||
}) {
|
}) {
|
||||||
const [status, setStatus] = useState<"pending" | "up" | "down">("pending");
|
const [status, setStatus] = useState<"pending" | "up" | "down">("pending");
|
||||||
|
|
||||||
|
|
@ -237,6 +259,7 @@ export function ServiceUnit({
|
||||||
|
|
||||||
const live = disabled ? "up" : status;
|
const live = disabled ? "up" : status;
|
||||||
const kind = live === "up" ? "on" : live === "down" ? "off" : "amber";
|
const kind = live === "up" ? "on" : live === "down" ? "off" : "amber";
|
||||||
|
const stateClass = disabled ? "is-wait" : live === "up" ? "is-on" : live === "down" ? "is-off" : "is-wait";
|
||||||
const label = disabled ? "STANDBY" : live === "up" ? "ONLINE" : live === "down" ? "OFFLINE" : "POLLING";
|
const label = disabled ? "STANDBY" : live === "up" ? "ONLINE" : live === "down" ? "OFFLINE" : "POLLING";
|
||||||
const accessLabel = access === "public" ? "public" : access === "private" ? "private" : access === "on_request" ? "on request" : null;
|
const accessLabel = access === "public" ? "public" : access === "private" ? "private" : access === "on_request" ? "on request" : null;
|
||||||
|
|
||||||
|
|
@ -253,8 +276,8 @@ export function ServiceUnit({
|
||||||
<span className="w-unit__grip" aria-hidden="true" />
|
<span className="w-unit__grip" aria-hidden="true" />
|
||||||
<span className="w-unit__grille w-grille" aria-hidden="true" />
|
<span className="w-unit__grille w-grille" aria-hidden="true" />
|
||||||
<span className="w-unit__id">
|
<span className="w-unit__id">
|
||||||
<span className="w-unit__name w-engrave">{name}</span>
|
<span className="w-unit__name">{name}</span>
|
||||||
<span className="w-tape mt-1">
|
<span className="w-tape mt-1.5">
|
||||||
{host}
|
{host}
|
||||||
{accessLabel ? ` · ${accessLabel}` : ""}
|
{accessLabel ? ` · ${accessLabel}` : ""}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -262,7 +285,7 @@ export function ServiceUnit({
|
||||||
</span>
|
</span>
|
||||||
<span className="w-unit__state">
|
<span className="w-unit__state">
|
||||||
<Led kind={kind} pulse={live === "up"} />
|
<Led kind={kind} pulse={live === "up"} />
|
||||||
<span className="w-unit__status w-engrave">{label}</span>
|
<span className={`w-unit__status ${stateClass}`}>{label}</span>
|
||||||
</span>
|
</span>
|
||||||
<Screws />
|
<Screws />
|
||||||
</>
|
</>
|
||||||
|
|
@ -270,13 +293,20 @@ export function ServiceUnit({
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return (
|
return (
|
||||||
<span className="w-unit" aria-disabled="true">
|
<span className="w-unit" style={{ "--tone": tone } as CSSProperties} aria-disabled="true">
|
||||||
{inner}
|
{inner}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<a href={url} target="_blank" rel="noopener noreferrer" className="w-unit w-unit--link" title={desc}>
|
<a
|
||||||
|
href={url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="w-unit w-unit--link"
|
||||||
|
style={{ "--tone": tone } as CSSProperties}
|
||||||
|
title={desc}
|
||||||
|
>
|
||||||
{inner}
|
{inner}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
@ -295,7 +325,7 @@ interface Activity {
|
||||||
repo?: { full_name?: string };
|
repo?: { full_name?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
const DOT_OPACITY = [0.07, 0.3, 0.48, 0.64, 0.82];
|
const DOT_OPACITY = [0.12, 0.35, 0.55, 0.75, 1];
|
||||||
|
|
||||||
export function Report() {
|
export function Report() {
|
||||||
const [weeks, setWeeks] = useState<{ date: string; contribs: number; level: number }[][]>([]);
|
const [weeks, setWeeks] = useState<{ date: string; contribs: number; level: number }[][]>([]);
|
||||||
|
|
@ -377,7 +407,7 @@ export function Report() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-printout">
|
<div className="w-printout">
|
||||||
<div className="w-mt-paper w-printout__paper">
|
<div className="w-printout__paper">
|
||||||
<p className="w-print__head">
|
<p className="w-print__head">
|
||||||
<span>GITDXD · ACTIVITY REPORT · 12 MONTHS</span>
|
<span>GITDXD · ACTIVITY REPORT · 12 MONTHS</span>
|
||||||
<span className="ml-auto flex items-center gap-3">
|
<span className="ml-auto flex items-center gap-3">
|
||||||
|
|
@ -387,20 +417,20 @@ export function Report() {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{state === "error" ? (
|
{state === "error" ? (
|
||||||
<p className="w-mono mt-5 text-[0.72rem] text-[#4a4133]">could not reach the forge.</p>
|
<p className="w-mono mt-5 text-[0.74rem] text-[var(--ink-soft)]">could not reach the forge.</p>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="mt-5 flex flex-wrap items-end gap-x-6 gap-y-2">
|
<div className="mt-5 flex flex-wrap items-end gap-x-6 gap-y-2">
|
||||||
<div>
|
<div>
|
||||||
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#4a4133]">TOTAL / 12 MONTHS</p>
|
<p className="w-kicker">total / 12 months</p>
|
||||||
<p className="w-display text-[2.6rem] leading-none text-[var(--ink)]">
|
<p className="w-display mt-1 text-[2.6rem] leading-none text-[var(--ink)]">
|
||||||
{state === "loading" ? "—" : total}
|
{state === "loading" ? "—" : total}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="w-mono text-[0.62rem] leading-relaxed text-[#6f6450]">
|
<p className="w-mono text-[0.64rem] leading-relaxed text-[var(--ink-soft)]">
|
||||||
each dot = one day
|
each square = one day
|
||||||
<br />
|
<br />
|
||||||
darkest = busiest
|
strongest orange = busiest
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,34 +4,32 @@ import "../web.css";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "credits · dvorakmortonxd.dev",
|
title: "credits · dvorakmortonxd.dev",
|
||||||
description: "Textures, type and other borrowed work used on dvorakmortonxd.dev.",
|
description: "Type and other borrowed work used on dvorakmortonxd.dev.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const TEXTURES = [
|
|
||||||
{ file: "desk.jpg", asset: "Wood 028", id: "Wood028", use: "the near-black desk" },
|
|
||||||
{ file: "metal.jpg", asset: "Metal 051 C", id: "Metal051C", use: "rack, monitor, panel hardware" },
|
|
||||||
{ file: "paper.jpg", asset: "Paper 003", id: "Paper003", use: "sheets, labels, tape, the report" },
|
|
||||||
{ file: "carbon.jpg", asset: "Fabric 004", id: "Fabric004", use: "the keyboard deck" },
|
|
||||||
{ file: "plastic.jpg", asset: "Plastic 012 A", id: "Plastic012A", use: "the drawer interior" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const FONTS = [
|
const FONTS = [
|
||||||
|
{
|
||||||
|
name: "Nunito",
|
||||||
|
by: "Vernon Adams, Cyreal, Jacques Le Bailly",
|
||||||
|
use: "hardware labels, headings, buttons",
|
||||||
|
href: "https://fonts.google.com/specimen/Nunito",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Newsreader",
|
name: "Newsreader",
|
||||||
by: "Production Type",
|
by: "Production Type",
|
||||||
use: "printed headlines, sheet copy, stamped numbers",
|
use: "anything printed on paper",
|
||||||
href: "https://fonts.google.com/specimen/Newsreader",
|
href: "https://fonts.google.com/specimen/Newsreader",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IBM Plex Sans",
|
name: "IBM Plex Sans",
|
||||||
by: "IBM",
|
by: "IBM",
|
||||||
use: "body text on the desk",
|
use: "body copy",
|
||||||
href: "https://fonts.google.com/specimen/IBM+Plex+Sans",
|
href: "https://fonts.google.com/specimen/IBM+Plex+Sans",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IBM Plex Mono",
|
name: "IBM Plex Mono",
|
||||||
by: "IBM",
|
by: "IBM",
|
||||||
use: "engraved labels, terminal text, index cards",
|
use: "terminal text, receipt, technical labels",
|
||||||
href: "https://fonts.google.com/specimen/IBM+Plex+Mono",
|
href: "https://fonts.google.com/specimen/IBM+Plex+Mono",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -41,70 +39,34 @@ export default function Credits() {
|
||||||
<div className="w-page">
|
<div className="w-page">
|
||||||
<main className="mx-auto w-full max-w-[1120px] px-5 pt-24 pb-20 sm:px-8">
|
<main className="mx-auto w-full max-w-[1120px] px-5 pt-24 pb-20 sm:px-8">
|
||||||
<div className="w-sechead">
|
<div className="w-sechead">
|
||||||
<span className="w-mt-alu w-sechead__tab">credits</span>
|
<span className="w-chip w-sechead__tab" style={{ "--tone": "var(--mint)" } as React.CSSProperties}>
|
||||||
|
credits
|
||||||
|
</span>
|
||||||
<h1 className="w-display w-paint w-sechead__title">Everything borrowed, credited.</h1>
|
<h1 className="w-display w-paint w-sechead__title">Everything borrowed, credited.</h1>
|
||||||
</div>
|
</div>
|
||||||
<p className="w-prose w-paint max-w-[64ch] text-[var(--ftext-dim)]">
|
<p className="w-prose w-paint max-w-[62ch] text-[var(--ink-soft)]">
|
||||||
The desk is built out of five CC0 textures and three open-licensed typefaces. Nothing is scraped
|
The desk contains <strong>no images at all</strong> — every surface, screw, LED, paper sheet and coffee
|
||||||
or unlicensed — the authors are named even where the licence does not require it.
|
mug is drawn in CSS. That means there are no photography or texture licences to track: the only
|
||||||
|
borrowed work is type.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<section className="mt-14">
|
|
||||||
<div className="w-sechead !mb-6">
|
|
||||||
<span className="w-mt-alu w-sechead__tab">textures</span>
|
|
||||||
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">ambientCG — CC0 1.0</h2>
|
|
||||||
</div>
|
|
||||||
<p className="w-prose w-paint max-w-[64ch] text-[var(--ftext-dim)]">
|
|
||||||
All five materials come from{" "}
|
|
||||||
<a className="w-link" href="https://ambientcg.com" target="_blank" rel="noopener noreferrer">
|
|
||||||
ambientCG
|
|
||||||
</a>{" "}
|
|
||||||
(Struffel Productions) under the{" "}
|
|
||||||
<a
|
|
||||||
className="w-link"
|
|
||||||
href="https://creativecommons.org/publicdomain/zero/1.0/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
CC0 1.0
|
|
||||||
</a>{" "}
|
|
||||||
public-domain dedication. I downloaded the 1K JPG colour maps, downscaled them to 512 px and
|
|
||||||
tint them in CSS.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul className="mt-7 grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
|
||||||
{TEXTURES.map((t) => (
|
|
||||||
<li key={t.id} className="w-mt-paper w-cardindex">
|
|
||||||
<p className="w-serif text-[1.05rem] text-[var(--ink)]">{t.asset}</p>
|
|
||||||
<p className="w-mono mt-2 text-[0.64rem] leading-relaxed text-[var(--ink-soft)]">
|
|
||||||
source:{" "}
|
|
||||||
<a
|
|
||||||
className="w-link"
|
|
||||||
href={`https://ambientcg.com/view?id=${t.id}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
ambientCG / {t.id}
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
licence: CC0 1.0
|
|
||||||
<br />
|
|
||||||
used as <code>{`/textures/${t.file}`}</code> — {t.use}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-16">
|
<section className="mt-16">
|
||||||
<div className="w-sechead !mb-6">
|
<div className="w-sechead !mb-6">
|
||||||
<span className="w-mt-alu w-sechead__tab">type</span>
|
<span className="w-chip w-sechead__tab" style={{ "--tone": "var(--teal)" } as React.CSSProperties}>
|
||||||
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">Google Fonts — SIL OFL 1.1</h2>
|
type
|
||||||
|
</span>
|
||||||
|
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">
|
||||||
|
Google Fonts — SIL Open Font License 1.1
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<ul className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
<ul className="grid gap-5 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
{FONTS.map((f) => (
|
{FONTS.map((f, i) => (
|
||||||
<li key={f.name} className="w-mt-paper w-cardindex w-cardindex--alt">
|
<li
|
||||||
<p className="w-serif text-[1.05rem] text-[var(--ink)]">{f.name}</p>
|
key={f.name}
|
||||||
|
className="w-paper w-cardindex"
|
||||||
|
style={{ "--tone": ["var(--orange)", "var(--blue)", "var(--lilac)", "var(--mint)"][i % 4] } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<p className="w-display text-[1.05rem] text-[var(--ink)]">{f.name}</p>
|
||||||
<p className="w-mono mt-2 text-[0.64rem] leading-relaxed text-[var(--ink-soft)]">
|
<p className="w-mono mt-2 text-[0.64rem] leading-relaxed text-[var(--ink-soft)]">
|
||||||
by {f.by}
|
by {f.by}
|
||||||
<br />
|
<br />
|
||||||
|
|
@ -122,13 +84,15 @@ export default function Credits() {
|
||||||
|
|
||||||
<section className="mt-16">
|
<section className="mt-16">
|
||||||
<div className="w-sechead !mb-6">
|
<div className="w-sechead !mb-6">
|
||||||
<span className="w-mt-alu w-sechead__tab">notes</span>
|
<span className="w-chip w-sechead__tab" style={{ "--tone": "var(--lilac)" } as React.CSSProperties}>
|
||||||
|
notes
|
||||||
|
</span>
|
||||||
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">Other marks</h2>
|
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">Other marks</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="w-prose w-paint max-w-[64ch] text-[var(--ftext-dim)]">
|
<p className="w-prose w-paint max-w-[62ch] text-[var(--ink-soft)]">
|
||||||
The social ports use brand marks that remain the property of their owners and are used only to
|
The patch panel uses brand marks that remain the property of their owners and are used only to
|
||||||
link to those accounts. Everything written on the paper is mine. The desktop mode (the KDE
|
link to those accounts. Everything written on the paper — and every pixel of the desk itself — is
|
||||||
simulation) uses its own separate assets.
|
mine. The desktop mode (the KDE simulation) uses its own separate assets.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,20 @@
|
||||||
--ease-cinematic: cubic-bezier(0.22, 1, 0.36, 1);
|
--ease-cinematic: cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Beats page.css's :root when the editorial theme is active. */
|
/* Beats page.css's :root when the web theme is active. The desk is a light,
|
||||||
|
colourful, cartoon-skeuomorphic surface — the dark gruvbox values in :root
|
||||||
|
are only the fallback for the desktop mode. */
|
||||||
html[data-theme="web"] {
|
html[data-theme="web"] {
|
||||||
color-scheme: dark;
|
color-scheme: light;
|
||||||
--accent: #fe8019;
|
--bg: #f7e7c6;
|
||||||
--accent2: #d65d0e;
|
--bg-raised: #fffdf6;
|
||||||
--accent3: #fabd2f;
|
--bg-well: #eed9b0;
|
||||||
--green: #b8bb26;
|
--accent: #f4823a;
|
||||||
--red: #fb4934;
|
--accent2: #d4611c;
|
||||||
--gray: #a89984;
|
--accent3: #f2c14e;
|
||||||
|
--green: #58bd72;
|
||||||
|
--red: #ea6f6a;
|
||||||
|
--gray: #7f7768;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
|
|
@ -71,7 +76,7 @@ html[data-theme="web"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-theme="web"] ::selection {
|
html[data-theme="web"] ::selection {
|
||||||
background: rgba(254, 128, 25, 0.28);
|
background: rgba(244, 130, 58, 0.3);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,12 +93,12 @@ html[data-theme="web"] ::-webkit-scrollbar-track {
|
||||||
background: var(--bg-well);
|
background: var(--bg-well);
|
||||||
}
|
}
|
||||||
html[data-theme="web"] ::-webkit-scrollbar-thumb {
|
html[data-theme="web"] ::-webkit-scrollbar-thumb {
|
||||||
background: #3c3836;
|
background: #d9c49b;
|
||||||
border: 3px solid var(--bg-well);
|
border: 3px solid var(--bg-well);
|
||||||
border-radius: 99px;
|
border-radius: 99px;
|
||||||
}
|
}
|
||||||
html[data-theme="web"] ::-webkit-scrollbar-thumb:hover {
|
html[data-theme="web"] ::-webkit-scrollbar-thumb:hover {
|
||||||
background: #504945;
|
background: #c9b184;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Phones never get the desktop-mode button. */
|
/* Phones never get the desktop-mode button. */
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Newsreader, IBM_Plex_Sans, IBM_Plex_Mono } from "next/font/google";
|
import { Newsreader, IBM_Plex_Sans, IBM_Plex_Mono, Nunito } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import ThemeScript from "./components/ThemeScript";
|
import ThemeScript from "./components/ThemeScript";
|
||||||
|
|
||||||
// Display serif for headlines, Plex for body/labels — same pairing as the
|
// The web desk pairs a rounded sans (hardware labels, headings) with a printed
|
||||||
// reference editorial site.
|
// serif (anything on paper) and a mono (terminal text + technical labels).
|
||||||
const news = Newsreader({
|
const news = Newsreader({
|
||||||
variable: "--font-news",
|
variable: "--font-news",
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
|
@ -24,6 +24,12 @@ const plexMono = IBM_Plex_Mono({
|
||||||
weight: ["400", "500"],
|
weight: ["400", "500"],
|
||||||
display: "swap",
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
const round = Nunito({
|
||||||
|
variable: "--font-round",
|
||||||
|
subsets: ["latin"],
|
||||||
|
weight: ["600", "700", "800"],
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "dvorakmortonxd.dev",
|
title: "dvorakmortonxd.dev",
|
||||||
|
|
@ -51,7 +57,7 @@ export default function RootLayout({
|
||||||
lang="en"
|
lang="en"
|
||||||
data-theme="web"
|
data-theme="web"
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
className={`${news.variable} ${plex.variable} ${plexMono.variable}`}
|
className={`${news.variable} ${plex.variable} ${plexMono.variable} ${round.variable}`}
|
||||||
>
|
>
|
||||||
<body className="antialiased">
|
<body className="antialiased">
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|
|
||||||
1614
src/app/web.css
1614
src/app/web.css
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue