replace raster assets with CSS: emoji gradient cards, mock windows (add forge), gradient wallpaper

This commit is contained in:
dvorakmortonxd 2026-10-05 14:28:41 +02:00
commit dae4f1c414
47 changed files with 333 additions and 184 deletions

View file

@ -36,6 +36,11 @@ more.
hand-built CSS mock windows instead of screenshots: a little necromancy game scene whose hand-built CSS mock windows instead of screenshots: a little necromancy game scene whose
blinking "PRESS START" launches the game, and a little browser window with tabs in the blinking "PRESS START" launches the game, and a little browser window with tabs in the
title bar, a toolbar and a start page. title bar, a toolbar and a start page.
- **Asset-free cards & wallpaper** — every project/hardware card is a CSS gradient tile with
an emoji glyph (no screenshots), the desktop shell's app previews are the same CSS mock
windows (a git-forge page for GitDXD), and its wallpaper is a layered CSS gradient. The
only real images left are the logo, the social SVGs, the Joker easter egg and the Gentoo
mark.
- **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per - **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per
cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never
collide. collide.
@ -76,8 +81,9 @@ more.
imported for the base layer but the UI is custom CSS. imported for the base layer but the UI is custom CSS.
- **Fonts:** the web theme uses the system sans stack; Monocraft (self-hosted in `public/`) is - **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. used by the desktop theme's SMP menu and the in-desktop terminal.
- **Images:** `next/image` - **Images:** `next/image` — only the logo (`src/app/icon.png`), the social SVGs, the Joker
- **Version:** `2.2.13` (see `package.json`) easter egg and the Gentoo mark; all cards, feature/app previews and the wallpaper are CSS.
- **Version:** `2.2.14` (see `package.json`)
- **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched - **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched
release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via
`overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**, `overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**,
@ -187,7 +193,7 @@ src/app/
globals.css → base/reset styles + Tailwind import globals.css → base/reset styles + Tailwind import
web.css → web theme styles (frosted glass, light + dark) web.css → web theme styles (frosted glass, light + dark)
skeuo.css → desktop theme styles (KDE/Plasma shell) skeuo.css → desktop theme styles (KDE/Plasma shell)
data.ts → socials, codingProjects, techProjects, friends, wallpapers data.ts → socials, codingProjects, techProjects, friends
revival-inc/page.tsx → embeds the bundled game at /public/revival-inc/index.html revival-inc/page.tsx → embeds the bundled game at /public/revival-inc/index.html
api/ api/
heatmap/route.ts → contribution heatmap proxy heatmap/route.ts → contribution heatmap proxy
@ -216,7 +222,7 @@ deploy/
install-system-service.sh → installs the system unit (run with sudo/doas) install-system-service.sh → installs the system unit (run with sudo/doas)
vendor/ vendor/
braces/ → locally patched braces@3.0.4 (depth guards for CVE-2026-93687; no upstream fix) braces/ → locally patched braces@3.0.4 (depth guards for CVE-2026-93687; no upstream fix)
public/ → fonts, wallpapers, icons, /revival-inc game, llms.txt, robots.txt, sitemap.xml public/ → fonts, social SVGs, /revival-inc game, llms.txt, robots.txt, sitemap.xml
``` ```
## 🌍 Deploying? ## 🌍 Deploying?

View file

