web theme: drop terminal leftovers (prompt hero, filename eyebrows, git command lines), make light buttons readable, remove unused assets

This commit is contained in:
Martin Dvořák 2026-09-30 12:22:15 +00:00
commit 7beb197f72
10 changed files with 129 additions and 105 deletions

View file

@ -72,7 +72,7 @@ more.
- **Fonts:** the web theme uses the system sans stack; Monocraft (self-hosted in `public/`) is
used by the desktop theme's SMP menu and the in-desktop terminal.
- **Images:** `next/image`
- **Version:** `2.2.0` (see `package.json`)
- **Version:** `2.2.1` (see `package.json`)
## 🚀 Running Locally

View file

@ -43,7 +43,7 @@ frosted-glass **web** site, and an alternate KDE/Plasma **desktop simulation**
utilities in the components (Tailwind is present via PostCSS but the look is
hand-written CSS).
- Path alias: `@/*` → `src/*`.
- Version: `2.2.0` (see `package.json`).
- Version: `2.2.1` (see `package.json`).
### Scripts
```bash
@ -123,30 +123,44 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
`skeuo` = KDE Plasma desktop simulation (desktop-only; the web sidebar's
"Desktop" control switches to it, and it has a "Web" control in its tray).
### Web theme + light/dark (v2.2.0 rework)
### Web theme + light/dark (v2.2 rework)
- `WebLayout.tsx` is the default shell. Layout is a **sticky sidebar** (brand,
section nav, light/dark switch, desktop switch, contact/clock) beside a single
content column of `.glass` panels: hero (with a compact "Find me" socials row),
a `.web-quick` 2-up (`now.txt` + `gym_sbd.db`), then services, activity,
a `.web-quick` 2-up ("Right now" + "Gym PRs"), then services, activity,
projects, hardware, features (Revival/RustyWolf) and friends. Projects/hardware
use a CSS multi-column **masonry** gallery (`.project-grid`) so short cards
don't leave half-empty grid slots. The old stacked TUI panel layout and the
`SlBackground` ASCII train were removed with it.
- **Not a terminal.** The web theme deliberately has no TUI chrome: the hero is
a name + blurb (no `$ whoami` prompt, no blinking cursor), section eyebrows are
plain words (not `README.MD` / `TECH_LIST.JSON` filenames), and the activity
panel renders without the `martin@git…:~$ echo …` / `git log -n 3` command
lines. `ContributionHeatmap` takes `variant?: "terminal" | "web"` (default
`terminal`); `WebLayout` passes `variant="web"`, the desktop app keeps the
terminal look. Don't reintroduce command prompts in the web variant.
- **Grid gotcha:** the mobile column is `minmax(0, 1fr)`, never bare `1fr`. A
`1fr` track has an automatic `min-content` floor, and with a wide child that
blew the single column out to ~2x the viewport (horizontal scroll + clipped
content on phones). Keep the `minmax(0, …)`.
- Glassmorphism lives in one utility class, `.glass`, in `web.css`: a subtle
`linear-gradient` over `--glass-hi`/`--glass-lo` with
`backdrop-filter: blur(20px) saturate(165%)`. The page blobs behind the panels
`backdrop-filter: blur(28px) saturate(180%)`. The page blobs behind the panels
are `.web-bg` (a fixed radial-gradient layer at `z-index: 0`; `.web-root` sits
at `z-index: 1`).
- Interactive surfaces (`.web-ctl`, pills, `.service-button`, `.social-button`,
`.project-card`, `.gym-item`, `.contribution-entry`, `.heatmap-container`) read
the `--btn-bg` / `--btn-bg-hi` / `--btn-border` / `--btn-shadow` tokens with
the glass tokens as **fallbacks** (`var(--btn-bg, var(--glass-lo))`). The
tokens are only defined under `html[data-theme="web"]`, so the desktop theme
falls back to the old values and is unaffected. Light-mode buttons need the
extra opacity + a visible (dark, low-alpha) outline or they melt into the pale
background.
- Colours are tokenised in `web.css` under `:root` (light) and
`html[data-theme="web"][data-scheme="dark"]` (dark). The shared token names
(`--fg`, `--gray`, `--accent`, `--green`, …) are reused so `ServiceButton` and
`ContributionHeatmap` need no changes. `skeuo.css` overrides the same names
while the desktop theme is active, so the two themes don't bleed into each
other.
(`--fg`, `--gray`, `--accent`, `--green`, …) are reused by `ServiceButton` and
`ContributionHeatmap`. `skeuo.css` overrides the same names while the desktop
theme is active, so the two themes don't bleed into each other.
- The desktop switch (`.web-ctl-desktop`) is hidden on phones: under the
`max-width: 960px` breakpoint and under `(hover: none) and (pointer: coarse)`.
There is no device sniff anymore — phones just get the responsive web shell.

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "dvorakmortonxd",
"version": "2.2.0",
"version": "2.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dvorakmortonxd",
"version": "2.2.0",
"version": "2.2.1",
"dependencies": {
"next": "^16.3.5",
"react": "^19.3.0",

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 KiB

View file

@ -25,7 +25,8 @@ interface ActivityPayload {
};
}
export default function ContributionHeatmap() {
export default function ContributionHeatmap({ variant = "terminal" }: { variant?: "terminal" | "web" }) {
const isWeb = variant === "web";
const [weeks, setWeeks] = useState<{ date: string; contribs: number; level: number }[][]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
@ -123,11 +124,13 @@ export default function ContributionHeatmap() {
return (
<div className="contribution-heatmap">
<div className="contribution-command-row">
<div className="contribution-command">
<span style={{ color: "var(--green)" }}>martin@git.dvorakmortonxd.dev</span>:<span style={{ color: "var(--gray)" }}>~</span>$ echo &#34;<span style={{ color: "var(--accent)" }}>{totalContribs}</span> contributions in the last year&#34;
{!isWeb && (
<div className="contribution-command-row">
<div className="contribution-command">
<span style={{ color: "var(--green)" }}>martin@git.dvorakmortonxd.dev</span>:<span style={{ color: "var(--gray)" }}>~</span>$ echo &#34;<span style={{ color: "var(--accent)" }}>{totalContribs}</span> contributions in the last year&#34;
</div>
</div>
</div>
)}
<div className="contribution-total-title">
<span style={{ color: "var(--accent)" }}>{totalContribs}</span> contributions in the last year
</div>
@ -169,9 +172,13 @@ export default function ContributionHeatmap() {
{activities.length > 0 && (
<div className="contribution-log">
<div className="contribution-command contribution-log-command">
<span style={{ color: "var(--green)" }}>martin@git.dvorakmortonxd.dev</span>:<span style={{ color: "var(--gray)" }}>~</span>$ git log -n 3 <span style={{ color: "var(--gray)" }}>#displays my 3 latest contributions</span>
</div>
{isWeb ? (
<div className="contribution-log-title">Latest commits</div>
) : (
<div className="contribution-command contribution-log-command">
<span style={{ color: "var(--green)" }}>martin@git.dvorakmortonxd.dev</span>:<span style={{ color: "var(--gray)" }}>~</span>$ git log -n 3 <span style={{ color: "var(--gray)" }}>#displays my 3 latest contributions</span>
</div>
)}
{activities.map((act, idx) => {
let msg = "No message";
try {
@ -190,7 +197,7 @@ export default function ContributionHeatmap() {
return (
<div key={idx} className="contribution-entry">
<div className="contribution-entry-header">
<span className="contribution-repo">repo: {act.repo?.full_name || "unknown"}</span>
<span className="contribution-repo">{isWeb ? "" : "repo: "}{act.repo?.full_name || "unknown"}</span>
<span className="contribution-date">
{created.toLocaleDateString()}-{created.toLocaleTimeString()}
</span>

View file

@ -228,12 +228,11 @@ export default function WebLayout() {
<main className="web-main">
<header id="overview" className="web-hero glass">
<span className="web-eyebrow">README.MD</span>
<h1 className="web-title">$ whoami</h1>
<span className="web-eyebrow">About me</span>
<h1 className="web-title">Martin Dvořák</h1>
<p className="web-lead">
Welcome to my personal website! Here I show off my projects and the hardware I
own, as well as my self-hosted services.
<span className="cursor-blink" />
</p>
<div className="web-hero-actions">
<a className="web-pill" href="#projects">
@ -252,7 +251,7 @@ export default function WebLayout() {
<div className="web-quick">
<article id="now" className="web-tile glass">
<h3 className="web-tile-title">now.txt</h3>
<h3 className="web-tile-title">Right now</h3>
<p className="web-tile-text">
I&#39;m currently hosting an SMP (dm me on discord). Also I&#39;m pretty much
cooking daily, because im bulking through out the non-summer seasons. And me
@ -263,7 +262,7 @@ export default function WebLayout() {
</article>
<article className="web-tile glass">
<h3 className="web-tile-title">gym_sbd.db</h3>
<h3 className="web-tile-title">Gym PRs</h3>
<Gym />
<p className="web-tile-note">Current squat / bench / deadlift PRs at ~73kg BW.</p>
</article>
@ -273,11 +272,11 @@ export default function WebLayout() {
<Services />
</Section>
<Section id="activity" eyebrow="GITDXD.GIT" title="Contribution activity">
<ContributionHeatmap />
<Section id="activity" eyebrow="GitDXD" title="Contribution activity">
<ContributionHeatmap variant="web" />
</Section>
<Section id="projects" eyebrow="PROJECTS.DOCX" title="Things I've worked on">
<Section id="projects" eyebrow="Portfolio" title="Things I've worked on">
<div className="project-grid">
{codingProjects.map((project, idx) => (
<a
@ -298,7 +297,7 @@ export default function WebLayout() {
</div>
</Section>
<Section id="hardware" eyebrow="TECH_LIST.JSON" title="Hardware I own">
<Section id="hardware" eyebrow="My gear" title="Hardware I own">
<div className="project-grid">
{techProjects.map((project) => (
<div key={project.title} className="project-card">
@ -330,7 +329,7 @@ export default function WebLayout() {
</p>
</Section>
<Section eyebrow="RUSTYWOLF.RS" title="Introducing RustyWolf" className="web-feature">
<Section eyebrow="RustyWolf" title="Introducing RustyWolf" className="web-feature">
<FeatureShot
src="/RustyWolfPreview.webp"
alt="RustyWolf Browser Preview"
@ -347,7 +346,7 @@ export default function WebLayout() {
</Section>
</div>
<Section id="friends" eyebrow="FRIENDS.TSX" title="Friend's websites and projects">
<Section id="friends" eyebrow="Links" title="Friend's websites and projects">
<div className="services-buttons">
{friends.map((friend, idx) => (
<ServiceButton
@ -372,7 +371,7 @@ export default function WebLayout() {
</Section>
<footer className="web-footer glass">
<span className="web-footer-label">v2.2.0</span>
<span className="web-footer-label">v2.2.1</span>
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just
follow the GPL3.0 License)
</footer>

View file

@ -1,5 +1,27 @@
@import "tailwindcss";
/* Monocraft is the desktop theme's pixel font (SMP menu + in-desktop terminal).
It is declared globally because the desktop shell is mounting/unmounting
alongside the web shell. */
@font-face {
font-family: "Monocraft";
src: url("/Monocraft.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Monocraft";
src: url("/Monocraft-Bold.ttf") format("truetype");
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "Monocraft";
src: url("/Monocraft-Italic.ttf") format("truetype");
font-weight: normal;
font-style: italic;
}
:root {
--background: #1d2021;
--foreground: #ebdbb2;

View file

@ -1,29 +1,3 @@
/* ============================================================
FONT FACES
------------------------------------------------------------
Monocraft is no longer the web theme's font, but it is still
used by the desktop theme's Minecraft-style SMP menu and the
in-desktop terminal, so the faces stay registered here.
============================================================ */
@font-face {
font-family: "Monocraft";
src: url("/Monocraft.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Monocraft";
src: url("/Monocraft-Bold.ttf") format("truetype");
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "Monocraft";
src: url("/Monocraft-Italic.ttf") format("truetype");
font-weight: normal;
font-style: italic;
}
/* ============================================================
WEB THEME — light frosted-glass website shell.
------------------------------------------------------------
@ -95,6 +69,11 @@ html[data-theme="web"][data-scheme="dark"] {
--blob-c: rgba(236, 72, 153, 0.18);
--blob-d: rgba(45, 212, 191, 0.16);
--btn-bg: rgba(255, 255, 255, 0.06);
--btn-bg-hi: rgba(255, 255, 255, 0.12);
--btn-border: rgba(255, 255, 255, 0.16);
--btn-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.9);
--bg-hard: rgba(255, 255, 255, 0.08);
--green: #34d399;
--aqua: #2dd4bf;
@ -106,6 +85,15 @@ html[data-theme="web"][data-scheme="dark"] {
html[data-theme="web"] {
scroll-behavior: smooth;
/* Interactive surfaces (buttons, cards, chips). Kept a step more opaque and
more clearly outlined than the glass panels, so they don't melt into the
light background. The base rules read these with the old glass tokens as
fallbacks, which keeps the desktop theme untouched. */
--btn-bg: rgba(255, 255, 255, 0.62);
--btn-bg-hi: rgba(255, 255, 255, 0.92);
--btn-border: rgba(20, 28, 52, 0.14);
--btn-shadow: 0 8px 20px -12px rgba(23, 32, 60, 0.45);
}
html[data-theme="web"] body {
@ -261,8 +249,9 @@ html[data-theme="web"] body {
gap: 10px;
padding: 9px 12px;
border-radius: 12px;
border: 1px solid var(--glass-border);
background: var(--glass-lo);
border: 1px solid var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
color: var(--fg);
font: inherit;
font-size: 0.82rem;
@ -272,7 +261,7 @@ html[data-theme="web"] body {
}
.web-ctl:hover {
border-color: var(--accent-line);
background: var(--glass-hi);
background: var(--btn-bg-hi, var(--glass-hi));
box-shadow: 0 0 0 1px var(--accent-soft);
}
.web-ctl-icon {
@ -290,7 +279,7 @@ html[data-theme="web"] body {
height: 20px;
border-radius: 999px;
background: var(--bg-hard);
border: 1px solid var(--glass-border);
border: 1px solid var(--btn-border, var(--glass-border));
transition: background-color 0.2s ease, border-color 0.2s ease;
flex-shrink: 0;
}
@ -385,13 +374,13 @@ html[data-theme="web"] body {
box-shadow: 0 16px 30px -12px var(--accent);
}
.web-pill.ghost {
background: var(--glass-lo);
background: var(--btn-bg, var(--glass-lo));
color: var(--fg);
border: 1px solid var(--glass-border);
box-shadow: none;
border: 1px solid var(--btn-border, var(--glass-border));
box-shadow: var(--btn-shadow, none);
}
.web-pill.ghost:hover {
background: var(--glass-hi);
background: var(--btn-bg-hi, var(--glass-hi));
border-color: var(--accent-line);
}
@ -514,8 +503,9 @@ html[data-theme="web"] body {
gap: 12px;
padding: 14px 16px;
border-radius: 16px;
border: 1px solid var(--glass-border);
background: var(--glass-lo);
border: 1px solid var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
color: var(--fg);
text-decoration: none;
min-width: 0;
@ -525,7 +515,7 @@ html[data-theme="web"] body {
.service-button:hover {
transform: translateY(-3px);
border-color: var(--accent-line);
background: var(--glass-hi);
background: var(--btn-bg-hi, var(--glass-hi));
box-shadow: 0 18px 34px -18px rgba(23, 32, 60, 0.5), 0 0 0 1px var(--accent-soft);
}
.service-button.disabled {
@ -533,9 +523,9 @@ html[data-theme="web"] body {
}
.service-button.disabled:hover {
transform: none;
border-color: var(--glass-border);
background: var(--glass-lo);
box-shadow: none;
border-color: var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
}
.service-button.disabled:hover .service-icon {
transform: none;
@ -651,8 +641,9 @@ html[data-theme="web"][data-scheme="dark"] .friend-icon {
margin-bottom: 14px;
padding: 12px;
border-radius: 16px;
border: 1px solid var(--glass-border);
background: var(--glass-lo);
border: 1px solid var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
color: inherit;
text-decoration: none;
transition: transform 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease,
@ -661,7 +652,7 @@ html[data-theme="web"][data-scheme="dark"] .friend-icon {
.project-card:hover {
transform: translateY(-4px);
border-color: var(--accent-line);
background: var(--glass-hi);
background: var(--btn-bg-hi, var(--glass-hi));
box-shadow: 0 20px 38px -20px rgba(23, 32, 60, 0.55);
}
.project-image {
@ -701,8 +692,9 @@ html[data-theme="web"][data-scheme="dark"] .friend-icon {
gap: 9px;
padding: 9px 12px;
border-radius: 12px;
border: 1px solid transparent;
background: var(--glass-lo);
border: 1px solid var(--btn-border, transparent);
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
color: var(--fg);
font-size: 0.82rem;
font-weight: 550;
@ -711,8 +703,8 @@ html[data-theme="web"][data-scheme="dark"] .friend-icon {
}
.social-button:hover {
transform: translateY(-2px);
background: var(--glass-hi);
border-color: var(--glass-border);
background: var(--btn-bg-hi, var(--glass-hi));
border-color: var(--btn-border, var(--glass-border));
color: var(--accent);
}
.social-icon {
@ -750,8 +742,9 @@ html[data-theme="web"][data-scheme="dark"] .social-icon {
gap: 6px;
padding: 12px 8px;
border-radius: 14px;
border: 1px solid var(--glass-border);
background: var(--glass-lo);
border: 1px solid var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
text-align: center;
transition: border-color 0.18s ease, transform 0.18s ease;
}
@ -806,9 +799,9 @@ html[data-theme="web"][data-scheme="dark"] .social-icon {
display: flex;
gap: 3px;
padding: 6px;
border: 1px solid var(--glass-border);
border: 1px solid var(--btn-border, var(--glass-border));
border-radius: 14px;
background: var(--glass-lo);
background: var(--btn-bg, var(--glass-lo));
overflow-x: auto;
scrollbar-width: none;
}
@ -912,6 +905,13 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
min-width: 0;
margin-top: 4px;
}
.contribution-log-title {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--web-muted);
}
.contribution-entry {
display: flex;
flex-direction: column;
@ -919,8 +919,9 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
min-width: 0;
padding: 10px 12px;
border-radius: 12px;
border: 1px solid var(--glass-border);
background: var(--glass-lo);
border: 1px solid var(--btn-border, var(--glass-border));
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
font-size: 0.78rem;
}
.contribution-entry-header {
@ -982,25 +983,6 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
/* ============================================================
SHARED BITS
============================================================ */
.cursor-blink {
display: inline-block;
width: 7px;
height: 1.05em;
margin-left: 4px;
vertical-align: text-bottom;
background: var(--accent);
animation: cursorBlink 1s step-end infinite;
}
@keyframes cursorBlink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
.joker-flash-overlay {
position: fixed;
inset: 0;