Free

Quarterly Magazine

A whole magazine site in one offline file: wood-type contents poster, a masthead that turns with each issue, woodblock plates cut from the stories.

Install

Install the "Quarterly Magazine" skill into ~/.claude/skills/quarterly-magazine.
Download each file and save it under that folder:
https://skilllift.pages.dev/packs/quarterly-magazine/SKILL.md → SKILL.md
https://skilllift.pages.dev/packs/quarterly-magazine/references/style.md → references/style.md
https://skilllift.pages.dev/packs/quarterly-magazine/assets/template.html → assets/template.html
https://skilllift.pages.dev/packs/quarterly-magazine/assets/template.zh.html → assets/template.zh.html
https://skilllift.pages.dev/packs/quarterly-magazine/assets/fonts-license.txt → assets/fonts-license.txt
https://skilllift.pages.dev/packs/quarterly-magazine/scripts/check.mjs → scripts/check.mjs
https://skilllift.pages.dev/packs/quarterly-magazine/scripts/fill.mjs → scripts/fill.mjs
https://skilllift.pages.dev/packs/quarterly-magazine/scripts/drive.mjs → scripts/drive.mjs
Then read SKILL.md, tell me what it does, and remind me to start a new session.

Unpack it into ~/.claude/skills

Same skill, different brief

The left is the preview. The right was made by a separate agent from a different brief, using only the installed skill and without seeing the preview.

The preview
The preview
A new brief, made by a separate agent
A new brief, made by a separate agent

One test run on a fictional brief. Letterforms can differ on systems without the same fonts.

Builds a complete offline site for an independent magazine, journal or newsletter as one HTML file with hash routes, rendered from one JSON block of stories, topics, products and plans. Letterpress look on warm paper with one ink and one spot colour, a masthead that changes with the issue number, a contents-as-poster front page on a folded sheet, woodblock plates generated from each story's data. Search, filters, saved stories, shop, bag, and demo checkout and subscribe flows all really work and take no payment. Use when someone asks for a magazine, journal, zine or newsletter website, an editorial site with a shop, or this style by name.

What it must pass before delivery

  • check.mjs passes without --static
  • drive.mjs passes
  • I looked at every screenshot at 1280, 768 and 390, including overlays and state_ shots
  • Every story is the user's real text, or labelled fictional
  • No invented testimonials, reader counts, awards or press quotes
  • fictional is set correctly, and the label is in the notice, footer and FAQ when true
  • Inferred lines are listed for the user
  • One language throughout
Read the full SKILL.md
---
name: quarterly-magazine
description: Builds a complete offline site for an independent magazine, journal or newsletter as one HTML file with hash routes, rendered from one JSON block of stories, topics, products and plans. Letterpress look on warm paper with one ink and one spot colour, a masthead that changes with the issue number, a contents-as-poster front page on a folded sheet, woodblock plates generated from each story's data. Search, filters, saved stories, shop, bag, and demo checkout and subscribe flows all really work and take no payment. Use when someone asks for a magazine, journal, zine or newsletter website, an editorial site with a shop, or this style by name.
version: 1.0.0
---

# Quarterly Magazine

The look and all behaviour live in `assets/template.html` (English) and `assets/template.zh.html` (Chinese). You write one JSON brief. `scripts/fill.mjs` puts it into the template, and the engine draws every page from it. Do not edit the style, engine or interface text: `scripts/check.mjs` compares them with the template byte for byte.

Read `references/style.md` for tokens, type scale, motion and do and do not rules.

## Inputs to ask for

Ask once, in one message, for whatever the brief does not give.

Required:
1. Name of the magazine.
2. What it covers, in one line.
3. Issue number. A season or date only if there is a real one.
4. Stories with their real text: title, one or two sentence standfirst, topic, body, and a byline if there is one.
5. Topics, with a sentence on each.

Optional: an editor's letter; about text and staff; a contact email, address or note; products with prices; subscription plans; FAQ entries; images to replace plates; language (Chinese uses the Chinese template).

## Steps

1. Write `brief.json` in the shape shown under Fields. Add `"lang":"zh"` for Chinese. Everything you need is on this page; the pack's example is not part of what gets installed.
2. `node scripts/fill.mjs brief.json output/index.html`
3. `node scripts/check.mjs output/index.html --shots shots/` Fix the brief and rerun until it passes. About a minute per run on a six-story site.
4. `node scripts/drive.mjs output/index.html` drives every feature at three widths, motion on and off. About a minute.
5. Open and look at every screenshot in `shots/`: every route at 1280, 768 and 390, the search, bag and menu overlays, and the `state_` shots: `saved_filled`, `checkout_blank`, `checkout_errors`, `checkout_filled`, `receipt_checkout`, `subscribe_errors`, `receipt_subscribe`, `contact_errors`, `contact_note`. Rewrite words that read badly. Do not touch the CSS. The checker also fails on a numeral touching the masthead, descenders hitting what lies below, 140 px or more of empty space inside a column, a band under the footer, a story listed twice on a topic page, and a lone last word in running text.

