2026-05-19 07:56:55 +02:00
# Roux
A slow-publishing fashion journal — one hundred photographs at a time.
Editorial, couture, beauty and backstage plates.
**Live:** [roux.pivoine.art ](https://roux.pivoine.art )
---
## Stack
| Layer | Tool |
|---|---|
2026-08-29 21:11:24 +02:00
| Static site generator | Hugo Extended (≥ 0.161) |
| CSS | Tailwind CSS v4, compiled by Hugo's `css.TailwindCSS` |
2026-05-19 07:56:55 +02:00
| JavaScript | Vanilla (no framework) |
| Package manager | pnpm |
| Server | nginx (Docker) |
## Development
```bash
2026-08-29 21:11:24 +02:00
pnpm install # installs @tailwindcss/cli (Hugo shells out to it)
pnpm dev # hugo server -D → http://localhost:1313
2026-05-19 07:56:55 +02:00
```
2026-08-29 21:11:24 +02:00
Hugo builds the CSS itself via `css.TailwindCSS` (see `layouts/partials/css.html` ),
watches `assets/` , and hot-reloads. There is no separate CSS build step or watcher.
Requires `node` on `PATH` — Hugo execs `node_modules/.bin/tailwindcss` .
2026-05-19 07:56:55 +02:00
## Build
```bash
2026-08-29 21:11:24 +02:00
pnpm build # hugo --minify → public/
2026-05-19 07:56:55 +02:00
```
2026-08-29 21:11:24 +02:00
CSS is emitted as a fingerprinted, integrity-hashed resource under `public/css/` ;
`hugo_stats.json` (the class list Tailwind scans) is generated on every build and
gitignored.
`pnpm-workspace.yaml` sets `preferSymlinkedExecutables: true` so pnpm links
`node_modules/.bin/tailwindcss` as a symlink — Hugo rejects pnpm's default shell
shim (`binary "tailwindcss" is not a Node.js script` ).
2026-05-19 07:56:55 +02:00
## Content
2026-08-29 21:11:24 +02:00
Posts live in `content/posts/<issue>/<slug>/` as Hugo page bundles (e.g.
`content/posts/01/…` , `content/posts/02/…` ). Each bundle contains:
2026-05-19 07:56:55 +02:00
- `index.md` — frontmatter with `plate` , `title` , `description` , `categories` , `tags` , `issues` , optional `featured: true`
- `<slug>.png` — the plate image (used for thumbnails, lightbox, og:image)
2026-08-29 21:11:24 +02:00
`content/posts/<issue>/_index.md` carries `build.render: never` so only `/posts/<issue>/<slug>/`
pages are emitted — the per-issue landing page is the taxonomy term at `/issues/<issue>/` .
Issue membership is the `issues` taxonomy; each issue also has a branch bundle at
`content/issues/<id>/_index.md` (`title` , `description` , `issueNumber` , `season` , `status` ).
2026-05-19 07:56:55 +02:00
2026-08-29 21:11:24 +02:00
### Adding a new issue
2026-05-19 07:56:55 +02:00
2026-08-29 21:11:24 +02:00
1. Write `data/prompts/issue-NN.json` — an array of `{slug, title, category, tags, description, prompt, featured?}` .
2. Generate images: `python3 scripts/generate-images.py --prompts data/prompts/issue-NN.json --issue NN`
(Replicate `flux-1.1-pro` by default; token from `$REPLICATE_API_TOKEN` or `~/.env` ).
3. Face-swap one consistent model into every plate:
`python3 scripts/faceswap-images.py --issue NN --source ~/Bilder/palina.webp`
(FaceFusion at `~/Projekte/facefusion` ; add `--enhance` for a sharper face pass).
4. Upscale: `python3 scripts/upscale-images.py --issue NN`
(Upscayl + the `remacri-4x` model, then clamped to a 4992 px long edge; `--scale 2` or
`--max-edge 2880` keeps the committed PNGs lean).
5. Build the bundles: `python3 scripts/build-issue.py --issue NN --prompts data/prompts/issue-NN.json`
6. Add `content/issues/NN/_index.md` , add `content/posts/NN/_index.md` (`build.render: never` ),
flip the previous issue's `status` away from `current` , and update `hugo.toml` `[params]`
(`issueIds` , `issueNumber` , `issueName` , `issueSeason` , `issueBlurb` ).
Steps 2– 4 all write `content/posts/NN/<slug>/<slug>.png` in place and are each idempotent
(re-run to finish a failed batch; `--force` to redo). Pass `--out DIR` to stage instead.
### Importing issue 01
Issue 01 is sourced from `~/projects/ginger/posts.csv` and matching images in
`~/projects/ginger/images/final/selected/` :
2026-05-19 07:56:55 +02:00
```bash
2026-08-29 21:11:24 +02:00
python3 scripts/import-posts.py # writes content/posts/01/
2026-05-19 07:56:55 +02:00
```
This creates or updates page bundles, assigns plate numbers (sorted alphabetically by filename for stability), normalises descriptions, and copies images.
## Design tokens
All colours, fonts, and spacing are defined in `assets/css/main.css` inside `@theme {}` and mirrored as CSS custom properties in `:root` . Edit there — not in a Tailwind config file (v4 has none).
Key tokens: `--color-paper` `--color-ink` `--color-roux` `--font-display` `--font-serif` `--font-sans` `--pad` `--gap`
## Deployment
2026-08-29 21:11:24 +02:00
`Dockerfile` is a two-stage build: `hugomods/hugo:debian-node-*` runs
`pnpm install` then `hugo --minify --environment production` (Hugo compiles
Tailwind in-process — no pre-build CSS step), and `nginx:alpine` serves the
result with `nginx.conf` . Analytics (Umami) are injected only in the production
environment.
`nginx.conf` also 301-redirects the legacy flat issue-01 plate URLs
(`/posts/<slug>/` → `/posts/01/<slug>/` ); widen its negative-lookahead when
adding issue 03+.
2026-05-19 07:56:55 +02:00
## License
All editorial content, photographs, and design © Roux MMXXVI. All rights reserved.
Contents are explicitly excluded from AI/ML training use without written consent.