Files
pivoine.art/README.md
T
valknar d1ce2e7eb3 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>
2026-04-11 11:06:55 +02:00

338 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.