modernise the desk: near-black surface, dark anodised hardware, machined aluminium plates, printed labels with barcodes, baked sans headlines, single orange accent

This commit is contained in:
Martin Dvořák 2026-09-30 09:27:02 +00:00
commit a71cfb1731
8 changed files with 491 additions and 406 deletions

View file

@ -2,15 +2,15 @@
> The source code for my personal website, **[dvorakmortonxd.dev](https://dvorakmortonxd.dev)**.
It's a **skeuomorphic personal site**: a walnut desk with graphite hardware, cream paper and
brass details resting on it, with the whole KDE desktop simulation kept behind a physical power
switch. Two modes, one content set:
It's a **skeuomorphic personal site**: a near-black desk with anodised hardware, machined
aluminium, clean printed labels and a single orange accent, with the whole KDE desktop
simulation kept behind a physical power switch. Two modes, one content set:
- **Web** (default) — the desk. A monitor carries the headline with a keyboard in front of it
and a small rack beside it; services are 1U rack units with health LEDs, the 18 coding
projects are labelled drives in a drawer, the machines hang as paper tags on a pegboard, the
contribution heatmap is printed on continuous dot-matrix paper, friends are business cards,
the gym log is a clipboard and the socials are a patch panel. Textures are CC0 — see
- **Web** (default) — the desk. A monitor carries the headline with a mechanical keyboard in
front of it and a small rack beside it; services are 1U rack units with health LEDs, the 18
coding projects are labelled drives in a drawer, the machines are barcoded asset labels on a
pegboard, the contribution heatmap is printed on a label-printer sheet, friends are business
cards, the gym log is a clipboard and the socials are a patch panel. Textures are CC0 — see
[`/credits`](https://dvorakmortonxd.dev/credits).
- **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
@ -24,9 +24,10 @@ heatmap**, gym SBD and links to friends' sites.
- **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.
- **Skeuomorphic materials** — walnut desk, graphite faceplates, cream paper, brass plaques and
amber LEDs, all CSS over five CC0 textures. Every section is a physical object with a contact
shadow; nothing is pure white on black or grey on black.
- **Skeuomorphic materials, modern finish** — near-black desk, dark anodised faceplates,
machined aluminium plaques, matte printed labels and mint/orange LEDs, all CSS over five CC0
textures. Every section is a physical object with a contact shadow; nothing is pure white on
black or grey on black.
- **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
the old iOS-style phone home screen is gone.
@ -67,14 +68,14 @@ heatmap**, gym SBD and links to friends' sites.
- **Language:** TypeScript, React 19 (client components everywhere interactivity lives)
- **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
(walnut desk, graphite metal, cream paper, brass, OLED glass, LEDs, screws, rack rails, rocker
switch) and the object layouts. `page.css` keeps the shared component styles the desktop apps
still use, and `skeuo.css` is the KDE desktop.
- **Fonts:** Newsreader (headlines, paper) + IBM Plex Sans/Mono (body, machine labels) via
`next/font/google`; the desktop keeps its system sans stack.
- **Textures:** five CC0 materials from ambientCG — `desk` (walnut), `metal`, `paper`, `carbon`,
`plastic` — downscaled to 512 px in `public/textures`. Attribution is on `/credits`.
- **Version:** `3.2.0` (see `package.json`)
(near-black desk, anodised metal, machined aluminium, printed labels, black glass, LEDs, rack
rails, rocker switch) and the object layouts. `page.css` keeps the shared component styles the
desktop apps still use, and `skeuo.css` is the KDE desktop.
- **Fonts:** IBM Plex Sans/Mono for screens, machine labels and headings; Newsreader for things
printed on paper; via `next/font/google`. The desktop keeps its system sans stack.
- **Textures:** five CC0 materials from ambientCG — `desk` (near-black wood), `metal`, `paper`,
`carbon`, `plastic` — downscaled to 512 px in `public/textures`. Attribution is on `/credits`.
- **Version:** `3.3.0` (see `package.json`)
## 🚀 Running Locally

View file

@ -34,20 +34,20 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a
## What this project is
Personal portfolio site for **dvorakmortonxd.dev**. The default view is a
**skeuomorphic desk**: a walnut surface with a monitor, keyboard, rack, drive
drawer, pegboard, dot-matrix report, business cards, clipboard and patch panel
resting on it. A physical power switch flips to a **KDE Plasma desktop
simulation**. Phones only ever get the desk.
**skeuomorphic desk**: a near-black surface with a monitor, mechanical keyboard,
rack, drive drawer, pegboard of asset labels, label-printer report, business
cards, clipboard and patch panel resting on it. 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.
- Styling: **Tailwind v4** for layout plus **`web.css`**, the material system
(walnut desk, graphite metal, cream paper, brass, OLED glass, LEDs, screws,
rack rails, rocker switch) over five CC0 textures in `public/textures`
(credited on `/credits`). `globals.css` holds the tokens, base styles and the
reveals; `skeuo.css` and `page.css` are the desktop and the shared
desktop-app styles.
(near-black desk, anodised metal, machined aluminium, printed labels, black
glass, LEDs, rack rails, rocker switch) over five CC0 textures in
`public/textures` (credited on `/credits`). `globals.css` holds the tokens,
base styles and the reveals; `skeuo.css` and `page.css` are the desktop and
the shared desktop-app styles.
- Path alias: `@/*` → `src/*`.
- Version: `3.2.0` (see `package.json`).
- Version: `3.3.0` (see `package.json`).
### Scripts
```bash
@ -127,8 +127,8 @@ scripts/
Modes: `web` = the skeuomorphic desk (default/landing), `skeuo` = KDE Plasma
desktop.
The desk uses **five CC0 textures** from ambientCG (`public/textures`: desk
walnut, metal, paper, carbon, plastic), downscaled to 512 px and credited on
The desk uses **five CC0 textures** from ambientCG (`public/textures`: near-black
desk wood, metal, paper, carbon, plastic), downscaled to 512 px and credited on
`/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`
renders an inline mark for it instead. The desktop still uses the image assets.

View file

@ -1,6 +1,6 @@
{
"name": "dvorakmortonxd",
"version": "3.2.0",
"version": "3.3.0",
"private": true,
"scripts": {
"dev": "node scripts/dev-server.mjs",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

View file

@ -149,7 +149,7 @@ export default function WebSite() {
<span className="a">martin@dvorakmortonxd</span>:~$ whoami
<span className="w-caret ml-2" />
</p>
<h1 className="w-serif mt-5 text-[clamp(1.85rem,4.2vw,2.85rem)] text-[var(--ftext)]">
<h1 className="w-display mt-5 text-[clamp(1.8rem,4.1vw,2.75rem)] text-[var(--ftext)]">
My own little internet — one rack, zero subscriptions.
</h1>
<p className="w-prose mt-5 max-w-[54ch] text-[var(--ftext-dim)]">
@ -250,15 +250,15 @@ export default function WebSite() {
</Reveal>
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)] lg:items-start">
<Reveal delay={60}>
<div className="w-sticky">
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#6b6047]">
<div className="w-note">
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#6b7075]">
NOW.TXT · UPDATED 09/2026
</p>
<p className="w-prose mt-4 text-[#4a4232]">
<p className="w-prose mt-4 text-[#34383b]">
I&rsquo;m hosting an SMP — message me on Discord if you want in. I&rsquo;m also cooking most
days, bulking through the non-summer seasons.
</p>
<p className="w-prose mt-3 text-[#4a4232]">
<p className="w-prose mt-3 text-[#34383b]">
Me and my friend{" "}
<a className="w-link" href="https://skopec.dev" target="_blank" rel="noopener noreferrer">
Pepa
@ -295,7 +295,7 @@ export default function WebSite() {
</li>
</ul>
</div>
<i className="w-pencil ml-6 w-44" aria-hidden="true" />
<i className="w-pen ml-6 w-44" aria-hidden="true" />
</div>
</Reveal>
</div>
@ -356,6 +356,7 @@ export default function WebSite() {
<span className="w-drive__meta">
{status ?? "unlabelled"} · {p.desc}
</span>
<span className="w-barcode w-drive__bar" aria-hidden="true" />
</span>
<Led kind={statusKind(status)} className="w-drive__led" />
</a>
@ -383,9 +384,10 @@ export default function WebSite() {
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{techProjects.map((t) => (
<article key={t.title} className="w-mt-paper w-hangtag">
<span className="w-hangtag__hole" aria-hidden="true" />
<h3 className="w-serif text-[1.02rem] text-[var(--ink)]">{t.title}</h3>
<p className="w-prose mt-2 text-[0.8rem] text-[var(--ink-soft)]">{t.desc}</p>
<span className="w-hangtag__clip" aria-hidden="true" />
<h3 className="w-display text-[1rem] text-[var(--ink)]">{t.title}</h3>
<p className="w-prose mb-4 mt-2 text-[0.8rem] text-[var(--ink-soft)]">{t.desc}</p>
<span className="w-barcode w-hangtag__bar" aria-hidden="true" />
</article>
))}
</div>
@ -499,11 +501,11 @@ export default function WebSite() {
{/* ------------------------------------------------------------ footer */}
<footer className="mx-auto w-full max-w-[1120px] px-5 pb-16 sm:px-8">
<Reveal>
<div className="w-mt-brass relative flex flex-col gap-5 px-6 py-5 sm:flex-row sm:items-center sm:justify-between">
<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="min-w-0">
<p className="w-mono text-[0.72rem] tracking-[0.2em]">DVORAKMORTONXD.LOCAL</p>
<p className="w-mono mt-1.5 text-[0.62rem] opacity-80">
v3.2.0 · GPL-3.0-only · built with Next.js · textures CC0 via{" "}
v3.3.0 · GPL-3.0-only · built with Next.js · textures CC0 via{" "}
<a className="w-link" href="/credits">
credits
</a>
@ -517,8 +519,8 @@ export default function WebSite() {
>
<Led kind="amber" /> desktop mode
</button>
<i className="w-screw w-screw--brass w-corner-tr" aria-hidden="true" />
<i className="w-screw w-screw--brass w-corner-bl" aria-hidden="true" />
<i className="w-screw w-screw--alu w-corner-tr" aria-hidden="true" />
<i className="w-screw w-screw--alu w-corner-bl" aria-hidden="true" />
</div>
<p className="w-mono mt-4 text-[0.62rem] text-[var(--ftext-faint)]">
© 2025–2026 Martin Dvořák — follow the licence or don&rsquo;t, I don&rsquo;t mind

View file

@ -79,22 +79,24 @@ export function Led({
);
}
export function Screws({ count = 2, brass = false, className = "" }: { count?: number; brass?: boolean; className?: string }) {
export function Screws({ count = 2, alu = false, className = "" }: { count?: number; alu?: boolean; className?: string }) {
return (
<span className={`w-unit__screws ${className}`}>
{Array.from({ length: count }).map((_, i) => (
<i key={i} className={`w-screw${brass ? " w-screw--brass" : ""}`} />
<i key={i} className={`w-screw${alu ? " w-screw--alu" : ""}`} />
))}
</span>
);
}
/** Section heading: a brass tab screwed to the desk + painted title. */
/** Section heading: an etched aluminium tab on the desk + painted title. */
export function SectionHead({ index, label, title }: { index: string; label: string; title: string }) {
return (
<div className="w-sechead">
<span className="w-mt-brass w-sechead__tab">{index} · {label}</span>
<h2 className="w-serif w-paint w-sechead__title">{title}</h2>
<span className="w-mt-alu w-sechead__tab">
<b>{index}</b> {label}
</span>
<h2 className="w-display w-paint w-sechead__title">{title}</h2>
</div>
);
}
@ -130,7 +132,15 @@ export function Keyboard() {
return (
<div className="w-keyboard" aria-hidden="true">
<div className="w-keyboard__deck">
{KEY_ROWS.map((row, i) => (
<div className="w-keyboard__row">
<span className="w-key w-key--accent">esc</span>
{KEY_ROWS[0].map((k) => (
<span key={k} className="w-key">
{k}
</span>
))}
</div>
{KEY_ROWS.slice(1).map((row, i) => (
<div key={i} className="w-keyboard__row">
{row.map((k) => (
<span key={k} className="w-key">
@ -142,6 +152,7 @@ export function Keyboard() {
<div className="w-keyboard__row">
<span className="w-key w-key--space" />
</div>
<span className="w-keyboard__bar" />
</div>
</div>
);
@ -176,13 +187,13 @@ export function MiniRack({ label = "DXD-01" }: { label?: string }) {
);
}
/** A brass plate with one number stamped into it. */
/** A machined aluminium plate with one number etched into it. */
export function Plaque({ value, label }: { value: string; label: string }) {
return (
<div className="w-mt-brass w-plaque relative">
<div className="w-mt-alu w-plaque">
<span className="w-plaque__value">{value}</span>
<span className="w-plaque__label">{label}</span>
<i className="w-screw w-screw--brass w-plaque__screw" />
<Led kind="amber" className="w-plaque__dot" />
</div>
);
}
@ -367,12 +378,12 @@ export function Report() {
return (
<div className="w-printout">
<div className="w-mt-paper w-printout__paper">
<span className="w-printout__perf w-printout__perf--l" aria-hidden="true" />
<span className="w-printout__perf w-printout__perf--r" aria-hidden="true" />
<p className="w-print__head">
<span>GITDXD // ACTIVITY REPORT</span>
<span className="ml-auto">{state === "ready" ? "PRINTED LIVE" : state === "error" ? "PRINTER OFFLINE" : "SPOOLING…"}</span>
<span>GITDXD · ACTIVITY REPORT · 12 MONTHS</span>
<span className="ml-auto flex items-center gap-3">
<span className="w-barcode w-[110px]" aria-hidden="true" />
<span>{state === "ready" ? "PRINTED LIVE" : state === "error" ? "PRINTER OFFLINE" : "SPOOLING…"}</span>
</span>
</p>
{state === "error" ? (
@ -382,7 +393,7 @@ export function Report() {
<div className="mt-5 flex flex-wrap items-end gap-x-6 gap-y-2">
<div>
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#4a4133]">TOTAL / 12 MONTHS</p>
<p className="w-serif text-[2.6rem] leading-none text-[var(--ink)]">
<p className="w-display text-[2.6rem] leading-none text-[var(--ink)]">
{state === "loading" ? "—" : total}
</p>
</div>

View file

@ -8,7 +8,7 @@ export const metadata: Metadata = {
};
const TEXTURES = [
{ file: "desk.jpg", asset: "Wood 027", id: "Wood027", use: "the desk the page sits on" },
{ 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" },
@ -41,8 +41,8 @@ export default function Credits() {
<div className="w-page">
<main className="mx-auto w-full max-w-[1120px] px-5 pt-24 pb-20 sm:px-8">
<div className="w-sechead">
<span className="w-mt-brass w-sechead__tab">credits</span>
<h1 className="w-serif w-paint w-sechead__title">Everything borrowed, credited.</h1>
<span className="w-mt-alu w-sechead__tab">credits</span>
<h1 className="w-display w-paint w-sechead__title">Everything borrowed, credited.</h1>
</div>
<p className="w-prose w-paint max-w-[64ch] text-[var(--ftext-dim)]">
The desk is built out of five CC0 textures and three open-licensed typefaces. Nothing is scraped
@ -51,8 +51,8 @@ export default function Credits() {
<section className="mt-14">
<div className="w-sechead !mb-6">
<span className="w-mt-brass w-sechead__tab">textures</span>
<h2 className="w-serif w-paint w-sechead__title !text-[1.3rem]">ambientCG — CC0 1.0</h2>
<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{" "}
@ -98,8 +98,8 @@ export default function Credits() {
<section className="mt-16">
<div className="w-sechead !mb-6">
<span className="w-mt-brass w-sechead__tab">type</span>
<h2 className="w-serif w-paint w-sechead__title !text-[1.3rem]">Google Fonts — SIL OFL 1.1</h2>
<span className="w-mt-alu w-sechead__tab">type</span>
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">Google Fonts — SIL OFL 1.1</h2>
</div>
<ul className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{FONTS.map((f) => (
@ -122,8 +122,8 @@ export default function Credits() {
<section className="mt-16">
<div className="w-sechead !mb-6">
<span className="w-mt-brass w-sechead__tab">notes</span>
<h2 className="w-serif w-paint w-sechead__title !text-[1.3rem]">Other marks</h2>
<span className="w-mt-alu w-sechead__tab">notes</span>
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">Other marks</h2>
</div>
<p className="w-prose w-paint max-w-[64ch] text-[var(--ftext-dim)]">
The social ports use brand marks that remain the property of their owners and are used only to

File diff suppressed because it is too large Load diff