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:
parent
cfc256c7bd
commit
a71cfb1731
8 changed files with 491 additions and 406 deletions
39
README.md
39
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
24
agents.md
24
agents.md
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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’m hosting an SMP — message me on Discord if you want in. I’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’t, I don’t mind
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
735
src/app/web.css
735
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