aboutsummaryrefslogtreecommitdiff
path: root/CLAUDE.md
blob: c8c8c620173aac6516a35ccebaad5a682874730e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

A static personal website at `fun.tylerhoang.xyz` styled as a retro desktop OS. There is no build system, no framework, no package manager. Everything is plain HTML/CSS/JS served directly by a web server.

## Development

Preview locally with any static server:

```bash
python3 -m http.server 8080
# or
npx serve .
```

Syntax-check JS without running it:

```bash
node --check aero.js
node --check index.js
node --check articles.js
```

There are no tests and no lint step.

## Architecture

### Entry flow

`enter.html` → welcome splash/gate → user clicks "enter" → `index.html` main desktop

Both pages load `aero.css` and `aero.js`. `index.html` additionally loads `articles.js` then `index.js`.

### Shared library: `aero.js`

Exports everything under `window.Aero`:

- **Background fields**: `spawnBubbles(host, count, speed)`, `spawnShards(host, count, kind, speed)` — generate DOM elements for the ambient animated background. Called once on mount; animation is pure CSS. Both fields are always in the DOM; CSS shows only the active-theme one.
- **Theme API**: `getTheme()`, `setTheme(t)`, `mountThemeSwitcher()`, `initTheme()` — source of truth is `document.body[data-theme]` = `"aero"` | `"chrome"`, persisted in `localStorage["tyler.theme"]`. A tiny inline script at the top of `<body>` applies the saved theme before paint.
- **Window management**: `makeDraggable(el, handle)`, `makeResizable(el, options)` — used for all `.win` elements in `index.html`.
- **UI helpers**: `counterHTML()`, `nowPlayingHTML()`, `animateEq()`, `musicToggleHTML()`, `makeClouds()`, `sparkleCursor()`
- **API calls**: `fetchLastFm()`, `fetchFilms()`, `fetchVisitorCount()`, `fetchReelMouthFeed()`

### Stylesheets

- `aero.css` — the entire design system. Aero (Frutiger) styles are the base; `body[data-theme="chrome"]` overrides apply the Y2K liquid-metal treatment. All `!important` on chrome overrides is intentional — the base styles set defaults that chrome must override.
- `index.css` — desktop-specific layout (`.desk`, `.win`, `.taskbar`, `.icons`, `.icon`, etc.)

### Content

- `articles.js` — defines the `ARTICLES` array. Each article is `{ slug, title, date, tag, excerpt, body }` where `body` is an HTML string. The faux browser in `index.html` renders these client-side — there is no server routing for articles.

### Server-side (PHP)

- `counter.php` — visitor counter backed by a flat file (`counter.dat`), deduplicated by 24h cookie.
- `podcast.php` — RSS proxy for the Reel Mouth podcast feed (Anchor/Spotify), returns `{ art, episodes }`.

### Asset conventions

- Colors: OKLCH throughout. Do not convert to hex/rgb.
- Fonts: loaded from Google Fonts in each HTML `<head>`. Aero uses Plus Jakarta Sans + IBM Plex Mono + Caveat. Chrome uses Audiowide + Michroma + Space Grotesk + IBM Plex Mono.
- Images: `img/wallpaper.png` (aero), `img/wallpaper-chrome.png` (chrome), static assets in `img/static/`.
- Music: `/mus/` — theme-specific tracks swapped live on theme change.

### Adding an article

Add an entry to the `ARTICLES` array in `articles.js`. The browser window in the desktop renders it automatically — no routing changes needed.