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

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

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

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

/* 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

HTMXhx-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

# 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
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

# 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

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:

[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.

S
Description
An editorial magazine for AI-generated art and generative aesthetics.
Readme 282 MiB
Languages
HTML 70.1%
CSS 24.3%
JavaScript 3.3%
Shell 1.5%
Dockerfile 0.8%