Requirements: Node 18 or later. Check and drive need Playwright: `npm install playwright && npx playwright install chromium`, or set `NODE_PATH` to a `node_modules` that has it. Without it `check.mjs` exits 3 and tells you so. `--static` runs the data checks alone and is not a pass. No network, no build step, no other dependency; the output is a single file of about 210 KB plus your text.

## Fields

Limits count characters, with Chinese, Japanese and Korean characters counting as 2. The checker enforces every limit. Leave out any optional field you have no real content for: the page then omits that part. There are no placeholders to fill in. Delete `products`, `plans` or `faq` if you have none. The smallest working shape:

```json
{
  "lang": "en",
  "site": { "name": "", "short": "", "tagline": "", "issue": { "number": 1 }, "fictional": false, "currency": "$" },
  "topics": [ { "id": "", "name": "", "blurb": "" } ],
  "stories": [ { "id": "", "title": "", "dek": "", "topic": "", "body": ["First paragraph.", { "h": "Subheading" }, "Next paragraph."] } ],
  "products": [ { "id": "", "name": "", "price": 12, "blurb": "" } ],
  "plans": [ { "id": "", "name": "", "price": 0 } ],
  "faq": [ { "q": "", "a": "" } ]
}
```

### site
| Field | Meaning | Limit | If thin or missing |
|---|---|---|---|
| `name` | Full name, set as the masthead. Required | 60 | Ask. A long name breaks onto up to three lines; a very short one gets a plate band filling the rest of the masthead |
| `short` | Name in the top bar. Required when `name` is over 16 | 16 | Use the name's usual short form; if none, its main two words |
| `tagline` | What it covers. Shows in the folio and About | 90 | Restate the user's own description |
| `issue.number` | Whole number, drives masthead and colours. Required | 1 to 999 | If there are no issues yet, use 1 |
| `issue.label` | Optional. Season or date, such as "Autumn" | 24 | Omit, and the folio shows the number alone. Never invent a date |
| `fictional` | Must be true for anything invented, false for a real publication. Required | true or false | True adds the label to the notice, footer and FAQ automatically |
| `notice` | Replaces the top strip's text. If `fictional` is true it must itself say fictional | 120 | Omit |
| `currency` | Symbol before prices | | `$` |
| `press` | Pins the colours: `prussian`, `bottle`, `aubergine`, `soot` | | Omit, so colours turn with the issue |
| `letter` | Editor's letter on the home page; a line break starts a paragraph | 500 | Omit, and the fold shows two panels |
| `about` | Array of paragraphs for About | | Omit, and About gives a one-line count of this file's stories and topics |
| `staff` | Array of `{name, role}` | | Omit. Never invent people for a real publication |
| `contact` | `{email, address, note}` | | Omit any you lack. The page then says no address is published, which is the right answer for most fictional titles too. Never use a made-up address for a real publication. A `.invalid` address on a fictional one shows as plain text with no mail link |
| `verifiedClaims` | Set true only if the user supplied award or reader figures as fact | | Omit |

### topics (at least one)
`id` (a-z, 0-9, hyphens), `name` (28), `blurb` (160). With no topics given, make one from the magazine's subject and list it as inferred. Topics with no stories stay out of the home list but keep their page.

### stories (at least one)
`id`, `title` (80), `dek` the standfirst (170), `topic` (a topic id), `body`, and optionally `author` (40), `issue` (defaults to the current issue), `date` (24), `image` and `imageAlt`, `plate` (`{comp, motif}`), `short`.

`body` is an array. A string is a paragraph, and `*word*` marks emphasis: italics in English, spot colour with dots under the characters in Chinese. Objects: `{"h":"Subheading"}`, `{"q":"Pull quote"}`, `{"list":["a","b"]}`, `{"note":"Boxed aside"}`.

- Real text only. Each story needs 80 words or 150 Chinese characters. If the user gave only a short note, keep their words and set `"short": true`. Never pad it out.
- No standfirst given: use the story's own first sentence. No byline given: leave `author` out, and the page shows none. A byline equal to the magazine's name is also hidden.
- Few stories: one is fine. The poster and grids resize to the real count, with no empty slots. Do not add stories to fill space.
- Long title: over 34 units is set in tall condensed type on up to three lines. If the checker reports a line shrinking too far, shorten it.

