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:
Martin Dvořák 2026-09-30 09:59:00 +00:00
commit 1880a5b0db
14 changed files with 1165 additions and 989 deletions

View file

@ -2,16 +2,17 @@
> 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
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:
It's a **cartoon-skeuomorphic personal site**: a warm cream desk with chunky, brightly coloured
hardware resting on it, drawn entirely in CSS — no textures, no images — 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 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).
- **Web** (default) — the desk. A monitor carries the headline with a candy-keycap keyboard in
front of it and a mini rack beside it; services are 1U rack units with coloured strips and
health LEDs, the 18 coding projects are labelled drives in a drawer, the machines are cards
pinned to a cork board, the contribution heatmap is printed on a receipt, friends are business
cards, the gym numbers are a scoreboard and the socials are a patch panel with coloured ports.
[`/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
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.
@ -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
deliberately not persisted, so a reload never drops you into the desktop.
- **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.
- **CSS-only skeuomorphism** — every surface is a flat gradient with a thick bottom edge, a soft
cast shadow and `color-mix()` tints: slate chassis, cream plastic, paper, cork, black glass,
LEDs, screws, pushpins. The web theme ships no images at all.
- **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)
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,15 +70,16 @@ heatmap**, gym SBD and links to friends' sites.
- **Framework:** Next.js 16 (App Router, Turbopack)
- **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
(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`)
`@theme`/`@utility` classes and the scroll reveals. `src/app/web.css` is the whole desk: the
materials (slate chassis, cream plastic, paper, cork, black glass), the details (screws, LEDs,
pushpins, rack rails, barcode, torn receipt edge) and every object layout, all CSS with
`color-mix()` for tints and shades. `page.css` keeps the shared component styles the desktop
apps still use, and `skeuo.css` is the KDE desktop.
- **Fonts:** Nunito for hardware labels and headings, Newsreader for anything printed on paper,
IBM Plex Sans/Mono for body copy and terminal text — all via `next/font/google`. The desktop
keeps its system sans stack.
- **Assets:** the web theme ships **no images**. The desktop keeps its own wallpaper and icons.
- **Version:** `4.0.0` (see `package.json`)
## 🚀 Running Locally
@ -182,7 +186,7 @@ src/app/
page.css → shared component styles still used by the desktop apps
skeuo.css → KDE desktop styles
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
api/
heatmap/route.ts → contribution heatmap proxy

View file

