docs: add comprehensive README

Covers stack, getting started, project structure, content model
(posts + authors), image processing pipeline, design system
(tokens, typography, components), templates, HTMX/Alpine
interactivity, SEO, Docker/nginx/Traefik deployment, and
content authoring workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 11:06:55 +02:00
parent 84624fec33
commit d1ce2e7eb3
+337
View File
@@ -0,0 +1,337 @@
# PIVOINE
> *Where the machine dreams.*
An editorial magazine for AI-generated art and generative aesthetics — built on Hugo, styled with Tailwind v4, animated with Alpine.js and HTMX.
---
## Stack
| Layer | Technology |
|-------|-----------|
| Static site generator | Hugo (extended) |
| CSS | Tailwind CSS v4 via PostCSS |
| Reactivity | Alpine.js v3 |
| Navigation | HTMX v2 (boosted links + View Transitions) |
| Fonts | Bebas Neue · Barlow · Share Tech Mono (Google Fonts) |
| Analytics | Umami (cookie-free) |
| Server | nginx (Docker) → Traefik → Coolify |
---
## Getting Started
**Prerequisites:** Node.js 22+, pnpm, Hugo extended
```bash
pnpm install
pnpm dev # Hugo dev server with drafts, http://localhost:1313
pnpm build # Production build → public/
```
---
## Project Structure
```
pivoine.art/
├── assets/
│ ├── css/main.css # Design tokens + all component styles
│ └── js/main.js # HTMX progress bar, lazy-load videos
├── content/
│ ├── _index.md # Homepage front matter
│ ├── about/index.md
│ ├── imprint/index.md
│ ├── authors/
│ │ └── {slug}/
│ │ ├── index.md # Author profile
│ │ └── avatar.png # Page resource (auto-processed to WebP)
│ └── posts/
│ └── {slug}/
│ ├── index.md # Post front matter
│ ├── banner.png # Hero image (or 01.png as fallback)
│ ├── 01.png # Gallery image
│ ├── 01.mp4 # Optional companion video
│ └── 02.png …
├── layouts/
│ ├── _default/ # baseof, list, single, terms
│ ├── partials/ # head, nav, footer, cards, img, schema …
│ ├── posts/single.html # Article template
│ ├── authors/ # Author list + profile
│ ├── categories/ tags/ # Taxonomy pages
│ ├── index.html # Homepage
│ └── 404.html
├── static/
│ ├── images/og-default.jpg
│ └── favicon.* / site.webmanifest
├── Dockerfile
├── nginx.conf
├── hugo.toml
└── postcss.config.js
```
---
## Content Model
### Posts
```yaml
title: "Hyperloop"
description: "Short editorial summary shown in cards and meta tags."
date: 2025-08-17
author: "valknar" # must match content/authors/{slug}/
featured: true # appears in homepage hero / secondary strip
categories: [Surreal]
tags: [futuristic, vibrant, urban]
# Optional — only needed when not using page-bundle images
banner:
type: image # or "video"
src: "/images/posts/…"
alt: "Alt text"
background: # full-bleed blurred backdrop on article page
type: image
src: "/images/posts/…"
alt: "Alt text"
```
**Page-bundle images (preferred):**
Drop numbered images and optional companion videos alongside `index.md`:
```
posts/hyperloop/
├── index.md
├── banner.png ← hero (detected automatically)
├── 01.png ← gallery item 1
├── 01.mp4 ← plays instead of 01.png if present
├── 02.png
└── 03.png …
```
Hugo automatically resizes all bundle images to WebP at multiple widths — no manual optimisation needed.
### Authors
```yaml
title: "Valknar"
name: "Valknar"
bio: "Short biography shown in bylines and author profile."
social:
instagram: "handle"
x: "handle"
```
Place `avatar.png` (or `.jpg`) in the same directory — it is converted to 96 × 96 px WebP automatically.
---
## Image Processing
All page-resource images are handled by `layouts/partials/img.html`, which converts to WebP and emits responsive `srcset`:
| Context | Widths generated | `sizes` hint |
|---------|-----------------|-------------|
| Post banner | 1200w · 1800w | `100vw` |
| Gallery image | 800w · 1200w | `33vw` on desktop |
| Featured card | 800w · 1200w | `50vw` on desktop |
| Grid card thumbnail | 600w · 1000w | `25vw` on desktop |
| Author avatar | 96px | — |
**Before / after example (hyperloop/banner.png):**
| Original PNG | WebP 1800w | WebP 600w |
|-------------|-----------|----------|
| 7.9 MB | 496 KB | 56 KB |
Processed images are cached in `resources/_gen/` (gitignored).
---
## Design System
### Colour Palette
```css
/* Base — deep navy-black */
--color-void: #050510
--color-ink: #09091A
--color-concrete: #0F0F22
--color-zinc: #1C1C38
/* Neutrals */
--color-fog: #6868A0
--color-chalk: #B8B8E0
--color-paper: #EEEEFF
/* Accent triad */
--color-heat: #FF1A8C /* hot pink — primary */
--color-pulse: #9B00FF /* electric purple — mid */
--color-frost: #00C8FF /* ice blue — cool */
```
### Typography
| Role | Font | Usage |
|------|------|-------|
| Display | Bebas Neue | Headlines, oversized decorative text |
| Body | Barlow 300600 | Editorial prose, UI copy |
| Label | Share Tech Mono | Metadata, badges, specs |
### Component Classes
```
.btn CTA button — clip-path skew, shimmer sweep on hover
.btn-outline Transparent variant
.btn-frost Ice blue variant
.badge Parallelogram sticker (multiple colour variants)
.badge-outline Bordered, transparent
.card-comic Card with 1.5px border + lift + glow on hover
.card-media Media container — zoom on hover
.gradient-line 1 px heat → pulse → frost divider
.graffiti-tag Oversized hollow decorative text (text-stroke)
.speed-lines Isometric triangle grid background
.halftone Diamond crosshatch background
.spray-bg Radial dot pattern
.text-gradient Static pink → purple → ice gradient text
.text-gradient-animated Looping gradient (5 s)
.label Monospace spec-sheet micro-text
.prose-editorial Long-form article body styles
.gutter-x Responsive horizontal padding (clamp 1 3 rem)
```
---
## Templates
| Template | Route | Description |
|----------|-------|-------------|
| `index.html` | `/` | Hero (featured post) · secondary cards (3) · latest grid (8) · about strip |
| `posts/single.html` | `/posts/{slug}/` | Article header · author byline · banner · prose · gallery · tags · related |
| `_default/list.html` | `/posts/` | Paginated post grid (12/page) |
| `authors/single.html` | `/authors/{slug}/` | Author profile · all posts by author |
| `authors/list.html` | `/authors/` | Author grid |
| `categories/single.html` | `/categories/{slug}/` | Posts by category |
| `_default/terms.html` | `/categories/` `/tags/` | Badge cloud of all terms + counts |
| `404.html` | `*` | Styled error page |
---
## Navigation & Interactivity
**HTMX**`hx-boost` on `<body>` intercepts all internal links, swaps only `#main-content`, and pushes URL history. Combined with the CSS View Transitions API for smooth page animations.
**Alpine.js**`$store.nav.path` drives active link highlighting. `$store.nav.open` toggles the mobile fullscreen overlay (teleported to `<body>` to escape header stacking context).
**Mobile menu** — animated hamburger → ✕, staggered link slide-in with `mob-link-in` keyframe.
---
## SEO & Meta
- Open Graph + Twitter Card on every page
- JSON-LD structured data (`WebSite` on home, `BlogPosting` on articles)
- `robots.txt` generated by Hugo (`enableRobotsTXT = true`)
- `X-Robots-Tag: index, follow` set in nginx
- Canonical URLs, `article:modified_time` from git history
- Sitemap at `/sitemap.xml`
---
## Deployment
### Docker
```dockerfile
# Stage 1 — build
FROM node:22-alpine AS builder
# installs Hugo + pnpm, runs pnpm build
# Stage 2 — serve
FROM nginx:alpine
COPY --from=builder /app/public /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
```
```bash
docker build -t pivoine-art .
docker run -p 8080:80 pivoine-art
```
### nginx
- Gzip enabled for text, CSS, JS, SVG, JSON
- Static assets: `Cache-Control: public, immutable` · 1 year
- HTML: `Cache-Control: no-store, no-cache` (always fresh)
- Clean URLs via `try_files $uri $uri/ $uri.html`
### Traefik / Coolify
The container sits behind Traefik (managed by Coolify). A global `security-headers` middleware in `stacks/traefik/dynamic/security.yaml` applies HSTS, CSP, and other headers to all services. A separate `no-index` middleware in the same file can be applied to internal services that should not be indexed.
---
## Adding Content
### New post
```bash
# Create a leaf bundle
mkdir -p content/posts/my-new-drop
touch content/posts/my-new-drop/index.md
# Drop images alongside index.md
cp ~/renders/banner.png content/posts/my-new-drop/
cp ~/renders/01.png content/posts/my-new-drop/
```
Fill in `index.md` front matter, run `pnpm dev`, and the post appears immediately. Hugo handles WebP conversion, srcset generation, and gallery detection automatically.
### New author
```bash
mkdir -p content/authors/my-handle
touch content/authors/my-handle/index.md
cp ~/avatar.png content/authors/my-handle/
```
Reference the author slug in any post's `author:` field.
---
## Configuration
Key settings in `hugo.toml`:
```toml
[params]
description = "An editorial magazine for AI-generated art …"
tagline = "Where the machine dreams."
author = "Pivoine Editorial"
logoText = "PIVOINE"
twitterHandle = "pivoineArt"
ogImage = "/images/og-default.jpg"
copyrightYear = "2026"
umamiSrc = "https://umami.pivoine.art/script.js"
umamiId = "…"
[params.social]
instagram = "https://instagram.com/…"
x = "https://x.com/…"
```
Menus are defined as `[[menus.main]]` and `[[menus.footer]]` arrays in the same file.
---
## License
© 2026 Pivoine Editorial. All rights reserved.