### products (optional, 44 name, 200 blurb)
`id`, `name`, `price` (number), `blurb`, `details` (array), `options` (array), `optionLabel`, `plate`, `image`, `imageAlt`. With none, the shop, bag and checkout disappear from the site, and their addresses show the 404 page.

### plans (optional, 28 name, 20 period, 110 blurb)
`id`, `name`, `price` (required), and optionally `period`, `blurb`, `includes`. With no plans, Subscribe disappears the same way.

- `price`: a number. 0 prints the word Free in large type, unless the plan's own name already contains it, so "Free" is a fine name.
- `period`: what the price buys, such as "a year" or "each season". Leave it out if the user gave none. Never write something like "free" here as well.
- `includes` and `blurb`: only benefits the user stated. If they gave a name and a price and nothing more, leave both out. The card still works. Do not invent benefits, and list any you inferred.

### faq (optional, 90 question, 400 answer)
`q`, `a`. Yours come first. Honest entries about the demo shop, the demo subscription and where saved stories live are always added, plus the fictional entry when `fictional` is true.

## Routes

`#/` home, `#/stories` (`?topic=`, `?issue=`, `?view=grid|index`), `#/story/<id>`, `#/topics`, `#/topic/<id>`, `#/saved`, `#/shop`, `#/shop/<id>`, `#/checkout`, `#/subscribe` (`?plan=<id>`), `#/about`, `#/contact`, `#/faq`. Anything else shows the designed 404. The `/` key opens search; Escape closes any overlay.

## Automatic behaviour

- **Masthead:** issue number modulo 4 picks the arrangement and press colours: 0 numeral right, prussian; 1 numeral left, bottle; 2 reversed ink block, aubergine; 3 name in spot colour, soot. The numeral and the band under the name also change with the number.
- **Front poster:** lists the current issue's stories, up to nine, each fitted to the full width. A short title stops at a size cap and a plate strip fills the rest of the line. Earlier issues become a "more" link.
- **Plates:** each story and product gets a woodblock image from its id, title, standfirst, topic and text. Words in the title or standfirst choose the motif where they can (water gives waves, fire gives rays, wall gives bricks); no motif repeats until all twelve are used. A letter plate uses a glyph with enough ink, skipping thin ones such as 一 or I, and turns into a medallion if none exists. Pinning `plate.comp` to `letter` follows the same rule. `plate.comp` is one of horizon, quilt, arch, diagonal, medallion, strata, letter, columns. `plate.motif` is one of waves, scales, chevrons, reeds, bricks, rays, hatch, rings, diamonds, gouges, rope, ripples. An `image` (a local file beside the page, or a data URI) replaces the plate everywhere.
- **Topic pages:** the newest story is the lead and the archive holds the rest, so one story means no archive. A single card spans the row with an excerpt of its first paragraph.
- **Fold panels:** plate bands take up whatever height the text leaves, so few topics or no letter leave no hole.
- **Footer:** sits at the bottom of the window on short pages.
- **Last lines:** the final word or last few characters of running text stay together.
- **Order:** newest issue first, then brief order. **Reading time** is computed from the text.
- **Stored in this browser:** saved stories, bag, grid or index choice, demo slip counter.
- **Demo flows:** checkout and subscribe validate, then show a numbered demo slip on the page, stating that nothing was charged or sent. There is no card field. The contact form only lays out the note, with a mail-app link if a contact email exists. Do not change this wording: if the user wants real payments, see below.

## When things are unclear

- Thin brief: restate the user's facts, never invent. List every inferred line when you deliver.
- Nobody can answer: proceed, and list your assumptions.
- No real link or address: leave the field out.
- Different length: story length is up to the user. There is no upper limit, and very long pieces get the same reading column.
- Sample or invented content for a real client: set `fictional` to true until the real text arrives.

## Before delivering

- [ ] `check.mjs` passes without `--static`
- [ ] `drive.mjs` passes
- [ ] I looked at every screenshot at 1280, 768 and 390, including overlays and `state_` shots
- [ ] Every story is the user's real text, or labelled fictional
- [ ] No invented testimonials, reader counts, awards or press quotes
- [ ] `fictional` is set correctly, and the label is in the notice, footer and FAQ when true
- [ ] Inferred lines are listed for the user
- [ ] One language throughout

## When not to use it

- A daily news site, or a publication with hundreds of stories: everything is in one file.
- A shop that must take real money or a list that must really send mail: this has no backend. Use it as the design, then have a developer connect a real service and replace the demo wording.
- A photo-led magazine where pictures carry the story: images replace plates, but the design is built around type.

Similar skills