@ -34,20 +34,24 @@ 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 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.
**cartoon-skeuomorphic desk**: a warm cream surface with a monitor, candy-keycap
keyboard, mini rack, drive drawer, cork board of pinned cards, printed receipt,
business cards, scoreboard and patch panel resting on it — all drawn in CSS, with
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.
- Styling: **Tailwind v4** for layout plus **`web.css`**, the material system
(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.
- Styling: **Tailwind v4** for layout plus **`web.css`**, which is the whole desk
— materials (slate chassis, cream plastic, paper, cork, black glass), details
(screws, LEDs, pushpins, rails, barcode, torn receipt edge) and object layouts,
all CSS with `color-mix()` tints. `globals.css` holds the tokens, base styles
and the reveals; `skeuo.css` and `page.css` are the desktop and the shared
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/*`.
- Version: `3.3.0` (see `package.json`).
- Version: `4.0.0` (see `package.json`).
### Scripts
```bash
@ -85,7 +89,7 @@ src/app/
page.css → shared component styles used by the desktop apps
skeuo.css → KDE Plasma desktop styles
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
api/
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
(phones can never reach `skeuo`).
Modes: `web` = the skeuomorphic desk (default/landing), `skeuo` = KDE Plasma
desktop.
Modes: `web` = the cartoon-skeuomorphic desk (default/landing), `skeuo` = KDE
Plasma desktop.
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`);
The desk ships **no image assets** — every surface is CSS. The only external
visual files are the social SVGs, 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.
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
properties (e.g. `--tilt` on `.w-drive`), never in inline `style` transforms —
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
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

View file

@ -1,6 +1,6 @@
{
"name": "dvorakmortonxd",
"version": "3.3.0",
"version": "4.0.0",
"private": true,
"scripts": {
"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

View file

@ -27,6 +27,17 @@ const NAV = [
{ 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: {
name: string;
desc: string;
@ -72,11 +83,11 @@ export default function WebSite() {
return (
<div className="w-page">
{/* -------------------------------------------------------------- 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">
<a href="#top" className="flex shrink-0 items-center gap-2.5" aria-label="Back to top">
<Led kind="on" pulse slow />
<span className="w-wordmark w-engrave">dvorakmortonxd</span>
<span className="w-wordmark">dvorakmortonxd</span>
</a>
<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"
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
</span>
<span className="w-rocker">
@ -110,7 +121,7 @@ export default function WebSite() {
onClick={() => setMenuOpen((v) => !v)}
aria-expanded={menuOpen}
aria-label={menuOpen ? "Close menu" : "Open menu"}
className="w-keybtn !px-3 !py-2"
className="w-keybtn"
>
{menuOpen ? "close" : "menu"}
</button>
@ -120,7 +131,7 @@ export default function WebSite() {
{menuOpen ? (
<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"
>
{NAV.map((item) => (
@ -128,7 +139,7 @@ export default function WebSite() {
key={item.href}
href={item.href}
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}
</a>
@ -149,18 +160,18 @@ export default function WebSite() {
<span className="a">martin@dvorakmortonxd</span>:~$ whoami
<span className="w-caret ml-2" />
</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.
</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&rsquo;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.
</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">
$ open rack --services
</a>
<a href="#projects" className="w-cmd">
<a href="#projects" className="w-cmd w-cmd--quiet">
$ open drawer --projects
</a>
</p>
@ -175,10 +186,10 @@ export default function WebSite() {
<div className="flex flex-col gap-6">
<MiniRack />
<div className="grid grid-cols-2 gap-3">
<Plaque value={String(SERVICES.length)} label="services" />
<Plaque value={String(codingProjects.length)} label="projects" />
<Plaque value={String(techProjects.length)} label="machines" />
<Plaque value="401" label="kg best SBD" />
<Plaque value={String(SERVICES.length)} label="services" tone="var(--orange)" />
<Plaque value={String(codingProjects.length)} label="projects" tone="var(--teal)" />
<Plaque value={String(techProjects.length)} label="machines" tone="var(--blue)" />
<Plaque value="401" label="kg best SBD" tone="var(--coral)" />
</div>
</div>
</Reveal>
@ -193,7 +204,7 @@ export default function WebSite() {
</Reveal>
<div className="grid gap-7 lg:grid-cols-[minmax(0,1.55fr)_minmax(0,1fr)] lg:items-start">
<Reveal delay={60}>
<article className="w-mt-paper w-sheet">
<article className="w-paper w-sheet">
<div className="w-sheet__head">
<span>manifest.txt</span>
<span className="ml-auto">printed on the desk</span>
@ -224,15 +235,13 @@ export default function WebSite() {
</Reveal>
<Reveal delay={150}>
<aside className="w-mt-paper w-cardindex lg:mt-12">
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[var(--ink-soft)]">
INDEX CARD
</p>
<p className="w-serif mt-3 text-[1.15rem] text-[var(--ink)]">
<aside className="w-paper w-cardindex lg:mt-14" style={{ "--tone": "var(--teal)" } as CSSProperties}>
<p className="w-kicker">index card</p>
<p className="w-serif mt-3 text-[1.2rem] text-[var(--ink)]">
&ldquo;If it has a fan, I&rsquo;ll find a use for it.&rdquo;
</p>
<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>gym · PPL 3–5×/week</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">
<Reveal delay={60}>
<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-[#34383b]">
<span className="w-pin" aria-hidden="true" />
<p className="w-kicker !text-[#8a7420]">now.txt · updated 09/2026</p>
<p className="w-prose mt-4 text-[#5c4a1f]">
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-[#34383b]">
<p className="w-prose mt-3 text-[#5c4a1f]">
Me and my friend{" "}
<a className="w-link" href="https://skopec.dev" target="_blank" rel="noopener noreferrer">
Pepa
@ -278,24 +286,25 @@ export default function WebSite() {
</Reveal>
<Reveal delay={140}>
<div className="flex flex-col gap-6">
<div className="w-mt-paper w-cardindex w-cardindex--alt">
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[var(--ink-soft)]">
BENCH STATUS
</p>
<ul className="w-mono mt-3 space-y-2 text-[0.7rem] text-[var(--ink)]">
<li className="flex items-center gap-2">
<div className="flex flex-col gap-8">
<div className="w-paper w-cardindex w-cardindex--alt" style={{ "--tone": "var(--coral)" } as CSSProperties}>
<p className="w-kicker">bench status</p>
<ul className="w-display mt-4 space-y-2.5 text-[0.82rem] text-[var(--ink)]">
<li className="flex items-center gap-2.5">
<Led kind="on" /> smp · accepting players
</li>
<li className="flex items-center gap-2">
<li className="flex items-center gap-2.5">
<Led kind="amber" pulse /> maturita.c · in development
</li>
<li className="flex items-center gap-2">
<li className="flex items-center gap-2.5">
<Led kind="on" /> gym · PPL 3–5×/week
</li>
</ul>
</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>
</Reveal>
</div>
@ -307,11 +316,13 @@ export default function WebSite() {
<SectionHead index="03" label="rack" title="Six machines, one shelf" />
</Reveal>
<Reveal delay={60}>
<div className="w-mt-metal w-rack">
<div className="w-chassis w-rack">
<div className="w-rack__top">
<Led kind="on" pulse slow />
<span className="w-engrave text-[0.62rem] tracking-[0.2em]">RACK 01 · SERVICES</span>
<span className="w-mono ml-auto hidden text-[0.6rem] tracking-[0.14em] text-[var(--ftext-faint)] sm:inline">
<span className="w-display text-[0.7rem] tracking-[0.18em] text-[var(--chassis-ink)]">
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
</span>
<span className="w-rack__vent w-vents" />
@ -319,8 +330,8 @@ export default function WebSite() {
<div className="w-rack__grid">
<span className="w-rails" />
<div>
{SERVICES.map((s) => (
<ServiceUnit key={s.name} {...s} />
{SERVICES.map((s, i) => (
<ServiceUnit key={s.name} tone={TONES[i % TONES.length]} {...s} />
))}
</div>
<span className="w-rails" />
@ -335,8 +346,8 @@ export default function WebSite() {
<SectionHead index="04" label="drawer" title="Eighteen drives, all labelled" />
</Reveal>
<Reveal delay={60}>
<div className="w-mt-metal w-drawer">
<div className="w-mt-plastic w-drawer__inner w-scroll">
<div className="w-chassis w-drawer">
<div className="w-well w-drawer__inner w-scroll">
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
{codingProjects.map((p, i) => {
const { name, status } = splitStatus(p.title);
@ -347,7 +358,12 @@ export default function WebSite() {
target="_blank"
rel="noopener noreferrer"
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}
>
<span className="w-drive__port" aria-hidden="true" />
@ -364,7 +380,7 @@ export default function WebSite() {
})}
</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" />
<span className="w-drawer__handle" aria-hidden="true" />
<i className="w-screw" aria-hidden="true" />
@ -376,18 +392,22 @@ export default function WebSite() {
{/* ------------------------------------------------------ hardware */}
<section id="hardware" className="scroll-mt-28">
<Reveal>
<SectionHead index="05" label="machines" title="Tagged on the pegboard" />
<SectionHead index="05" label="machines" title="Pinned to the cork board" />
</Reveal>
<Reveal delay={60}>
<div className="w-mt-metal w-pegboard">
<div className="w-pegboard">
<div className="w-pegboard__face">
<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__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" />
<div className="grid gap-5 sm:grid-cols-2 xl:grid-cols-3">
{techProjects.map((t, i) => (
<article
key={t.title}
className="w-paper w-hangtag"
style={{ "--tone": TONES[i % TONES.length] } as CSSProperties}
>
<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>
))}
</div>
@ -399,7 +419,7 @@ export default function WebSite() {
{/* -------------------------------------------------- activity feed */}
<section id="gitdxd" className="scroll-mt-28">
<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 delay={60}>
<Report />
@ -418,11 +438,16 @@ export default function WebSite() {
href={f.realUrl || f.url}
target="_blank"
rel="noopener noreferrer"
className="w-mt-paper w-bizcard"
style={{ "--tilt": `${i === 1 ? 0.9 : i === 2 ? -1 : -1.4}deg` } as CSSProperties}
className="w-bizcard"
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-mono mt-1 text-[0.66rem] text-[var(--ink-soft)]">{f.desc}</p>
<p className="w-serif text-[1.2rem] text-[var(--ink)]">{f.name}</p>
<p className="w-mono mt-1 text-[0.68rem] text-[var(--ink-soft)]">{f.desc}</p>
</a>
</Reveal>
))}
@ -432,29 +457,32 @@ export default function WebSite() {
{/* ----------------------------------------------------------- gym */}
<section id="gym" className="scroll-mt-28">
<Reveal>
<SectionHead index="08" label="strength" title="Numbers on the clipboard" />
<SectionHead index="08" label="strength" title="Numbers on the scoreboard" />
</Reveal>
<Reveal delay={60}>
<div className="w-clipboard">
<div className="w-mt-metal w-clipboard__clip" aria-hidden="true" />
<div className="w-mt-paper w-logsheet">
<div className="w-sheet__head">
<span>training log</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 className="w-chassis w-score">
<div className="w-score__head">
<Led kind="on" pulse slow />
<span>TRAINING LOG · 73 KG BODYWEIGHT</span>
<span className="ml-auto hidden sm:inline">3 LIFTS</span>
</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>
</Reveal>
</section>
@ -465,16 +493,23 @@ export default function WebSite() {
<SectionHead index="09" label="patch panel" title="Twelve ports, all live" />
</Reveal>
<Reveal delay={60}>
<div className="w-mt-metal w-patch">
<div className="w-mt-metal-flush w-patch__face">
<div className="w-chassis w-patch">
<div className="w-chassis-2 w-patch__face">
<div className="w-patch__head">
<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" />
</div>
<div className="grid gap-2.5 sm:grid-cols-2 xl:grid-cols-3">
{socials.map((s) => (
<a key={s.name} href={s.url} target="_blank" rel="noopener noreferrer" className="w-port">
{socials.map((s, i) => (
<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__slot" />
</span>
@ -485,7 +520,7 @@ export default function WebSite() {
</a>
))}
</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{" "}
<a className="w-link" href="mailto:dvorak@dvorakmortonxd.dev">
dvorak@dvorakmortonxd.dev
@ -501,11 +536,13 @@ 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-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">
<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.3.0 · GPL-3.0-only · built with Next.js · textures CC0 via{" "}
<p className="w-display text-[0.8rem] tracking-[0.16em] text-[var(--plastic-ink)]">
DVORAKMORTONXD.LOCAL
</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">
credits
</a>
@ -514,15 +551,15 @@ export default function WebSite() {
<button
type="button"
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"
>
<Led kind="amber" /> desktop mode
<Led kind="on" /> desktop mode
</button>
<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" />
<i className="w-screw w-corner-tr" aria-hidden="true" />
<i className="w-screw w-corner-bl" aria-hidden="true" />
</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&rsquo;t, I don&rsquo;t mind
</p>
</Reveal>

View file

@ -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 (
<span className={`w-unit__screws ${className}`}>
{Array.from({ length: count }).map((_, i) => (
<i key={i} className={`w-screw${alu ? " w-screw--alu" : ""}`} />
<i key={i} className="w-screw" />
))}
</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 }) {
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 (
<div className="w-sechead">
<span className="w-mt-alu w-sechead__tab">
<b>{index}</b> {label}
<span className="w-chip w-sechead__tab" style={{ "--tone": tone } as CSSProperties}>
{index} · {label}
</span>
<h2 className="w-display w-paint w-sechead__title">{title}</h2>
</div>
@ -107,9 +119,9 @@ export function Monitor({ children, className = "" }: { children: ReactNode; cla
return (
<div className={`w-monitor ${className}`}>
<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">
<Led kind="amber" />
<Led kind="on" />
<span className="w-monitor__brand">DXD</span>
<span className="w-monitor__osd" aria-hidden="true">
<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 CAP_TONES = ["var(--mint)", "var(--blue)", "var(--yellow)", "var(--coral)", "var(--lilac)", "var(--teal)"];
export function Keyboard() {
return (
@ -134,16 +147,20 @@ export function Keyboard() {
<div className="w-keyboard__deck">
<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">
{KEY_ROWS[0].map((k, i) => (
<span key={k} className="w-key" style={{ "--tone": CAP_TONES[i % CAP_TONES.length] } as CSSProperties}>
{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">
{KEY_ROWS.slice(1).map((row, r) => (
<div key={r} className="w-keyboard__row">
{row.map((k, i) => (
<span
key={k}
className="w-key"
style={{ "--tone": CAP_TONES[(i + r + 2) % CAP_TONES.length] } as CSSProperties}
>
{k}
</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. */
export function MiniRack({ label = "DXD-01" }: { label?: string }) {
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">
<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" />
</div>
<div className="w-rack__grid">
<span className="w-rails" />
<div>
{[0, 1, 2, 3].map((i) => (
<div key={i} className="w-unit !min-h-[46px] !gap-[10px] !py-2">
<span className="w-unit__grille !h-6 !w-9" />
<div key={i} className="w-unit !min-h-[48px] !gap-[10px] !py-2" style={{ "--tone": RACK_TONES[i] } as CSSProperties}>
<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="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]}
</span>
</div>
@ -187,13 +207,13 @@ export function MiniRack({ label = "DXD-01" }: { label?: string }) {
);
}
/** A machined aluminium plate with one number etched into it. */
export function Plaque({ value, label }: { value: string; label: string }) {
/** A big coloured sticker with one number on it. */
export function Plaque({ value, label, tone = "var(--orange)" }: { value: string; label: string; tone?: string }) {
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__label">{label}</span>
<Led kind="amber" className="w-plaque__dot" />
<i className="w-plaque__dot" aria-hidden="true" />
</div>
);
}
@ -209,6 +229,7 @@ export function ServiceUnit({
access,
checkUrl,
disabled,
tone = "var(--teal)",
}: {
name: string;
desc: string;
@ -216,6 +237,7 @@ export function ServiceUnit({
access?: ServiceAccess;
checkUrl?: string;
disabled?: boolean;
tone?: string;
}) {
const [status, setStatus] = useState<"pending" | "up" | "down">("pending");
@ -237,6 +259,7 @@ export function ServiceUnit({
const live = disabled ? "up" : status;
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 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__grille w-grille" aria-hidden="true" />
<span className="w-unit__id">
<span className="w-unit__name w-engrave">{name}</span>
<span className="w-tape mt-1">
<span className="w-unit__name">{name}</span>
<span className="w-tape mt-1.5">
{host}
{accessLabel ? ` · ${accessLabel}` : ""}
</span>
@ -262,7 +285,7 @@ export function ServiceUnit({
</span>
<span className="w-unit__state">
<Led kind={kind} pulse={live === "up"} />
<span className="w-unit__status w-engrave">{label}</span>
<span className={`w-unit__status ${stateClass}`}>{label}</span>
</span>
<Screws />
</>
@ -270,13 +293,20 @@ export function ServiceUnit({
if (disabled) {
return (
<span className="w-unit" aria-disabled="true">
<span className="w-unit" style={{ "--tone": tone } as CSSProperties} aria-disabled="true">
{inner}
</span>
);
}
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}
</a>
);
@ -295,7 +325,7 @@ interface Activity {
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() {
const [weeks, setWeeks] = useState<{ date: string; contribs: number; level: number }[][]>([]);
@ -377,7 +407,7 @@ export function Report() {
return (
<div className="w-printout">
<div className="w-mt-paper w-printout__paper">
<div className="w-printout__paper">
<p className="w-print__head">
<span>GITDXD · ACTIVITY REPORT · 12 MONTHS</span>
<span className="ml-auto flex items-center gap-3">
@ -387,20 +417,20 @@ export function Report() {
</p>
{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>
<p className="w-mono text-[0.62rem] tracking-[0.16em] text-[#4a4133]">TOTAL / 12 MONTHS</p>
<p className="w-display text-[2.6rem] leading-none text-[var(--ink)]">
<p className="w-kicker">total / 12 months</p>
<p className="w-display mt-1 text-[2.6rem] leading-none text-[var(--ink)]">
{state === "loading" ? "—" : total}
</p>
</div>
<p className="w-mono text-[0.62rem] leading-relaxed text-[#6f6450]">
each dot = one day
<p className="w-mono text-[0.64rem] leading-relaxed text-[var(--ink-soft)]">
each square = one day
<br />
darkest = busiest
strongest orange = busiest
</p>
</div>

View file

@ -4,34 +4,32 @@ import "../web.css";
export const metadata: Metadata = {
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 = [
{
name: "Nunito",
by: "Vernon Adams, Cyreal, Jacques Le Bailly",
use: "hardware labels, headings, buttons",
href: "https://fonts.google.com/specimen/Nunito",
},
{
name: "Newsreader",
by: "Production Type",
use: "printed headlines, sheet copy, stamped numbers",
use: "anything printed on paper",
href: "https://fonts.google.com/specimen/Newsreader",
},
{
name: "IBM Plex Sans",
by: "IBM",
use: "body text on the desk",
use: "body copy",
href: "https://fonts.google.com/specimen/IBM+Plex+Sans",
},
{
name: "IBM Plex Mono",
by: "IBM",
use: "engraved labels, terminal text, index cards",
use: "terminal text, receipt, technical labels",
href: "https://fonts.google.com/specimen/IBM+Plex+Mono",
},
];
@ -41,70 +39,34 @@ 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-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>
</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
or unlicensed — the authors are named even where the licence does not require it.
<p className="w-prose w-paint max-w-[62ch] text-[var(--ink-soft)]">
The desk contains <strong>no images at all</strong> — every surface, screw, LED, paper sheet and coffee
mug is drawn in CSS. That means there are no photography or texture licences to track: the only
borrowed work is type.
</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&nbsp;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">
<div className="w-sechead !mb-6">
<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>
<span className="w-chip w-sechead__tab" style={{ "--tone": "var(--teal)" } as React.CSSProperties}>
type
</span>
<h2 className="w-display w-paint w-sechead__title !text-[1.3rem]">
Google Fonts — SIL Open Font License 1.1
</h2>
</div>
<ul className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
{FONTS.map((f) => (
<li key={f.name} className="w-mt-paper w-cardindex w-cardindex--alt">
<p className="w-serif text-[1.05rem] text-[var(--ink)]">{f.name}</p>
<ul className="grid gap-5 sm:grid-cols-2 xl:grid-cols-4">
{FONTS.map((f, i) => (
<li
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)]">
by {f.by}
<br />
@ -122,13 +84,15 @@ export default function Credits() {
<section className="mt-16">
<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>
</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
link to those accounts. Everything written on the paper is mine. The desktop mode (the KDE
simulation) uses its own separate assets.
<p className="w-prose w-paint max-w-[62ch] text-[var(--ink-soft)]">
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 — and every pixel of the desk itself — is
mine. The desktop mode (the KDE simulation) uses its own separate assets.
</p>
</section>

View file

@ -37,15 +37,20 @@
--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"] {
color-scheme: dark;
--accent: #fe8019;
--accent2: #d65d0e;
--accent3: #fabd2f;
--green: #b8bb26;
--red: #fb4934;
--gray: #a89984;
color-scheme: light;
--bg: #f7e7c6;
--bg-raised: #fffdf6;
--bg-well: #eed9b0;
--accent: #f4823a;
--accent2: #d4611c;
--accent3: #f2c14e;
--green: #58bd72;
--red: #ea6f6a;
--gray: #7f7768;
}
@theme inline {
@ -71,7 +76,7 @@ html[data-theme="web"] {
}
html[data-theme="web"] ::selection {
background: rgba(254, 128, 25, 0.28);
background: rgba(244, 130, 58, 0.3);
color: var(--ink);
}
@ -88,12 +93,12 @@ html[data-theme="web"] ::-webkit-scrollbar-track {
background: var(--bg-well);
}
html[data-theme="web"] ::-webkit-scrollbar-thumb {
background: #3c3836;
background: #d9c49b;
border: 3px solid var(--bg-well);
border-radius: 99px;
}
html[data-theme="web"] ::-webkit-scrollbar-thumb:hover {
background: #504945;
background: #c9b184;
}
/* Phones never get the desktop-mode button. */

View file

@ -1,10 +1,10 @@
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 ThemeScript from "./components/ThemeScript";
// Display serif for headlines, Plex for body/labels — same pairing as the
// reference editorial site.
// The web desk pairs a rounded sans (hardware labels, headings) with a printed
// serif (anything on paper) and a mono (terminal text + technical labels).
const news = Newsreader({
variable: "--font-news",
subsets: ["latin"],
@ -24,6 +24,12 @@ const plexMono = IBM_Plex_Mono({
weight: ["400", "500"],
display: "swap",
});
const round = Nunito({
variable: "--font-round",
subsets: ["latin"],
weight: ["600", "700", "800"],
display: "swap",
});
export const metadata: Metadata = {
title: "dvorakmortonxd.dev",
@ -51,7 +57,7 @@ export default function RootLayout({
lang="en"
data-theme="web"
suppressHydrationWarning
className={`${news.variable} ${plex.variable} ${plexMono.variable}`}
className={`${news.variable} ${plex.variable} ${plexMono.variable} ${round.variable}`}
>
<body className="antialiased">
<noscript>

File diff suppressed because it is too large Load diff