@ -34,7 +34,7 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a
## Bump the version on EVERY prompt — also do this every time ## Bump the version on EVERY prompt — also do this every time
Another standing requirement: increment the **patch** version (`x.x.+1`, so Another standing requirement: increment the **patch** version (`x.x.+1`, so
`2.2.12` → `2.2.13` → …) as part of every task, even the small ones. Keep all of `2.2.13` → `2.2.14` → …) as part of every task, even the small ones. Keep all of
these in sync: these in sync:
- `package.json` → `"version"` - `package.json` → `"version"`
@ -56,7 +56,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 utilities in the components (Tailwind is present via PostCSS but the look is
hand-written CSS). hand-written CSS).
- Path alias: `@/*` → `src/*`. - Path alias: `@/*` → `src/*`.
- Version: `2.2.13` (see `package.json`). - Version: `2.2.14` (see `package.json`).
### Scripts ### Scripts
```bash ```bash
@ -90,7 +90,7 @@ src/app/
page.tsx → renders <ThemeRoot/>, imports web.css + skeuo.css page.tsx → renders <ThemeRoot/>, imports web.css + skeuo.css
layout.tsx → html shell, metadata, ThemeScript layout.tsx → html shell, metadata, ThemeScript
globals.css → base/reset styles + Tailwind import globals.css → base/reset styles + Tailwind import
data.ts → socials, codingProjects, techProjects, friends, wallpapers data.ts → socials, codingProjects, techProjects, friends
web.css → web theme: frosted glass, light + dark tokens web.css → web theme: frosted glass, light + dark tokens
skeuo.css → Windows/Plasma desktop styles (desktop theme only) skeuo.css → Windows/Plasma desktop styles (desktop theme only)
revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html
@ -103,7 +103,8 @@ src/app/
components/ components/
ThemeRoot.tsx → picks which shell to mount (the router of the UI) ThemeRoot.tsx → picks which shell to mount (the router of the UI)
WebLayout.tsx → "web" theme (default): sticky sidebar + glass content WebLayout.tsx → "web" theme (default): sticky sidebar + glass content
FeatureWindow.tsx → decorative CSS "windows" for the two feature panels (game / browser) FeatureWindow.tsx → decorative CSS "windows" (game / browser / forge); used in the
web feature panels and the desktop app windows
WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar
WinWindow.tsx → a single draggable/resizable window WinWindow.tsx → a single draggable/resizable window
apps.tsx → APPS registry + the content of each app + in-app terminal apps.tsx → APPS registry + the content of each app + in-app terminal
@ -158,7 +159,11 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
(Josef is 🧌 goblin, the rest 🧙 — the old per-friend SVGs are gone). (Josef is 🧌 goblin, the rest 🧙 — the old per-friend SVGs are gone).
Projects/hardware use a CSS multi-column Projects/hardware use a CSS multi-column
**masonry** gallery (`.project-grid`) so short cards don't leave half-empty **masonry** gallery (`.project-grid`) so short cards don't leave half-empty
grid slots; on phones it drops to 2 columns with a shorter card image. The two feature panels (`.web-split`) stretch grid slots; on phones it drops to 2 columns with a shorter tile. Cards are
**asset-free**: each renders a `.project-thumb` — a `.thumb-N` gradient
(N = index % 6) with the item's `emoji` glyph centred — instead of a
screenshot. `codingProjects` / `techProjects` in `data.ts` carry `emoji`
(no `img`/`image`). The two feature panels (`.web-split`) stretch
to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow` to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow`
component) — a decorative, **asset-free CSS mock-up** rather than a screenshot: component) — a decorative, **asset-free CSS mock-up** rather than a screenshot:
Revival Inc. is a little necromancy game scene (night sky, moon, tombstones, Revival Inc. is a little necromancy game scene (night sky, moon, tombstones,
@ -168,10 +173,18 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
`aria-hidden` but "PRESS START" is a **real link** to the game `aria-hidden` but "PRESS START" is a **real link** to the game
(`https://dvorakmortonxd.dev/revival-inc`). The real copy stays in the (`https://dvorakmortonxd.dev/revival-inc`). The real copy stays in the
`.web-feature-text` paragraph below. `.web-feature-text` paragraph below.
The old raster feature shots (`.web-shot` + `next/image`) were removed from the `FeatureWindow` also has a **`forge`** variant (a little git-forge repo page)
web theme only — `revivalinc.webp` / `RustyWolfPreview.webp` are still used by and all three are reused inside the desktop app windows (GitDXD → forge,
the desktop theme and the projects gallery, so the files stay. The old stacked Revival Inc. → game, RustyWolf → browser), so there are no app-preview
TUI panel layout and the `SlBackground` ASCII train were removed earlier. screenshots. The old stacked TUI panel layout and the `SlBackground` ASCII
train were removed earlier.
- **No raster UI assets.** Every card, feature panel, app preview and the desktop
wallpaper is drawn in CSS: project/hardware cards are `.project-thumb` gradient
tiles with an `emoji`, the feature/app previews are `FeatureWindow` mock windows,
and `.win-desktop` carries a layered gradient "wallpaper" (there is no
`wallpaper.jpg`). Only the social SVGs, the site logo (`src/app/icon.png`), the
Joker easter egg (`joker.webp`) and the Gentoo mark (`gentoo.svg`) remain as
real image files.
- **Not a terminal.** The web theme deliberately has no TUI chrome: the hero is - **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 a name + blurb (no `$ whoami` prompt, no blinking cursor), section eyebrows are
plain words (not `README.MD` / `TECH_LIST.JSON` filenames), and the activity plain words (not `README.MD` / `TECH_LIST.JSON` filenames), and the activity
@ -473,9 +486,9 @@ knows about icon ids, via `APP_BY_ID`.
contain `data-theme="web"` / `data-scheme="dark"` and the `.web-root` markup. contain `data-theme="web"` / `data-scheme="dark"` and the `.web-root` markup.
Theme + scheme are read pre-paint from `localStorage.theme` / `.scheme`, so a Theme + scheme are read pre-paint from `localStorage.theme` / `.scheme`, so a
quick check is to set those in devtools and reload. quick check is to set those in devtools and reload.
- `next/image` is lazy by default, so a headless full-page screenshot shows the - There are almost no raster images left (only the logo, the social SVGs, the
below-the-fold feature shots as blank boxes. Scroll the page Joker easter egg and the Gentoo mark), so a headless full-page screenshot no
slowly first and they load; this is a capture artifact, not a bug. Headless longer shows blank lazy-image boxes below the fold. Headless
Chromium lives at `~/.cache/ms-playwright/chromium-*/chrome-linux64/chrome` Chromium lives at `~/.cache/ms-playwright/chromium-*/chrome-linux64/chrome`
(drive it with `--headless=new --remote-debugging-port=…` and CDP). (drive it with `--headless=new --remote-debugging-port=…` and CDP).
- A dev server may already be running on port 3000 — on this host it is normally - A dev server may already be running on port 3000 — on this host it is normally

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "dvorakmortonxd", "name": "dvorakmortonxd",
"version": "2.2.13", "version": "2.2.14",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "dvorakmortonxd", "name": "dvorakmortonxd",
"version": "2.2.13", "version": "2.2.14",
"dependencies": { "dependencies": {
"next": "^16.3.8", "next": "^16.3.8",
"react": "^19.3.0", "react": "^19.3.0",

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -93,7 +93,45 @@ function BrowserBody() {
); );
} }
export default function FeatureWindow({ variant }: { variant: "game" | "browser" }) { function ForgeBody() {
const files = ["src/", "public/", "package.json", "README.md", "LICENSE"];
return (
<div className="fw-forge">
<div className="fw-forge-head">
<span className="fw-forge-repo">
dvorakmortonxd / <b>dvorakmortonxd.dev</b>
</span>
<span className="fw-forge-tabs">
<span className="fw-forge-tab fw-forge-tab-active">Code</span>
<span className="fw-forge-tab">Issues</span>
<span className="fw-forge-tab">Pull requests</span>
</span>
</div>
<div className="fw-forge-body">
<ul className="fw-forge-files">
{files.map((f) => (
<li key={f}>
<span className="fw-forge-file-icon" />
{f}
</li>
))}
</ul>
<div className="fw-forge-code">
{Array.from({ length: 8 }).map((_, i) => (
<span key={i} className="fw-code-line" />
))}
</div>
</div>
<div className="fw-forge-lang">
<span className="fw-lang-seg fw-lang-ts" />
<span className="fw-lang-seg fw-lang-css" />
<span className="fw-lang-seg fw-lang-md" />
</div>
</div>
);
}
export default function FeatureWindow({ variant }: { variant: "game" | "browser" | "forge" }) {
if (variant === "game") { if (variant === "game") {
return ( return (
<Chrome title="revival-inc — mini jame gam #55" decorative={false}> <Chrome title="revival-inc — mini jame gam #55" decorative={false}>
@ -101,6 +139,13 @@ export default function FeatureWindow({ variant }: { variant: "game" | "browser"
</Chrome> </Chrome>
); );
} }
if (variant === "forge") {
return (
<Chrome title="GitDXD — dvorakmortonxd/dvorakmortonxd.dev">
<ForgeBody />
</Chrome>
);
}
return ( return (
<Chrome <Chrome
tabs={ tabs={

View file

@ -276,10 +276,9 @@ export default function WebLayout() {
rel="noopener noreferrer" rel="noopener noreferrer"
className="project-card" className="project-card"
> >
<div <div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
className="project-image" {project.emoji}
style={{ backgroundImage: `url(${project.img})` }} </div>
/>
<h4 className="project-title">{project.title}</h4> <h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p> <p className="project-desc">{project.desc}</p>
</a> </a>
@ -289,12 +288,11 @@ export default function WebLayout() {
<Section id="hardware" eyebrow="My gear" title="Hardware I own"> <Section id="hardware" eyebrow="My gear" title="Hardware I own">
<div className="project-grid"> <div className="project-grid">
{techProjects.map((project) => ( {techProjects.map((project, idx) => (
<div key={project.title} className="project-card"> <div key={project.title} className="project-card">
<div <div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
className="project-image" {project.emoji}
style={{ backgroundImage: `url(${project.image})` }} </div>
/>
<h4 className="project-title">{project.title}</h4> <h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p> <p className="project-desc">{project.desc}</p>
</div> </div>
@ -318,7 +316,7 @@ export default function WebLayout() {
</Section> </Section>
<footer className="web-footer glass"> <footer className="web-footer glass">
<span className="web-footer-label">v2.2.13</span> <span className="web-footer-label">v2.2.14</span>
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just © 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just
follow the GPL3.0 License) follow the GPL3.0 License)
</footer> </footer>

View file

@ -7,7 +7,7 @@ import WinWindow, { type WinState } from "./WinWindow";
import Clock from "./Clock"; import Clock from "./Clock";
import { useTheme } from "./useTheme"; import { useTheme } from "./useTheme";
import { useDesktopIcons } from "./useDesktopIcons"; import { useDesktopIcons } from "./useDesktopIcons";
import { WALLPAPER, friends } from "../data"; import { friends } from "../data";
// The panel sits at `bottom: 10px` with a height of 44px, so its top edge is // The panel sits at `bottom: 10px` with a height of 44px, so its top edge is
// 54px above the viewport bottom. Windows clamp 5px above that (59), so a // 54px above the viewport bottom. Windows clamp 5px above that (59), so a
@ -24,7 +24,6 @@ interface Shortcut {
url?: string; url?: string;
appId?: string; appId?: string;
desc?: string; desc?: string;
image?: string;
/** Show the real site in an iframe. Only for sites that allow framing. */ /** Show the real site in an iframe. Only for sites that allow framing. */
embed?: boolean; embed?: boolean;
/** Frame src when it differs from `url` (e.g. a local mirror of a blocking site). */ /** Frame src when it differs from `url` (e.g. a local mirror of a blocking site). */
@ -37,7 +36,6 @@ interface IconEntry {
title: string; title: string;
glyph: ReactNode; glyph: ReactNode;
color?: string; color?: string;
image?: string;
onOpen: () => void; onOpen: () => void;
} }
@ -224,7 +222,6 @@ export default function WindowsDesktop() {
url: s.url, url: s.url,
desc: s.desc, desc: s.desc,
icon: s.icon, icon: s.icon,
image: s.image,
embed: s.embed, embed: s.embed,
embedUrl: s.embedUrl, embedUrl: s.embedUrl,
}); });
@ -336,8 +333,6 @@ export default function WindowsDesktop() {
} }
}; };
const wallpaper = `url('${WALLPAPER}')`;
const iconClick = (id: string, action: () => void) => { const iconClick = (id: string, action: () => void) => {
if (consumeDrag()) return; if (consumeDrag()) return;
activateIcon(id, action); activateIcon(id, action);
@ -389,18 +384,7 @@ export default function WindowsDesktop() {
className="win-icon-glyph" className="win-icon-glyph"
style={entry.color ? { background: entry.color } : undefined} style={entry.color ? { background: entry.color } : undefined}
> >
{entry.image ? ( {entry.glyph}
<Image
src={entry.image}
alt={entry.label}
width={38}
height={38}
className="win-icon-img"
draggable={false}
/>
) : (
entry.glyph
)}
</span> </span>
<span className="win-icon-label">{entry.label}</span> <span className="win-icon-label">{entry.label}</span>
</button> </button>
@ -411,7 +395,6 @@ export default function WindowsDesktop() {
<div <div
ref={desktopRef} ref={desktopRef}
className="win-desktop" className="win-desktop"
style={{ backgroundImage: wallpaper }}
onPointerDown={(e) => { onPointerDown={(e) => {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) {
closeStart(); closeStart();

View file

@ -7,10 +7,10 @@ import {
codingProjects, codingProjects,
techProjects, techProjects,
friends, friends,
WALLPAPER_CREDIT,
} from "../data"; } from "../data";
import ServiceButton from "./ServiceButton"; import ServiceButton from "./ServiceButton";
import ContributionHeatmap from "./ContributionHeatmap"; import ContributionHeatmap from "./ContributionHeatmap";
import FeatureWindow from "./FeatureWindow";
import JokerLogo from "./JokerLogo"; import JokerLogo from "./JokerLogo";
import { setTheme } from "./useTheme"; import { setTheme } from "./useTheme";
@ -59,11 +59,7 @@ function WelcomeApp() {
. .
</p> </p>
<p className="app-muted app-fineprint"> <p className="app-muted app-fineprint">
Wallpaper from{" "} Gentoo logo by Matteo &apos;Peach&apos; Pescarin (CC BY-SA 2.5), via Wikimedia
<a href={WALLPAPER_CREDIT} target="_blank" rel="noopener noreferrer">
wallpapercave.com/kde-plasma-wallpapers
</a>
. Gentoo logo by Matteo &apos;Peach&apos; Pescarin (CC BY-SA 2.5), via Wikimedia
Commons. Commons.
</p> </p>
</div> </div>
@ -217,7 +213,9 @@ function ProjectsApp() {
rel="noopener noreferrer" rel="noopener noreferrer"
className="project-card" className="project-card"
> >
<div className="project-image" style={{ backgroundImage: `url(${project.img})` }} /> <div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
{project.emoji}
</div>
<h4 className="project-title">{project.title}</h4> <h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p> <p className="project-desc">{project.desc}</p>
</a> </a>
@ -231,12 +229,11 @@ function HardwareApp() {
return ( return (
<div className="app-pad"> <div className="app-pad">
<div className="project-grid"> <div className="project-grid">
{techProjects.map((project) => ( {techProjects.map((project, idx) => (
<div key={project.title} className="project-card"> <div key={project.title} className="project-card">
<div <div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
className="project-image" {project.emoji}
style={{ backgroundImage: `url(${project.image})` }} </div>
/>
<h4 className="project-title">{project.title}</h4> <h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p> <p className="project-desc">{project.desc}</p>
</div> </div>
@ -270,15 +267,7 @@ function GitdxdApp() {
<div className="app-pad app-center"> <div className="app-pad app-center">
<h2 className="app-h1">GitDXD</h2> <h2 className="app-h1">GitDXD</h2>
<p className="app-h2">My Self-hosted Git Instance</p> <p className="app-h2">My Self-hosted Git Instance</p>
<div className="app-shot app-shot-glow"> <FeatureWindow variant="forge" />
<Image
src="/gitdxd.webp"
alt="GitDXD Preview"
width={1280}
height={720}
style={{ width: "100%", height: "auto", display: "block" }}
/>
</div>
<p className="app-text"> <p className="app-text">
My very own forgejo git instance, originally started as a private git instance for My very own forgejo git instance, originally started as a private git instance for
me and my friends to boycott GitHub but I&#39;ve made it public now so. Join at{" "} me and my friends to boycott GitHub but I&#39;ve made it public now so. Join at{" "}
@ -475,15 +464,7 @@ function RevivalApp() {
return ( return (
<div className="app-pad app-center"> <div className="app-pad app-center">
<h2 className="app-h1">Revival Inc.</h2> <h2 className="app-h1">Revival Inc.</h2>
<div className="app-shot app-shot-light"> <FeatureWindow variant="game" />
<Image
src="/revivalinc.webp"
alt="revival inc thumbnail"
width={768}
height={576}
style={{ width: "100%", height: "auto", display: "block" }}
/>
</div>
<p className="app-h2">What is it?</p> <p className="app-h2">What is it?</p>
<p className="app-text"> <p className="app-text">
It&#39;s a game I made for the game jam &#34;Mini Jame Gam #55&#34;, the theme was It&#39;s a game I made for the game jam &#34;Mini Jame Gam #55&#34;, the theme was
@ -500,15 +481,7 @@ function RustyWolfApp() {
return ( return (
<div className="app-pad app-center"> <div className="app-pad app-center">
<h2 className="app-h1">Introducing RustyWolf</h2> <h2 className="app-h1">Introducing RustyWolf</h2>
<div className="app-shot app-shot-glow"> <FeatureWindow variant="browser" />
<Image
src="/RustyWolfPreview.webp"
alt="RustyWolf Browser Preview"
width={1280}
height={720}
style={{ width: "100%", height: "auto", display: "block" }}
/>
</div>
<p className="app-h2">What makes it so cool???</p> <p className="app-h2">What makes it so cool???</p>
<p className="app-text"> <p className="app-text">
A lightweight browser made in Rust with WebKitGTK. It&#39;s the successor to my A lightweight browser made in Rust with WebKitGTK. It&#39;s the successor to my
@ -766,22 +739,16 @@ function WebsiteApp({
url, url,
desc, desc,
icon, icon,
image,
}: { }: {
title: string; title: string;
url: string; url: string;
desc?: string; desc?: string;
icon?: ReactNode; icon?: ReactNode;
image?: string;
}) { }) {
return ( return (
<div className="app-pad app-center website-app"> <div className="app-pad app-center website-app">
<div className="website-app-icon"> <div className="website-app-icon">
{image ? ( <span className="website-app-emoji">{icon ?? "🌐"}</span>
<Image src={image} alt={title} width={84} height={84} className="website-app-img" />
) : (
<span className="website-app-emoji">{icon ?? "🌐"}</span>
)}
</div> </div>
<h2 className="app-h1">{title}</h2> <h2 className="app-h1">{title}</h2>
{desc && <p className="app-h2">{desc}</p>} {desc && <p className="app-h2">{desc}</p>}
@ -825,7 +792,6 @@ export function createWebsiteApp(opts: {
url: string; url: string;
desc?: string; desc?: string;
icon?: ReactNode; icon?: ReactNode;
image?: string;
/** /**
* Render the real site in an iframe instead of the link card. Only works for * Render the real site in an iframe instead of the link card. Only works for
* sites that do not send `X-Frame-Options` / a `frame-ancestors` CSP. * sites that do not send `X-Frame-Options` / a `frame-ancestors` CSP.
@ -854,7 +820,6 @@ export function createWebsiteApp(opts: {
url={opts.url} url={opts.url}
desc={opts.desc} desc={opts.desc}
icon={opts.icon} icon={opts.icon}
image={opts.image}
/> />
), ),
}; };

View file

@ -7,14 +7,16 @@ export interface Social {
export interface CodingProject { export interface CodingProject {
title: string; title: string;
desc: string; desc: string;
img: string; /** Emoji glyph shown on the card's CSS gradient tile (no image assets). */
emoji: string;
url: string; url: string;
} }
export interface TechProject { export interface TechProject {
title: string; title: string;
desc: string; desc: string;
image: string; /** Emoji glyph shown on the card's CSS gradient tile (no image assets). */
emoji: string;
} }
export interface Friend { export interface Friend {
@ -54,109 +56,109 @@ export const codingProjects: CodingProject[] = [
{ {
title: "Hospital Execution (Unfinished)", title: "Hospital Execution (Unfinished)",
desc: "the first ever real game I made, in pygame. I've abandoned it.", desc: "the first ever real game I made, in pygame. I've abandoned it.",
img: "/coding-1.webp", emoji: "🏥",
url: "https://martin-dv.itch.io/hospital-execution-demo", url: "https://martin-dv.itch.io/hospital-execution-demo",
}, },
{ {
title: "Underbaranoia (Finished)", title: "Underbaranoia (Finished)",
desc: "basically a undertale like infinite fight simulator that gets progressively harder, made in pygame.", desc: "basically a undertale like infinite fight simulator that gets progressively harder, made in pygame.",
img: "/coding-2.webp", emoji: "⚔️",
url: "https://martin-dv.itch.io/underbaranoia", url: "https://martin-dv.itch.io/underbaranoia",
}, },
{ {
title: "Offline memer (Finished)", title: "Offline memer (Finished)",
desc: "parody of the discord bot \"Dank Memer\" made in batch. (this was technically my first ever game I published).", desc: "parody of the discord bot \"Dank Memer\" made in batch. (this was technically my first ever game I published).",
img: "/coding-3.webp", emoji: "🤖",
url: "https://martin-dv.itch.io/offline-memer", url: "https://martin-dv.itch.io/offline-memer",
}, },
{ {
title: "4y (Forgotten)", title: "4y (Forgotten)",
desc: "just a renamed fork of the ly login manager (based login manager btw I love it), no extra features, just a rename, its actually outdated so dont use it.", desc: "just a renamed fork of the ly login manager (based login manager btw I love it), no extra features, just a rename, its actually outdated so dont use it.",
img: "/coding-4.webp", emoji: "🔐",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4y", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4y",
}, },
{ {
title: "4rch linux (Forgotten)", title: "4rch linux (Forgotten)",
desc: "same as above, basically just a renamed fork of arch linux with calamares installer, also outdated and not maintained at all so no use please.", desc: "same as above, basically just a renamed fork of arch linux with calamares installer, also outdated and not maintained at all so no use please.",
img: "/coding-5.webp", emoji: "🐧",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4rch-linux", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4rch-linux",
}, },
{ {
title: "You_Are_An_Idiot_Virus.py (Finished)", title: "You_Are_An_Idiot_Virus.py (Finished)",
desc: "this is just a python script that just spams blinking \"you are an idiot\" windows, made it as a prank I think, not really sure rn.", desc: "this is just a python script that just spams blinking \"you are an idiot\" windows, made it as a prank I think, not really sure rn.",
img: "/coding-6.webp", emoji: "🪟",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/uranidiot", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/uranidiot",
}, },
{ {
title: "Venturesome (Discontinued)", title: "Venturesome (Discontinued)",
desc: "a browser I wrote in python using PyQtWebEngine with PyQt5, actually pretty cool and modular but I couldnt get past the stage of adding a tab system, basically surpassed by my other project: RustyWolf.", desc: "a browser I wrote in python using PyQtWebEngine with PyQt5, actually pretty cool and modular but I couldnt get past the stage of adding a tab system, basically surpassed by my other project: RustyWolf.",
img: "/coding-7.webp", emoji: "🌐",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/Venturesome", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/Venturesome",
}, },
{ {
title: "Mandelbrot set in Pygame (Finished)", title: "Mandelbrot set in Pygame (Finished)",
desc: "a python pygame script that renders the mandelbrot set, made it when I was fascinated by how it looks and ngl it still looks cool af.", desc: "a python pygame script that renders the mandelbrot set, made it when I was fascinated by how it looks and ngl it still looks cool af.",
img: "/coding-8.webp", emoji: "🌀",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/mandelbrot", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/mandelbrot",
}, },
{ {
title: "Underpygametale (Unfinished)", title: "Underpygametale (Unfinished)",
desc: "a remake of undertale written in pygame, got it to about the flowey part (at the beginning) and didnt touch it in a long time.", desc: "a remake of undertale written in pygame, got it to about the flowey part (at the beginning) and didnt touch it in a long time.",
img: "/coding-9.webp", emoji: "💀",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/underpygametale", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/underpygametale",
}, },
{ {
title: "Many discord bots...", title: "Many discord bots...",
desc: "so many of them, once I (allegedly, pls discord dont ban me) even wrote a \"self-bot\" which auto-advertised a minecraft server on minecraft discord server in lfg-hosting every two hours, around 800 people joined in total.", desc: "so many of them, once I (allegedly, pls discord dont ban me) even wrote a \"self-bot\" which auto-advertised a minecraft server on minecraft discord server in lfg-hosting every two hours, around 800 people joined in total.",
img: "/coding-10.webp", emoji: "💬",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/PYGETEX", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/PYGETEX",
}, },
{ {
title: "MMO minecraft plugin (Unfinished)", title: "MMO minecraft plugin (Unfinished)",
desc: "a minceraft plugin with custom bosses, items, and functions. pretty barebones and basic but I did it all manually before vibecoding was even a thing so what.", desc: "a minceraft plugin with custom bosses, items, and functions. pretty barebones and basic but I did it all manually before vibecoding was even a thing so what.",
img: "/coding-11.webp", emoji: "⛏️",
url: "https://www.youtube.com/watch?v=cSAX5Ds61ac", url: "https://www.youtube.com/watch?v=cSAX5Ds61ac",
}, },
{ {
title: "Many many failed minecraft servers..", title: "Many many failed minecraft servers..",
desc: "creative games, softcraft, frostville, eusmp, eurthsmp, softsmp and many other SMPs that died later on. I cant even count how many mc servers I had in total.", desc: "creative games, softcraft, frostville, eusmp, eurthsmp, softsmp and many other SMPs that died later on. I cant even count how many mc servers I had in total.",
img: "/coding-12.webp", emoji: "🧱",
url: "https://discord.gg/KtHRm9kRmy", url: "https://discord.gg/KtHRm9kRmy",
}, },
{ {
title: "Swords and Sandals 2 pygame remake (Unfinished)", title: "Swords and Sandals 2 pygame remake (Unfinished)",
desc: "an unfinished remake of swords and sandals 2 in pygame, I got bored of it and never finished it but ill finish it one day for sure lol.", desc: "an unfinished remake of swords and sandals 2 in pygame, I got bored of it and never finished it but ill finish it one day for sure lol.",
img: "/coding-13.webp", emoji: "🗡️",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/sas2py", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/sas2py",
}, },
{ {
title: "Llama.cpp server", title: "Llama.cpp server",
desc: "my llama.cpp vm running on my proxmox with a gpu passthroughed RTX 3070, dm me if u wanna test it lol.", desc: "my llama.cpp vm running on my proxmox with a gpu passthroughed RTX 3070, dm me if u wanna test it lol.",
img: "/coding-14.webp", emoji: "🦾",
url: "https://ai.dvorakmortonxd.dev/", url: "https://ai.dvorakmortonxd.dev/",
}, },
{ {
title: "Batch MSDOS Simulator (Finished)", title: "Batch MSDOS Simulator (Finished)",
desc: "a freshman school project on making games in batch, it was like an OS simulator with a web browser, games, photos, even a video suprise (youll have to try it to figure that one out lol).", desc: "a freshman school project on making games in batch, it was like an OS simulator with a web browser, games, photos, even a video suprise (youll have to try it to figure that one out lol).",
img: "/coding-15.webp", emoji: "💾",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/DvorakMSDOS", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/DvorakMSDOS",
}, },
{ {
title: "RustyWolf (WIP, Available now!)", title: "RustyWolf (WIP, Available now!)",
desc: "a browser I made in rust with webkitgtk, I made it to be lightweight and to be able to be ran on macbook unibody 2010 before I realised I got scammed.. It's the successor to my previous project Venturesome :)", desc: "a browser I made in rust with webkitgtk, I made it to be lightweight and to be able to be ran on macbook unibody 2010 before I realised I got scammed.. It's the successor to my previous project Venturesome :)",
img: "/coding-16.webp", emoji: "🐺",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/RustyWolf", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/RustyWolf",
}, },
{ {
title: "dxdwm (Discontinued)", title: "dxdwm (Discontinued)",
desc: "a small x11 window manager written in rust.", desc: "a small x11 window manager written in rust.",
img: "/coding-17.webp", emoji: "🪟",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/dxdwm", url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/dxdwm",
}, },
{ {
title: "Revival Inc. (Finished)", title: "Revival Inc. (Finished)",
desc: "My game written in pygame and packaged with pygbag made for the Mini Jame Gam #55! tho I only made it in 6 hours lmao", desc: "My game written in pygame and packaged with pygbag made for the Mini Jame Gam #55! tho I only made it in 6 hours lmao",
img: "/revivalinc.webp", emoji: "🧟",
url: "https://dvorakmortonxd.dev/revival-inc", url: "https://dvorakmortonxd.dev/revival-inc",
}, },
]; ];
@ -165,77 +167,77 @@ export const techProjects: TechProject[] = [
{ {
title: "Gentoo Setup", title: "Gentoo Setup",
desc: "my main computer with gentoo and SwayFX. specs: Ryzen 5 3600 (6c/12t), RX 580 8GB ASUS ROG, 16gb of ram and a 240gb sata ssd.", desc: "my main computer with gentoo and SwayFX. specs: Ryzen 5 3600 (6c/12t), RX 580 8GB ASUS ROG, 16gb of ram and a 240gb sata ssd.",
image: "/gentoo-setup.webp", emoji: "🐧",
}, },
{ {
title: "Proxmox Server", title: "Proxmox Server",
desc: "my personal proxmox server (dm me on ig if u need a VPS/hosting), specs: Xeon E5 2680 v4 (14c/28t), RTX 3070 (llama.cpp), 68gb ddr4 rdimm ram (2*16gb, 1*32gb, 1*4gb), 256gb of sata m2 space + 1tb hdd + 750gb hdd.", desc: "my personal proxmox server (dm me on ig if u need a VPS/hosting), specs: Xeon E5 2680 v4 (14c/28t), RTX 3070 (llama.cpp), 68gb ddr4 rdimm ram (2*16gb, 1*32gb, 1*4gb), 256gb of sata m2 space + 1tb hdd + 750gb hdd.",
image: "/proxmox-server.webp", emoji: "🖥️",
}, },
{ {
title: "Xbox 360 S (badupdate)", title: "Xbox 360 S (badupdate)",
desc: "xbox 360 slim (trinity) with badupdate exploit, allowing to boot custom software. (not my photo), the specs are XCGPU (3c/6t), 0.5GB of gddr3 ram and a 500gb hdd.", desc: "xbox 360 slim (trinity) with badupdate exploit, allowing to boot custom software. (not my photo), the specs are XCGPU (3c/6t), 0.5GB of gddr3 ram and a 500gb hdd.",
image: "/xbox360.webp", emoji: "🎮",
}, },
{ {
title: "Lenovo Legion Go S", title: "Lenovo Legion Go S",
desc: "my gaming handheld which I sometimes use as a computer, it has 32gb of unified lpddr5x memory and 1tb of nvme storage, I dualboot artix linux (openrc) with SwayFX and stock SteamOS on it.", desc: "my gaming handheld which I sometimes use as a computer, it has 32gb of unified lpddr5x memory and 1tb of nvme storage, I dualboot artix linux (openrc) with SwayFX and stock SteamOS on it.",
image: "/legiongos.webp", emoji: "🕹️",
}, },
{ {
title: "Macbook Air M4 13\"", title: "Macbook Air M4 13\"",
desc: "my main device for basically everything on the go except gaming. specs are Apple M4 (cpu:10c,gpu:8c), 16gb of unified ram (lpddr5x), 256gb Storage.", desc: "my main device for basically everything on the go except gaming. specs are Apple M4 (cpu:10c,gpu:8c), 16gb of unified ram (lpddr5x), 256gb Storage.",
image: "/macbook-air.webp", emoji: "💻",
}, },
{ {
title: "Macbook Air 2013 11\"", title: "Macbook Air 2013 11\"",
desc: "bought this for tinkering lol, specs are intel core i5 (2c/4t), 8gb of ddr3 ram and 250gb ssd, installed arch linux on it with i3wm.", desc: "bought this for tinkering lol, specs are intel core i5 (2c/4t), 8gb of ddr3 ram and 250gb ssd, installed arch linux on it with i3wm.",
image: "/macbook-air-11.webp", emoji: "💻",
}, },
{ {
title: "iPhone 7 128gb", title: "iPhone 7 128gb",
desc: "somehow found a listing for 4 bucks (refurbed.cz) for it so I got it lol, it has a little dent I marked it on the photo (which isnt mine btw), except cosmetic damage its pretty much fully functional, specs are Apple A10 (4c/4t), 2gb of ram and 128gb of storage", desc: "somehow found a listing for 4 bucks (refurbed.cz) for it so I got it lol, it has a little dent I marked it on the photo (which isnt mine btw), except cosmetic damage its pretty much fully functional, specs are Apple A10 (4c/4t), 2gb of ram and 128gb of storage",
image: "/iphone-7.webp", emoji: "📱",
}, },
{ {
title: "Macbook Unibody 2010", title: "Macbook Unibody 2010",
desc: "found random listing on bazos for 15 bucks but after I bought a power adapter it turned out to be non functioning so I got scammed :(", desc: "found random listing on bazos for 15 bucks but after I bought a power adapter it turned out to be non functioning so I got scammed :(",
image: "/macbook2010.webp", emoji: "💻",
}, },
{ {
title: "ThinkPad T400s 14\"", title: "ThinkPad T400s 14\"",
desc: "got it from a random guy for 20 bucks, its an old laptop from 2009. I put void linux on it and i3wm on it, specs are intel core 2 duo (2c/2t), 8gb ddr3 ram and 800gb hdd.", desc: "got it from a random guy for 20 bucks, its an old laptop from 2009. I put void linux on it and i3wm on it, specs are intel core 2 duo (2c/2t), 8gb ddr3 ram and 800gb hdd.",
image: "/thinkpadt400s.webp", emoji: "💻",
}, },
{ {
title: "Asus N56JR 15.6\"", title: "Asus N56JR 15.6\"",
desc: "one of my first laptops my dad got for me when I was like 12, still works but the battery is KO, specs are intel core i7 4700HQ (4c/8t), 8gb ddr3 ram and 240gb ssd, it currently runs ubuntu linux 24.04LTS and my mom uses it for movies and shows.", desc: "one of my first laptops my dad got for me when I was like 12, still works but the battery is KO, specs are intel core i7 4700HQ (4c/8t), 8gb ddr3 ram and 240gb ssd, it currently runs ubuntu linux 24.04LTS and my mom uses it for movies and shows.",
image: "/asuslaptop.webp", emoji: "💻",
}, },
{ {
title: "Small Unknown XP Acer Laptop", title: "Small Unknown XP Acer Laptop",
desc: "found that one in ewaste, I think it has like 2gb of ram and an intel atom, not sure what to do with it yet", desc: "found that one in ewaste, I think it has like 2gb of ram and an intel atom, not sure what to do with it yet",
image: "/acerlaptop.webp", emoji: "📟",
}, },
{ {
title: "Dell Latitude E6320", title: "Dell Latitude E6320",
desc: "also from ewaste, put mint on it but didnt do anything yet, lowk kinda capable for its time in 2011 so I might put it to use later but the battery is KO.", desc: "also from ewaste, put mint on it but didnt do anything yet, lowk kinda capable for its time in 2011 so I might put it to use later but the battery is KO.",
image: "/latitude.webp", emoji: "💻",
}, },
{ {
title: "iPhone 11 128gb", title: "iPhone 11 128gb",
desc: "my first iphone I got from my parents at 13, rn my dad has it because I switched over to the iphone 16 pro in 2025, specs are Apple A13 (6c/6t), 4gb of ram and 128gb of storage. (NOT MY IMAGE)", desc: "my first iphone I got from my parents at 13, rn my dad has it because I switched over to the iphone 16 pro in 2025, specs are Apple A13 (6c/6t), 4gb of ram and 128gb of storage. (NOT MY IMAGE)",
image: "/iphone11.webp", emoji: "📱",
}, },
{ {
title: "iPhone 16 Pro 128gb", title: "iPhone 16 Pro 128gb",
desc: "originally wanted iphone 13 mini (because of the size) but I somehow decided to get 16 pro, which is a bit smaller than my older iphone 11, specs are Apple A18 pro (6c/6t), (btw this chip is the same chip used in the macbook neo so thats cool) and 8gb of ram and 128gb of storage, my main phone.", desc: "originally wanted iphone 13 mini (because of the size) but I somehow decided to get 16 pro, which is a bit smaller than my older iphone 11, specs are Apple A18 pro (6c/6t), (btw this chip is the same chip used in the macbook neo so thats cool) and 8gb of ram and 128gb of storage, my main phone.",
image: "/iphone16pro.webp", emoji: "📱",
}, },
{ {
title: "Xbox one S 2TB", title: "Xbox one S 2TB",
desc: "my dad bought it for me also in like 2018 or 2019 and I sometimes play the console edition of minecraft because I like to play the gliding minigame on it when someone comes over.", desc: "my dad bought it for me also in like 2018 or 2019 and I sometimes play the console edition of minecraft because I like to play the gliding minigame on it when someone comes over.",
image: "/xboxones.webp", emoji: "🎮",
}, },
]; ];
@ -277,6 +279,3 @@ export const friends: Friend[] = [
iframe: true, iframe: true,
}, },
]; ];
export const WALLPAPER = "/wallpaper.jpg";
export const WALLPAPER_CREDIT = "https://wallpapercave.com/kde-plasma-wallpapers";

View file

@ -34,7 +34,13 @@ html[data-theme="skeuo"] body {
position: fixed; position: fixed;
inset: 0; inset: 0;
overflow: hidden; overflow: hidden;
background-color: #1b1e20; background-color: #0b1a2b;
/* Asset-free "wallpaper": layered KDE-ish blue/purple gradients. */
background-image:
radial-gradient(60rem 40rem at 15% 10%, rgba(61, 174, 233, 0.28), transparent 65%),
radial-gradient(55rem 42rem at 85% 18%, rgba(155, 89, 182, 0.22), transparent 65%),
radial-gradient(72rem 52rem at 68% 100%, rgba(41, 128, 185, 0.3), transparent 70%),
linear-gradient(160deg, #0a1524 0%, #10233b 45%, #0b1a2b 100%);
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -143,13 +149,6 @@ a.win-icon {
flex-wrap: nowrap; flex-wrap: nowrap;
max-height: none; max-height: none;
} }
.win-icon-img {
width: 38px;
height: 38px;
object-fit: contain;
filter: brightness(0) invert(1);
opacity: 0.92;
}
/* Cramped desktops: shrink the icons so the grid always has a cell for every /* Cramped desktops: shrink the icons so the grid always has a cell for every
icon (so they can never collide) and the launcher stacks stay on screen. The icon (so they can never collide) and the launcher stacks stay on screen. The
@ -168,10 +167,6 @@ a.win-icon {
.win-icon-label { .win-icon-label {
font-size: 0.68rem; font-size: 0.68rem;
} }
.win-icon-img {
width: 32px;
height: 32px;
}
} }
@media (max-width: 720px), (max-height: 440px) { @media (max-width: 720px), (max-height: 440px) {
.win-icon { .win-icon {
@ -191,10 +186,6 @@ a.win-icon {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.win-icon-img {
width: 26px;
height: 26px;
}
} }
@media (max-height: 380px) { @media (max-height: 380px) {
.win-icon { .win-icon {
@ -214,10 +205,6 @@ a.win-icon {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.win-icon-img {
width: 22px;
height: 22px;
}
} }
/* The three default stacks are laid out once so the grid columns can be read /* The three default stacks are laid out once so the grid columns can be read
@ -650,12 +637,6 @@ a.win-icon {
font-size: 3rem; font-size: 3rem;
line-height: 1; line-height: 1;
} }
.website-app-img {
width: 84px;
height: 84px;
object-fit: contain;
filter: brightness(0);
}
.website-app-link { .website-app-link {
margin-top: 14px; margin-top: 14px;
padding: 8px 16px; padding: 8px 16px;
@ -889,7 +870,7 @@ a.win-icon {
box-shadow: 0 0 0 1px #7db9e8, 0 0 14px rgba(110, 185, 255, 0.5), box-shadow: 0 0 0 1px #7db9e8, 0 0 14px rgba(110, 185, 255, 0.5),
0 8px 18px rgba(0, 25, 55, 0.2); 0 8px 18px rgba(0, 25, 55, 0.2);
} }
.app-surface .project-image { .app-surface .project-thumb {
height: 150px; height: 150px;
border-radius: 4px; border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.12); border: 1px solid rgba(0, 0, 0, 0.12);
@ -1022,18 +1003,6 @@ a.win-icon {
color: #0e3355; color: #0e3355;
margin: 14px 0 8px; margin: 14px 0 8px;
} }
.app-surface .app-shot {
border-radius: 8px;
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.2);
margin: 12px 0;
}
.app-surface .app-shot-glow {
box-shadow: 0 0 26px rgba(120, 190, 255, 0.35);
}
.app-surface .app-shot-light {
box-shadow: 0 0 26px rgba(0, 0, 0, 0.22);
}
.app-surface .app-list { .app-surface .app-list {
margin: 10px 0 10px 20px; margin: 10px 0 10px 20px;
line-height: 1.8; line-height: 1.8;
@ -1192,7 +1161,7 @@ a.win-icon {
background: #353b40; background: #353b40;
box-shadow: 0 0 0 1px #3daee9, 0 0 14px rgba(61, 174, 233, 0.45); box-shadow: 0 0 0 1px #3daee9, 0 0 14px rgba(61, 174, 233, 0.45);
} }
.win-body .project-image { .win-body .project-thumb {
border-color: #1b1e20; border-color: #1b1e20;
} }
.win-body .project-title { .win-body .project-title {
@ -1250,9 +1219,6 @@ a.win-icon {
color: #27ae60; color: #27ae60;
} }
.win-body .app-shot {
border-color: #1b1e20;
}
.win-body .app-linkrow a { .win-body .app-linkrow a {
border-color: #1b1e20; border-color: #1b1e20;
background: linear-gradient(to bottom, #3b4147, #31363b); background: linear-gradient(to bottom, #3b4147, #31363b);
@ -1262,6 +1228,22 @@ a.win-icon {
color: #3daee9; color: #3daee9;
} }
/* Feature mock windows (FeatureWindow) inside the desktop app windows: swap the
web glass tokens for the KDE dark surface so they fit the rest of the shell. */
html[data-theme="skeuo"] .fw {
--fw-chrome: rgba(255, 255, 255, 0.05);
--glass-hi: rgba(255, 255, 255, 0.06);
--glass-lo: rgba(255, 255, 255, 0.03);
--glass-border: rgba(255, 255, 255, 0.12);
--btn-bg: rgba(255, 255, 255, 0.06);
--btn-border: rgba(255, 255, 255, 0.14);
--btn-shadow: none;
--web-muted: #9aa0a6;
--fg: #eff0f1;
background: #232629;
box-shadow: 0 10px 30px -16px rgba(0, 0, 0, 0.85);
}
/* Alacritty easter egg */ /* Alacritty easter egg */
.alacritty { .alacritty {
min-height: 100%; min-height: 100%;
@ -1631,7 +1613,7 @@ a.win-icon {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 6px; gap: 6px;
} }
.app-surface .project-image { .app-surface .project-thumb {
height: 140px; height: 140px;
} }
} }

View file

@ -845,6 +845,141 @@ html[data-theme="web"] body {
background: linear-gradient(150deg, #8b5cf6, #6366f1); background: linear-gradient(150deg, #8b5cf6, #6366f1);
} }
/* ---------- GitDXD: a little git-forge repo page ---------- */
.fw-forge {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
background: linear-gradient(180deg, var(--glass-hi), var(--glass-lo));
}
.fw-forge-head {
display: flex;
align-items: flex-end;
gap: 14px;
padding: 10px 14px 0;
border-bottom: 1px solid var(--glass-border);
flex-wrap: wrap;
}
.fw-forge-repo {
padding-bottom: 8px;
font-size: 0.72rem;
color: var(--web-muted);
white-space: nowrap;
}
.fw-forge-repo b {
color: var(--fg);
}
.fw-forge-tabs {
display: flex;
gap: 13px;
margin-left: auto;
}
.fw-forge-tab {
padding-bottom: 8px;
font-size: 0.66rem;
color: var(--web-muted);
}
.fw-forge-tab-active {
color: var(--fg);
box-shadow: inset 0 -2px 0 var(--accent);
}
.fw-forge-body {
flex: 1;
min-height: 0;
display: grid;
grid-template-columns: minmax(84px, 34%) 1fr;
}
.fw-forge-files {
list-style: none;
margin: 0;
padding: 10px;
border-right: 1px solid var(--glass-border);
display: flex;
flex-direction: column;
gap: 7px;
font-size: 0.62rem;
color: var(--web-muted);
overflow: hidden;
}
.fw-forge-files li {
display: flex;
align-items: center;
gap: 6px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.fw-forge-file-icon {
flex: none;
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
opacity: 0.7;
}
.fw-forge-code {
display: flex;
flex-direction: column;
justify-content: center;
gap: 9px;
padding: 12px 14px;
}
.fw-code-line {
height: 6px;
border-radius: 3px;
background: var(--glass-border);
}
.fw-code-line:nth-child(1) {
width: 70%;
background: var(--accent);
opacity: 0.55;
}
.fw-code-line:nth-child(2) {
width: 88%;
}
.fw-code-line:nth-child(3) {
width: 54%;
}
.fw-code-line:nth-child(4) {
width: 78%;
background: var(--accent2);
opacity: 0.5;
}
.fw-code-line:nth-child(5) {
width: 62%;
}
.fw-code-line:nth-child(6) {
width: 84%;
}
.fw-code-line:nth-child(7) {
width: 46%;
background: var(--accent);
opacity: 0.45;
}
.fw-code-line:nth-child(8) {
width: 66%;
}
.fw-forge-lang {
display: flex;
height: 6px;
}
.fw-lang-seg {
height: 100%;
}
.fw-lang-ts {
width: 58%;
background: #3178c6;
}
.fw-lang-css {
width: 26%;
background: #e34c26;
}
.fw-lang-md {
width: 16%;
background: #563d7c;
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.fw-stars, .fw-stars,
.fw-start { .fw-start {
@ -1055,15 +1190,37 @@ html[data-theme="web"] body {
background: var(--btn-bg-hi, var(--glass-hi)); background: var(--btn-bg-hi, var(--glass-hi));
box-shadow: 0 20px 38px -20px rgba(23, 32, 60, 0.55); box-shadow: 0 20px 38px -20px rgba(23, 32, 60, 0.55);
} }
.project-image { .project-thumb {
display: grid;
place-items: center;
height: 150px; height: 150px;
width: 100%; width: 100%;
margin-bottom: 12px; margin-bottom: 12px;
border-radius: 12px; border-radius: 12px;
border: 1px solid var(--glass-border); border: 1px solid var(--glass-border);
background-size: cover; font-size: 2.6rem;
background-position: center; line-height: 1;
background-repeat: no-repeat; user-select: none;
text-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
/* Shared gradient palette for the card tiles (no image assets). */
.thumb-0 {
background: linear-gradient(150deg, #6d5efc, #2f8bff);
}
.thumb-1 {
background: linear-gradient(150deg, #ec4899, #db2777);
}
.thumb-2 {
background: linear-gradient(150deg, #f59e0b, #d97757);
}
.thumb-3 {
background: linear-gradient(150deg, #10b981, #0d9488);
}
.thumb-4 {
background: linear-gradient(150deg, #8b5cf6, #6366f1);
}
.thumb-5 {
background: linear-gradient(150deg, #06b6d4, #3b82f6);
} }
.project-title { .project-title {
font-size: 0.95rem; font-size: 0.95rem;
@ -1603,9 +1760,10 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
margin-bottom: 10px; margin-bottom: 10px;
padding: 10px; padding: 10px;
} }
.project-image { .project-thumb {
height: 90px; height: 90px;
margin-bottom: 8px; margin-bottom: 8px;
font-size: 1.8rem;
} }
.project-title { .project-title {
font-size: 0.88rem; font-size: 0.88rem;