default to dark mode, refresh Right now text, CSS barbell gym icons
This commit is contained in:
parent
6827ca6ed1
commit
4a8cac689b
10 changed files with 101 additions and 66 deletions
|
|
@ -8,7 +8,7 @@ from the saved theme, all from the same content:
|
|||
- **`web`** (default) — a light, frosted-glass **website**: a sticky full-height sidebar
|
||||
(section nav, light/dark switch, desktop switch) beside a column of glass cards — hero with
|
||||
a socials row, quick tiles, live services, activity heatmap, projects and hardware
|
||||
(a masonry gallery), features and friends. Light by default with a persisted dark mode,
|
||||
(a masonry gallery), features and friends. Dark by default with a persisted light mode,
|
||||
and fully responsive, so phones and desktops get the same shell.
|
||||
- **`skeuo`** — an alternate KDE Plasma / Windows-style **desktop simulation** (desktop
|
||||
only): draggable snap-to-grid icons, resizable/floating windows, a Start menu, taskbar and
|
||||
|
|
@ -26,7 +26,7 @@ more.
|
|||
- **Two themes / shells** — a default frosted-glass **web** shell and an alternate KDE-style
|
||||
**desktop** shell, toggled from either UI and persisted in `localStorage` (applied pre-paint
|
||||
to avoid a flash). Phones always get the web shell.
|
||||
- **Light / dark mode** — the web shell has a light/dark switch (light by default, remembered
|
||||
- **Light / dark mode** — the web shell has a light/dark switch (dark by default, remembered
|
||||
in `localStorage`, applied pre-paint). The desktop shell keeps its own Breeze Dark look.
|
||||
- **Glassmorphic web layout** — a sticky sidebar with section navigation beside a column of
|
||||
frosted-glass panels (subtle gradients + `backdrop-filter` blur), reorganised into
|
||||
|
|
|
|||
11
agents.md
11
agents.md
|
|
@ -109,7 +109,8 @@ scripts/
|
|||
1. `ThemeScript` runs before paint. It reads `localStorage.theme` and sets
|
||||
`data-theme` (`web` | `skeuo`) on `<html>`, defaulting to `web`; it also reads
|
||||
`localStorage.scheme` and sets `data-scheme` (`light` | `dark`), defaulting to
|
||||
`light`. There is no device sniff anymore.
|
||||
`dark` (only an explicit stored `light` overrides it). There is no device
|
||||
sniff anymore.
|
||||
2. `useTheme()` (in `useTheme.ts`) reads `data-theme` via `useSyncExternalStore`
|
||||
and dispatches a `theme-change` event on toggle. `useColorScheme()` does the
|
||||
same for `data-scheme` via a `scheme-change` event.
|
||||
|
|
@ -192,6 +193,14 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
|
|||
`.web-tile-note` is centered. Every other panel title is centered too:
|
||||
`.web-section-head` (the eyebrow + `.web-section-title`) and `.web-tile-title`
|
||||
("Right now" / "Gym PRs"). Desktop keeps the left-aligned headings.
|
||||
- The gym PR icons (`.gym-icon--squat` / `--bench` / `--deadlift`) are **pure
|
||||
CSS barbells**, not SVGs: a bar (`::before`) plus plates drawn as layered
|
||||
`linear-gradient` backgrounds on `::after`, all in `currentColor` so they
|
||||
adapt to light/dark and the desktop app surface. More plates per side = a
|
||||
heavier lift (bench 1, squat 2, deadlift 3). Don't swap them back to images.
|
||||
- `.web-quick` is top-aligned (`align-items: start`) so the shorter "Right now"
|
||||
tile isn't stretched to the Gym tile's height (it used to leave a big empty
|
||||
gap under the text).
|
||||
- Under `max-width: 640px` the year-grid heatmap (`.heatmap-container`) and its
|
||||
legend/hover row (`.heatmap-status-row`) are `display: none` — the grid is
|
||||
unreadable at phone widths. The contribution total and the commit history
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="bench-press-6" transform="translate(-2 -2)">
|
||||
<path id="primary" d="M6,21V15H18v6" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
||||
<path id="primary-2" data-name="primary" d="M4,3V9M3,4V8M20,3V9m1-5V8M4,6H20M16,4V15M8,15V4m9,17h2M5,21H7" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 625 B |
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="deadlift" transform="translate(-2 -2)">
|
||||
<circle id="secondary" fill="#2ca9bc" cx="2" cy="2" r="2" transform="translate(10 3)"/>
|
||||
<path id="primary" d="M8,15V10a2,2,0,0,1,2-2h4a2,2,0,0,1,2,2v5" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
||||
<path id="primary-2" data-name="primary" d="M14,5a2,2,0,1,1-2-2A2,2,0,0,1,14,5Zm0,9v7m-4,0V14M3,14H20m0-2v4m1-3v2M4,12v4M3,13v2" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 762 B |
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 256 256" xml:space="preserve">
|
||||
<g>
|
||||
<circle cx="127" cy="28.4" r="26.6"/>
|
||||
<path d="M213.4,171.2h-49.8V97.3c0-1.8,1.5-3.3,3.3-3.3c0.6,0,1.2,0.2,1.7,0.5l30.1,17.3l-20.8,36c-3.2,5.6-1.3,12.7,4.3,15.9
|
||||
c5.6,3.2,12.7,1.3,15.9-4.3l26.6-46c3.2-5.6,1.3-12.7-4.3-15.9l-51.6-29.8c-4.7-3.3-10-5.4-15.5-6L127,61.6h0h0l-26.3,0.1
|
||||
c-5.5,0.6-10.8,2.7-15.5,6L33.6,97.5c-5.6,3.2-7.5,10.3-4.3,15.9l26.6,46c3.2,5.6,10.3,7.5,15.9,4.3c5.6-3.2,7.5-10.3,4.3-15.9
|
||||
l-20.8-36l30.1-17.3c0.5-0.3,1.1-0.5,1.7-0.5c1.8,0,3.3,1.5,3.3,3.3v24.1v49.8H40.6c-9.2,0-16.6,7.4-16.6,16.6
|
||||
c0,4.6,1.8,8.7,4.8,11.7l49.7,49.7c6.5,6.5,17,6.5,23.5,0c6.5-6.5,6.5-17,0-23.5l-21.3-21.3l46.2,0l0-13.3c0,0,0,0,0,0s0,0,0,0
|
||||
l0,13.3l46.2,0l-21.3,21.3c-6.5,6.5-6.5,17,0,23.5c6.5,6.5,17,6.5,23.5,0l49.7-49.7c3-3,4.8-7.1,4.8-11.7
|
||||
C230,178.7,222.6,171.2,213.4,171.2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
|
@ -7,8 +7,9 @@ import { useServerInsertedHTML } from "next/navigation";
|
|||
// are applied without a flash. It is injected with useServerInsertedHTML so the
|
||||
// <script> lives in the SSR HTML stream instead of inside the React tree —
|
||||
// React 19 logs an error for script tags rendered by components on the client.
|
||||
// Anything unrecognised falls back to the defaults: web + light.
|
||||
const INIT_SCRIPT = `(function(){try{var t=localStorage.getItem('theme');document.documentElement.setAttribute('data-theme',t==='skeuo'?'skeuo':'web');var s=localStorage.getItem('scheme');document.documentElement.setAttribute('data-scheme',s==='dark'?'dark':'light');}catch(e){}})();`;
|
||||
// Anything unrecognised falls back to the defaults: web + dark. Dark is the
|
||||
// default scheme; only an explicit stored "light" switches it off.
|
||||
const INIT_SCRIPT = `(function(){try{var t=localStorage.getItem('theme');document.documentElement.setAttribute('data-theme',t==='skeuo'?'skeuo':'web');var s=localStorage.getItem('scheme');document.documentElement.setAttribute('data-scheme',s==='light'?'light':'dark');}catch(e){}})();`;
|
||||
|
||||
export default function ThemeScript() {
|
||||
// useServerInsertedHTML can fire several times during a single render pass;
|
||||
|
|
|
|||
|
|
@ -121,15 +121,15 @@ function Services() {
|
|||
|
||||
function Gym() {
|
||||
const lifts = [
|
||||
{ icon: "/squat.svg", label: "SQUAT", weight: "125kg", date: "5.1.2025" },
|
||||
{ icon: "/bench.svg", label: "BENCH", weight: "111kg", date: "29.5.2026" },
|
||||
{ icon: "/deadlift.svg", label: "DEADLIFT", weight: "165kg", date: "17.12.2024" },
|
||||
{ icon: "squat", label: "SQUAT", weight: "125kg", date: "5.1.2025" },
|
||||
{ icon: "bench", label: "BENCH", weight: "111kg", date: "29.5.2026" },
|
||||
{ icon: "deadlift", label: "DEADLIFT", weight: "165kg", date: "17.12.2024" },
|
||||
];
|
||||
return (
|
||||
<div className="gym-grid">
|
||||
{lifts.map((l) => (
|
||||
<div key={l.label} className="gym-item">
|
||||
<Image src={l.icon} alt={l.label} width={48} height={48} className="gym-icon" />
|
||||
<span className={`gym-icon gym-icon--${l.icon}`} aria-hidden="true" />
|
||||
<div className="gym-label">{l.label}</div>
|
||||
<div className="gym-weight">{l.weight}</div>
|
||||
<div className="gym-date">{l.date}</div>
|
||||
|
|
@ -238,11 +238,9 @@ export default function WebLayout() {
|
|||
<article id="now" className="web-tile glass">
|
||||
<h3 className="web-tile-title">Right now</h3>
|
||||
<p className="web-tile-text">
|
||||
I'm currently hosting an SMP (dm me on discord). Also I'm pretty much
|
||||
cooking daily, because im bulking through out the non-summer seasons. And me
|
||||
and my pal <a href="https://skopec.dev">Pepa</a> are working on an education
|
||||
app so we can study more efficiently, It's called{" "}
|
||||
<a href="https://git.dvorakmortonxd.dev/edu/maturita.c">maturita.c</a>!
|
||||
I left the maturita.c project to focus on my proxmox server (just popped in
|
||||
another 1.7TB hdd upgrade, 3 small hdds from ewaste laptops and I just
|
||||
ziplocked it into place and connected it lmao)
|
||||
</p>
|
||||
</article>
|
||||
|
||||
|
|
|
|||
|
|
@ -301,25 +301,19 @@ function GymApp() {
|
|||
<div className="app-pad">
|
||||
<div className="gym-grid">
|
||||
<div className="gym-item">
|
||||
<Image src="/squat.svg" alt="Squat" width={48} height={48} className="gym-icon" />
|
||||
<span className="gym-icon gym-icon--squat" aria-hidden="true" />
|
||||
<div className="gym-label">SQUAT</div>
|
||||
<div className="gym-weight">125kg</div>
|
||||
<div className="gym-date">5.1.2025</div>
|
||||
</div>
|
||||
<div className="gym-item">
|
||||
<Image src="/bench.svg" alt="Bench" width={48} height={48} className="gym-icon" />
|
||||
<span className="gym-icon gym-icon--bench" aria-hidden="true" />
|
||||
<div className="gym-label">BENCH</div>
|
||||
<div className="gym-weight">111kg</div>
|
||||
<div className="gym-date">29.5.2026</div>
|
||||
</div>
|
||||
<div className="gym-item">
|
||||
<Image
|
||||
src="/deadlift.svg"
|
||||
alt="Deadlift"
|
||||
width={48}
|
||||
height={48}
|
||||
className="gym-icon"
|
||||
/>
|
||||
<span className="gym-icon gym-icon--deadlift" aria-hidden="true" />
|
||||
<div className="gym-label">DEADLIFT</div>
|
||||
<div className="gym-weight">165kg</div>
|
||||
<div className="gym-date">17.12.2024</div>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { useSyncExternalStore } from "react";
|
|||
|
||||
/**
|
||||
* Light/dark appearance for the web theme. Persisted in `localStorage.scheme`
|
||||
* and applied pre-paint as `<html data-scheme>` so there is no flash. Light is
|
||||
* the default.
|
||||
* and applied pre-paint as `<html data-scheme>` so there is no flash. Dark is
|
||||
* the default; the user's choice is remembered once they toggle.
|
||||
*/
|
||||
export type ColorScheme = "light" | "dark";
|
||||
|
||||
const EVENT = "scheme-change";
|
||||
export const DEFAULT_SCHEME: ColorScheme = "light";
|
||||
export const DEFAULT_SCHEME: ColorScheme = "dark";
|
||||
|
||||
function subscribe(callback: () => void) {
|
||||
window.addEventListener(EVENT, callback);
|
||||
|
|
@ -22,9 +22,9 @@ function subscribe(callback: () => void) {
|
|||
}
|
||||
|
||||
function getSnapshot(): ColorScheme {
|
||||
return document.documentElement.getAttribute("data-scheme") === "dark"
|
||||
? "dark"
|
||||
: "light";
|
||||
return document.documentElement.getAttribute("data-scheme") === "light"
|
||||
? "light"
|
||||
: "dark";
|
||||
}
|
||||
|
||||
function getServerSnapshot(): ColorScheme {
|
||||
|
|
|
|||
|
|
@ -357,7 +357,9 @@ html[data-theme="web"] body {
|
|||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
gap: 20px;
|
||||
align-items: stretch;
|
||||
/* Don't stretch the shorter "Right now" tile to the taller Gym tile's
|
||||
height — that left a big empty gap under its text. */
|
||||
align-items: start;
|
||||
}
|
||||
.web-tile {
|
||||
display: flex;
|
||||
|
|
@ -373,7 +375,8 @@ html[data-theme="web"] body {
|
|||
color: var(--accent);
|
||||
}
|
||||
.web-tile-text {
|
||||
font-size: 0.9rem;
|
||||
font-size: 1.12rem;
|
||||
line-height: 1.7;
|
||||
color: var(--web-muted);
|
||||
}
|
||||
.web-tile-note {
|
||||
|
|
@ -728,10 +731,72 @@ html[data-theme="web"][data-scheme="dark"] .social-icon {
|
|||
border-color: var(--accent-line);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
/* Custom CSS barbell icons (the squat/bench/deadlift SVGs are gone). Same
|
||||
barbell, drawn from currentColor so it adapts to the theme; more plates per
|
||||
side = heavier lift (bench 1, squat 2, deadlift 3). */
|
||||
.gym-icon {
|
||||
width: 100%;
|
||||
max-width: 42px;
|
||||
height: auto;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 54px;
|
||||
height: 30px;
|
||||
}
|
||||
.gym-icon::before {
|
||||
/* the bar */
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: currentColor;
|
||||
opacity: 0.55;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.gym-icon::after {
|
||||
/* the weight plates */
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.gym-icon--bench::after {
|
||||
background-image:
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor);
|
||||
background-size: 7px 24px, 7px 24px;
|
||||
background-position: 9px center, calc(100% - 16px) center;
|
||||
}
|
||||
.gym-icon--squat::after {
|
||||
background-image:
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor);
|
||||
background-size: 6px 18px, 7px 26px, 6px 18px, 7px 26px;
|
||||
background-position:
|
||||
5px center,
|
||||
12px center,
|
||||
calc(100% - 11px) center,
|
||||
calc(100% - 19px) center;
|
||||
}
|
||||
.gym-icon--deadlift::after {
|
||||
background-image:
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor),
|
||||
linear-gradient(currentColor, currentColor);
|
||||
background-size: 6px 16px, 6px 22px, 7px 28px, 6px 16px, 6px 22px, 7px 28px;
|
||||
background-position:
|
||||
3px center,
|
||||
9px center,
|
||||
16px center,
|
||||
calc(100% - 9px) center,
|
||||
calc(100% - 15px) center,
|
||||
calc(100% - 23px) center;
|
||||
}
|
||||
.gym-label {
|
||||
font-size: 0.66rem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue