781 lines
30 KiB
Markdown
781 lines
30 KiB
Markdown
# Guide Style System — Authoring & Build Specification
|
||
|
||
This document is a **complete, self-contained specification** for producing a polished, book-style guide as a single HTML file that renders to a clean A4 PDF. It is **topic-neutral**: use it for programming subjects (PHP, Godot, Blender) and equally for non-technical ones (nutrition, finance, psychology, communication, language learning, …).
|
||
|
||
You will normally be given two things: this specification and possibly one reference HTML file built with it. From those alone you must be able to:
|
||
|
||
1. Gather the subject knowledge yourself (research as needed).
|
||
2. Decide a structure (parts → chapters).
|
||
3. Write a single HTML file that embeds the CSS from this document verbatim.
|
||
4. Convert that HTML to PDF.
|
||
|
||
Follow this spec exactly. The visual identity depends on small details (spacing, weights, the single accent color), so do not improvise the CSS. You **do** have full freedom over content, structure, length, and which optional building blocks you use.
|
||
|
||
---
|
||
|
||
## 1. Output contract
|
||
|
||
- **One HTML file**, self-contained: a single `<style>` block in `<head>`, no external CSS, no external JS, no web fonts. Fonts are system fonts only (see §3).
|
||
- The HTML converts to PDF with **WeasyPrint**. The canonical build command is:
|
||
```
|
||
weasyprint guide.html guide.pdf
|
||
```
|
||
- Page size is **A4**. All page furniture (page numbers, running header, footer label) is produced by CSS `@page` rules — you never write headers/footers into the body.
|
||
- The body is laid out as: **Cover → Table of Contents → Part 1 divider → its chapters → Part 2 divider → its chapters → …**
|
||
|
||
If WeasyPrint is unavailable, the HTML must still be a valid, good-looking standalone document; the layout is built so it degrades gracefully in a browser too.
|
||
|
||
---
|
||
|
||
## 2. Mental model of a guide
|
||
|
||
A guide is a short book. The structure is always the same three levels:
|
||
|
||
- **Part** — a major thematic section (e.g. "Fundamentals", "Advanced", "Pitfalls"). Each part gets a full-page **divider**. A guide typically has **3–8 parts**.
|
||
- **Chapter** — one focused topic. Chapters flow continuously down the page (they do not force a page break); each is separated from the previous by spacing and its own heading block. **3–10 chapters per part**. Each chapter is numbered sequentially across the whole guide (Chapter 1, 2, 3 … regardless of part).
|
||
- **Section** (`<h2>`) and **sub-section** (`<h3>`) — structure inside a chapter.
|
||
|
||
### Scope — coverage-driven (middle tier)
|
||
|
||
**10–30 pages, covering all important building blocks of the topic** — everything a practical user needs in real work. More selective than the EndGuide tier ("all building blocks"), far broader than the MiniGuide tier ("core building blocks only"). How much that is depends on the topic.
|
||
|
||
A **topic inventory** is produced beforehand by a research agent and supplied to you. It is binding: every inventory item must appear in the guide as its own section — do not merge, trim, or drop items. Derive parts and chapters from the inventory; part/chapter counts follow from it, there is no default number. Page count is an outcome within the 10–30 range, never a target. State the resulting scope on the cover (parts · chapters).
|
||
|
||
### Voice and content rules (apply to every topic)
|
||
|
||
- Write in the **reader's language** (German request → German guide). This spec is in English, but that does not constrain the output language.
|
||
- Open each chapter with a one- or two-sentence **lead** (`.lead`) that frames why the topic matters.
|
||
- Explain each new term the first time it appears. Assume an intelligent reader who is new to *this* subject.
|
||
- Prefer **short, concrete examples** over abstract description. For technical topics that means small code snippets; for non-technical topics it means worked examples, sample dialogues, before/after comparisons, small tables, checklists, or step lists.
|
||
- Use **callouts** to highlight tips, warnings, side-notes, and deeper digressions — not more than one or two per chapter.
|
||
- Keep prose tight. This is a reference people skim and return to, not an essay.
|
||
|
||
---
|
||
|
||
## 3. The CSS — embed this verbatim
|
||
|
||
Paste the following into a single `<style>` element in `<head>`. **Do not rename classes, change spacing units, or restructure rules.** The only thing you customize is the small block of CSS variables in `:root` (see §4).
|
||
|
||
```css
|
||
/* ============================================================
|
||
GUIDE STYLE SYSTEM · Stylesheet (WeasyPrint)
|
||
============================================================ */
|
||
|
||
/* ---------- PAGE ---------- */
|
||
@page {
|
||
size: A4;
|
||
margin: 20mm 18mm 18mm 18mm;
|
||
@bottom-center {
|
||
content: counter(page);
|
||
font-family: -apple-system, "Segoe UI", sans-serif;
|
||
font-size: 8pt;
|
||
color: #888;
|
||
}
|
||
@bottom-right {
|
||
content: var(--footer-label); /* set in :root, see §4 */
|
||
font-family: -apple-system, "Segoe UI", sans-serif;
|
||
font-size: 8pt;
|
||
color: #888;
|
||
}
|
||
@top-right {
|
||
content: string(chaptertitle);
|
||
font-family: -apple-system, "Segoe UI", sans-serif;
|
||
font-size: 8pt;
|
||
color: #aaa;
|
||
}
|
||
}
|
||
|
||
/* Cover page: no header/footer */
|
||
@page cover {
|
||
margin: 0;
|
||
@bottom-center { content: none; }
|
||
@bottom-right { content: none; }
|
||
@top-right { content: none; }
|
||
}
|
||
|
||
/* Part-divider pages: no running header (the title is already prominent) */
|
||
@page chapterstart {
|
||
@top-right { content: none; }
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
/* ===== CUSTOMIZE PER TOPIC — see §4 ===== */
|
||
--accent: #777BB4; /* main accent */
|
||
--accent-dark: #4F5B93; /* darker shade for headings */
|
||
--accent-darker: #2C3E66; /* darkest, for cover gradient + part titles */
|
||
--footer-label: "Guide"; /* short title shown bottom-right of every page */
|
||
|
||
/* ===== USUALLY LEAVE THESE ALONE ===== */
|
||
--ink: #1a1a1a;
|
||
--muted: #5a6470;
|
||
--line: #d8dde3;
|
||
--bg-soft: #f5f5fb; /* tint this toward your accent if you like */
|
||
--code-bg: #1e2a3a;
|
||
--code-fg: #e6e6e6;
|
||
--plus: #2c8a3e; /* tip / positive */
|
||
--minus: #c0392b; /* warning / negative */
|
||
--neutral: #b8860b; /* deep-dive / aside */
|
||
}
|
||
|
||
html, body {
|
||
font-family: Charter, "Source Serif Pro", Georgia, serif;
|
||
color: var(--ink);
|
||
font-size: 10.5pt;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
/* ============================================================
|
||
COVER
|
||
============================================================ */
|
||
.cover {
|
||
page: cover;
|
||
page-break-after: always;
|
||
height: 297mm;
|
||
background: linear-gradient(150deg, var(--accent-darker) 0%, var(--accent-dark) 45%, var(--accent) 100%);
|
||
color: white;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
padding: 0 22mm;
|
||
}
|
||
.cover-logo {
|
||
width: 30mm; height: 30mm;
|
||
background: rgba(255,255,255,0.14);
|
||
border: 1.5pt solid rgba(255,255,255,0.5);
|
||
border-radius: 7mm;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 20pt; font-weight: 800;
|
||
margin-bottom: 14mm;
|
||
letter-spacing: -1pt;
|
||
}
|
||
.cover h1 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 42pt;
|
||
font-weight: 800;
|
||
line-height: 1.02;
|
||
letter-spacing: -1.5pt;
|
||
margin-bottom: 6mm;
|
||
}
|
||
.cover h1 .light { font-weight: 300; display:block; font-size: 30pt; opacity: 0.85; }
|
||
.cover .sub {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 13pt;
|
||
font-weight: 400;
|
||
opacity: 0.9;
|
||
line-height: 1.5;
|
||
max-width: 130mm;
|
||
margin-bottom: 18mm;
|
||
}
|
||
.cover .meta-row {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 9.5pt;
|
||
opacity: 0.85;
|
||
border-top: 1pt solid rgba(255,255,255,0.3);
|
||
padding-top: 5mm;
|
||
display: flex;
|
||
gap: 8mm;
|
||
}
|
||
.cover .meta-row b { font-weight: 700; }
|
||
.cover-deco {
|
||
position: absolute;
|
||
font-family: "SF Mono", Consolas, monospace;
|
||
font-size: 120pt;
|
||
font-weight: 800;
|
||
opacity: 0.06;
|
||
bottom: 10mm;
|
||
right: 12mm;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* ============================================================
|
||
TABLE OF CONTENTS
|
||
============================================================ */
|
||
.toc {
|
||
page-break-after: always;
|
||
}
|
||
.toc h2 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 22pt;
|
||
font-weight: 800;
|
||
color: var(--accent-dark);
|
||
margin-bottom: 8mm;
|
||
border: none;
|
||
}
|
||
.toc-part {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 10pt;
|
||
font-weight: 800;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5pt;
|
||
color: var(--accent);
|
||
margin: 6mm 0 2mm 0;
|
||
padding-bottom: 1mm;
|
||
border-bottom: 1pt solid var(--line);
|
||
}
|
||
.toc-part:first-of-type { margin-top: 0; }
|
||
.toc-entry {
|
||
display: flex;
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 9.5pt;
|
||
margin: 1.6mm 0;
|
||
align-items: baseline;
|
||
}
|
||
.toc-num {
|
||
color: var(--accent-dark);
|
||
font-weight: 700;
|
||
width: 11mm;
|
||
flex-shrink: 0;
|
||
}
|
||
.toc-title { color: var(--ink); }
|
||
.toc-dots {
|
||
flex: 1;
|
||
border-bottom: 1pt dotted var(--line);
|
||
margin: 0 2mm;
|
||
transform: translateY(-1mm);
|
||
}
|
||
.toc-page { color: var(--muted); font-variant-numeric: tabular-nums; }
|
||
|
||
/* ============================================================
|
||
PART DIVIDER
|
||
============================================================ */
|
||
.part-divider {
|
||
page: chapterstart;
|
||
page-break-before: always;
|
||
page-break-after: always;
|
||
height: 257mm;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
background: var(--bg-soft);
|
||
border-radius: 4mm;
|
||
padding: 0 20mm;
|
||
}
|
||
.part-divider .part-kicker {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 11pt;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 3pt;
|
||
color: var(--accent);
|
||
margin-bottom: 4mm;
|
||
}
|
||
.part-divider h1 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 34pt;
|
||
font-weight: 800;
|
||
color: var(--accent-darker);
|
||
letter-spacing: -1pt;
|
||
line-height: 1.05;
|
||
margin-bottom: 6mm;
|
||
}
|
||
.part-divider .part-desc {
|
||
font-size: 12pt;
|
||
color: var(--muted);
|
||
font-style: italic;
|
||
max-width: 120mm;
|
||
}
|
||
.part-divider .part-chapters {
|
||
margin-top: 10mm;
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 9.5pt;
|
||
color: var(--accent-dark);
|
||
}
|
||
.part-divider .part-chapters span {
|
||
display: block;
|
||
margin: 1.5mm 0;
|
||
padding-left: 5mm;
|
||
border-left: 2pt solid var(--accent);
|
||
}
|
||
|
||
/* ============================================================
|
||
CHAPTER
|
||
============================================================ */
|
||
.chapter {
|
||
margin-top: 8mm; /* chapters flow continuously; no forced page break */
|
||
}
|
||
.chapter-head {
|
||
string-set: chaptertitle content();
|
||
margin-bottom: 6mm;
|
||
padding-bottom: 3mm;
|
||
border-bottom: 2pt solid var(--ink);
|
||
}
|
||
.chapter-num {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 9pt;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 2pt;
|
||
color: var(--accent);
|
||
display: block;
|
||
margin-bottom: 1.5mm;
|
||
}
|
||
.chapter-head h1 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 24pt;
|
||
font-weight: 800;
|
||
color: var(--accent-dark);
|
||
letter-spacing: -0.5pt;
|
||
line-height: 1.1;
|
||
}
|
||
|
||
/* ============================================================
|
||
IN-FLOW HEADINGS
|
||
============================================================ */
|
||
h2 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 14pt;
|
||
font-weight: 700;
|
||
color: var(--accent-dark);
|
||
margin: 6mm 0 2.5mm 0;
|
||
page-break-after: avoid;
|
||
}
|
||
h3 {
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 11pt;
|
||
font-weight: 700;
|
||
color: var(--ink);
|
||
margin: 4mm 0 1.5mm 0;
|
||
page-break-after: avoid;
|
||
}
|
||
p {
|
||
margin-bottom: 2.5mm;
|
||
text-align: justify;
|
||
hyphens: auto;
|
||
}
|
||
p b, li b { color: var(--accent-dark); }
|
||
ul, ol { margin: 1.5mm 0 3mm 6mm; }
|
||
li { margin-bottom: 1mm; }
|
||
|
||
/* ============================================================
|
||
CODE (optional — technical topics only, see §7)
|
||
============================================================ */
|
||
pre {
|
||
background: var(--code-bg);
|
||
color: var(--code-fg);
|
||
font-family: "SF Mono", Consolas, monospace;
|
||
font-size: 8.3pt;
|
||
line-height: 1.5;
|
||
padding: 3mm 4mm;
|
||
border-radius: 2mm;
|
||
margin: 2.5mm 0 3.5mm 0;
|
||
white-space: pre;
|
||
overflow: hidden;
|
||
page-break-inside: avoid;
|
||
}
|
||
.c { color: #6b8aae; font-style: italic; } /* comment / de-emphasized */
|
||
.k { color: #ff79c6; } /* keyword / control word */
|
||
.s { color: #f1c40f; } /* string / number / literal value */
|
||
.f { color: #50fa7b; } /* function / callable name */
|
||
.t { color: #8be9fd; } /* type / tag / class name */
|
||
.v { color: #ffb86c; } /* variable / identifier */
|
||
.a { color: #bd93f9; } /* attribute / annotation / decorator */
|
||
|
||
code.inline {
|
||
font-family: "SF Mono", Consolas, monospace;
|
||
font-size: 9pt;
|
||
background: var(--bg-soft);
|
||
padding: 0.3mm 1.5mm;
|
||
border-radius: 1mm;
|
||
color: var(--accent-dark);
|
||
}
|
||
|
||
/* ============================================================
|
||
CALLOUTS
|
||
============================================================ */
|
||
.callout {
|
||
border-radius: 2mm;
|
||
padding: 2.5mm 4mm;
|
||
margin: 3mm 0;
|
||
font-size: 10pt;
|
||
page-break-inside: avoid;
|
||
display: grid;
|
||
grid-template-columns: 6mm 1fr;
|
||
gap: 3mm;
|
||
}
|
||
.callout-icon {
|
||
font-family: -apple-system, sans-serif;
|
||
font-weight: 800;
|
||
font-size: 14pt;
|
||
line-height: 1;
|
||
text-align: center;
|
||
}
|
||
.callout-body > b:first-child {
|
||
font-family: -apple-system, sans-serif;
|
||
text-transform: uppercase;
|
||
font-size: 8pt;
|
||
letter-spacing: 1pt;
|
||
display: block;
|
||
margin-bottom: 1mm;
|
||
}
|
||
.callout.tip { background: #e8f4ea; border-left: 3pt solid var(--plus); }
|
||
.callout.tip .callout-icon, .callout.tip .callout-body > b:first-child { color: var(--plus); }
|
||
.callout.warn { background: #fdecea; border-left: 3pt solid var(--minus); }
|
||
.callout.warn .callout-icon, .callout.warn .callout-body > b:first-child { color: var(--minus); }
|
||
.callout.note { background: var(--bg-soft); border-left: 3pt solid var(--accent); }
|
||
.callout.note .callout-icon, .callout.note .callout-body > b:first-child { color: var(--accent-dark); }
|
||
.callout.deep { background: #fff8e8; border-left: 3pt solid var(--neutral); }
|
||
.callout.deep .callout-icon, .callout.deep .callout-body > b:first-child { color: var(--neutral); }
|
||
|
||
/* ============================================================
|
||
TABLES
|
||
============================================================ */
|
||
table {
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
margin: 2.5mm 0 3.5mm 0;
|
||
font-family: -apple-system, sans-serif;
|
||
font-size: 9pt;
|
||
page-break-inside: avoid;
|
||
}
|
||
th {
|
||
background: var(--accent-dark);
|
||
color: white;
|
||
text-align: left;
|
||
padding: 1.8mm 3mm;
|
||
font-weight: 700;
|
||
}
|
||
td {
|
||
padding: 1.5mm 3mm;
|
||
border-bottom: 1pt solid var(--line);
|
||
vertical-align: top;
|
||
}
|
||
tr:nth-child(even) td { background: var(--bg-soft); }
|
||
td code, th code {
|
||
font-family: "SF Mono", Consolas, monospace;
|
||
font-size: 8.2pt;
|
||
}
|
||
|
||
/* small helpers */
|
||
.lead {
|
||
font-size: 11.5pt;
|
||
color: var(--muted);
|
||
font-style: italic;
|
||
margin-bottom: 4mm;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. The only things you customize: `:root` variables
|
||
|
||
Change **four** values per guide; leave the rest unless you have a reason. The accent color is the entire brand: pick one that fits the topic, then derive a darker and darkest shade from it (roughly: accent at ~55% lightness, accent-dark ~40%, accent-darker ~28%, all at similar hue).
|
||
|
||
| Variable | What it is | Example (PHP) |
|
||
|---|---|---|
|
||
| `--accent` | main accent (kickers, links, code-inline, table tint sources) | `#777BB4` |
|
||
| `--accent-dark` | headings, table header background | `#4F5B93` |
|
||
| `--accent-darker` | cover gradient end + part titles | `#2C3E66` |
|
||
| `--footer-label` | short title shown bottom-right every page; **must be a quoted CSS string** | `"PHP Komplett-Guide"` |
|
||
|
||
### Recommended accent palettes (free to ignore or replace)
|
||
|
||
These are starting points — three shades each (accent / dark / darker):
|
||
|
||
- **Programming / PHP** — `#777BB4` / `#4F5B93` / `#2C3E66` (violet)
|
||
- **Godot / game dev** — `#478CBF` / `#3A6E97` / `#27496B` (blue)
|
||
- **Blender / 3D / art** — `#E87D0D` / `#C2670A` / `#8A4906` (orange)
|
||
- **Finance / business** — `#1F7A4D` / `#155C39` / `#0D3D26` (green)
|
||
- **Nutrition / health** — `#D2603A` / `#A8492B` / `#74331E` (terracotta)
|
||
- **Psychology / communication** — `#6A5ACD` / `#52459E` / `#372E6B` (indigo)
|
||
- **Language learning** — `#C0392B` / `#992D22` / `#641E16` (warm red)
|
||
- **Neutral / mixed topics** — `#3D5A73` / `#2E4557` / `#1E2E3B` (slate)
|
||
|
||
When you change the accent, optionally nudge `--bg-soft` toward the same hue (keep it very light, ~96% lightness) so tinted blocks feel cohesive. Everything else (`--ink`, `--muted`, `--line`, callout colors, code colors) stays fixed — those are deliberately topic-independent.
|
||
|
||
---
|
||
|
||
## 5. HTML skeleton
|
||
|
||
The body is a flat sequence of sections. There is no wrapper around chapters; the CSS handles page breaks. Order: cover, TOC, then for each part its divider immediately followed by its chapters.
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="de"> <!-- set to the guide's language -->
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>… Guide title …</title>
|
||
<style>
|
||
/* paste the entire stylesheet from §3 here */
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- COVER -->
|
||
<section class="cover"> … </section>
|
||
|
||
<!-- TABLE OF CONTENTS -->
|
||
<section class="toc"> … </section>
|
||
|
||
<!-- PART 1 -->
|
||
<section class="part-divider"> … </section>
|
||
<section class="chapter"> … </section>
|
||
<section class="chapter"> … </section>
|
||
<!-- … more chapters … -->
|
||
|
||
<!-- PART 2 -->
|
||
<section class="part-divider"> … </section>
|
||
<section class="chapter"> … </section>
|
||
<!-- … etc … -->
|
||
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
You may generate this HTML however you like — by hand, or with a small script that concatenates strings. A script helps for long guides because it keeps each chapter in its own readable chunk. If you use a script, **the script is throwaway**; the deliverable is the HTML (and the PDF), not the script.
|
||
|
||
---
|
||
|
||
## 6. The building blocks
|
||
|
||
Below is the exact markup for every block. Copy these shapes; fill in content.
|
||
|
||
### 6.1 Cover
|
||
|
||
```html
|
||
<section class="cover">
|
||
<div class="cover-logo">LOGO</div>
|
||
<h1>MAIN TITLE<span class="light">Subtitle line</span></h1>
|
||
<div class="sub">One or two sentences describing what the guide covers and who it is for.</div>
|
||
<div class="meta-row">
|
||
<span><b>N Parts</b> · M Chapters</span>
|
||
<span><b>Edition / version</b> · Year</span>
|
||
<span>Focus: …</span>
|
||
</div>
|
||
<div class="cover-deco">◆</div>
|
||
</section>
|
||
```
|
||
|
||
- **`.cover-logo`** — 1–4 characters or a single symbol that evokes the topic. Examples: `php`, `gd` (Godot), a Blender-style `b`, `€` (finance), `Ψ` (psychology), `EN` (English), `🍎` is **not** allowed (no emoji in the logo — keep it crisp). Prefer short letterforms or a geometric glyph (`◆ ● ▲ ■`).
|
||
- **`.cover h1`** — the big title. The `<span class="light">` is an optional lighter, smaller second line (e.g. `PHP` then `The Complete Guide`). Drop the span for a single-line title.
|
||
- **`.cover-deco`** — a giant, very faint background glyph in the bottom-right. Pick something topic-flavored: a code fragment (`<?php`, `{ }`, `def`), a symbol (`€`, `∑`, `Ψ`, `♪`), or a single bold letter. Keep it to a few characters; it is decorative and barely visible by design.
|
||
- **`.meta-row`** — two to four short facts separated into `<span>`s. Use it to state scope (parts/chapters), edition/year, and the focus. This is where you set the reader's expectations about length and depth.
|
||
|
||
### 6.2 Table of contents
|
||
|
||
List every part, and under it every chapter, numbered sequentially across the whole guide. There are **no real page numbers** (WeasyPrint does not back-fill them here), so the `.toc-page` element is optional — omit it, or use it only if you maintain numbers yourself. The dotted leader still looks right without a trailing number.
|
||
|
||
```html
|
||
<section class="toc">
|
||
<h2>Contents</h2>
|
||
|
||
<div class="toc-part">Part 1 · Fundamentals</div>
|
||
<div class="toc-entry">
|
||
<span class="toc-num">1</span>
|
||
<span class="toc-title">First chapter title</span>
|
||
<span class="toc-dots"></span>
|
||
</div>
|
||
<div class="toc-entry">
|
||
<span class="toc-num">2</span>
|
||
<span class="toc-title">Second chapter title</span>
|
||
<span class="toc-dots"></span>
|
||
</div>
|
||
|
||
<div class="toc-part">Part 2 · Going Deeper</div>
|
||
<div class="toc-entry">
|
||
<span class="toc-num">3</span>
|
||
<span class="toc-title">…</span>
|
||
<span class="toc-dots"></span>
|
||
</div>
|
||
</section>
|
||
```
|
||
|
||
### 6.3 Part divider
|
||
|
||
One full page that introduces a part. The chapter list mirrors the TOC entries for that part.
|
||
|
||
```html
|
||
<section class="part-divider">
|
||
<div class="part-kicker">Part 1</div>
|
||
<h1>Fundamentals</h1>
|
||
<div class="part-desc">Italic one-liner describing the arc of this part.</div>
|
||
<div class="part-chapters">
|
||
<span>1 · First chapter</span>
|
||
<span>2 · Second chapter</span>
|
||
<span>3 · Third chapter</span>
|
||
</div>
|
||
</section>
|
||
```
|
||
|
||
### 6.4 Chapter
|
||
|
||
The `.chapter-head` is special: its text is captured into the **running header** at the top-right of every page in that chapter (via `string-set: chaptertitle content()`). Note that `content()` concatenates the text of **all** children, so the chapter-number span and the title run together in the header (e.g. "Chapter 1Title"). For a clean separator, end the `.chapter-num` text with a trailing separator such as `Chapter 1 · ` (trailing space + middot) — or accept the run-on; both are acceptable. Either way keep the chapter `<h1>` reasonably short so it fits on one header line.
|
||
|
||
```html
|
||
<section class="chapter">
|
||
<div class="chapter-head">
|
||
<span class="chapter-num">Chapter 1</span>
|
||
<h1>Chapter title</h1>
|
||
</div>
|
||
|
||
<p class="lead">Framing sentence(s) — why this matters.</p>
|
||
|
||
<h2>A section</h2>
|
||
<p>Body text. Use <b>bold</b> for the key term in a sentence. Inline monospace
|
||
like <code class="inline">term</code> works for any short literal —
|
||
a command, a key name, a nutrient, a chord, a German case.</p>
|
||
|
||
<h3>A sub-section</h3>
|
||
<p>…</p>
|
||
|
||
<!-- tables, callouts, code, lists as needed -->
|
||
</section>
|
||
```
|
||
|
||
### 6.5 Callouts
|
||
|
||
Four flavors. Each has a short uppercase label as the **first `<b>`** inside `.callout-body`, then body text. The icon column holds one glyph.
|
||
|
||
```html
|
||
<div class="callout tip">
|
||
<div class="callout-icon">✓</div>
|
||
<div class="callout-body"><b>LABEL</b>Body text giving a practical tip.</div>
|
||
</div>
|
||
|
||
<div class="callout warn">
|
||
<div class="callout-icon">!</div>
|
||
<div class="callout-body"><b>LABEL</b>Body text warning about a trap.</div>
|
||
</div>
|
||
|
||
<div class="callout note">
|
||
<div class="callout-icon">i</div>
|
||
<div class="callout-body"><b>LABEL</b>Body text for a neutral side note.</div>
|
||
</div>
|
||
|
||
<div class="callout deep">
|
||
<div class="callout-icon">◆</div>
|
||
<div class="callout-body"><b>LABEL</b>Body text for an optional deeper dive.</div>
|
||
</div>
|
||
```
|
||
|
||
Meaning of each flavor (topic-independent):
|
||
|
||
| Flavor | Color | Use for |
|
||
|---|---|---|
|
||
| `tip` | green, ✓ | best practice, a recommendation, a shortcut |
|
||
| `warn` | red, ! | a common mistake, risk, or thing to avoid |
|
||
| `note` | accent, i | a neutral aside, clarification, context |
|
||
| `deep` | gold, ◆ | optional depth: history, edge case, advanced detail |
|
||
|
||
Standard icon entities: tip `✓` (✓), warn `!`, note `i`, deep `◆` (◆). You may substitute a more fitting single glyph, but keep it one character.
|
||
|
||
### 6.6 Tables
|
||
|
||
Plain `<table>` with a header row. The styling (accent header, zebra rows) is automatic. Keep tables to a few columns so they fit A4 width.
|
||
|
||
```html
|
||
<table>
|
||
<tr><th>Column A</th><th>Column B</th></tr>
|
||
<tr><td>value</td><td>value</td></tr>
|
||
<tr><td>value</td><td>value</td></tr>
|
||
</table>
|
||
```
|
||
|
||
Tables are one of the most useful blocks for **non-technical** topics too: nutrient comparisons, vocabulary lists, pros/cons, decision matrices, dosage/timing, term glossaries.
|
||
|
||
---
|
||
|
||
## 7. Code blocks — optional, technical topics only
|
||
|
||
The dark `<pre>` block and the highlight span classes exist for subjects that genuinely involve code or other monospaced literal text (config, shell commands, formulas). **For non-technical guides, do not use `<pre>` blocks at all** — they would look out of place. Use tables, lists, callouts, and worked examples instead. (`code.inline` is fine everywhere for short literals.)
|
||
|
||
When you do use code, you **hand-write the highlighting** by wrapping tokens in spans. The classes are intentionally generic so they map onto any language:
|
||
|
||
| Class | Generic meaning | Typical use |
|
||
|---|---|---|
|
||
| `.c` | de-emphasized | comments |
|
||
| `.k` | keyword / control word | `if`, `function`, `class`, `return` |
|
||
| `.s` | literal value | strings, numbers |
|
||
| `.f` | callable name | function / method names |
|
||
| `.t` | type / tag / class | type names, HTML tags, class names |
|
||
| `.v` | identifier | variables |
|
||
| `.a` | annotation | attributes, decorators, directives |
|
||
|
||
Rules for code blocks:
|
||
|
||
- Keep snippets **short** (roughly 2–12 lines). They must fit on one page — `page-break-inside: avoid` is set, so an oversized block can overflow. Split long examples into several captioned blocks.
|
||
- **Escape HTML inside code**: write `<`, `>`, `&`. This is the most common rendering bug.
|
||
- Inside the dark block, a comment uses `<span class="c">`. Indentation is literal spaces (the block is `white-space: pre`).
|
||
- You do not need to highlight every token — highlight the ones that aid reading (keywords, strings, names). Plain text inside `<pre>` is fine and renders in the default light color.
|
||
|
||
Example (PHP-flavored, but the pattern is language-agnostic):
|
||
|
||
```html
|
||
<pre><span class="k">function</span> <span class="f">greet</span>(<span class="t">string</span> <span class="v">$name</span>): <span class="t">string</span> {
|
||
<span class="k">return</span> <span class="s">"Hello, $name"</span>; <span class="c">// interpolation</span>
|
||
}</pre>
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Pitfalls — read before building (these caused real bugs)
|
||
|
||
1. **Escape `<`, `>`, `&` inside `<pre>` and `<code>`.** Unescaped angle brackets silently swallow content or break layout. Always `< > &`.
|
||
2. **Typographic quotes in prose, straight quotes in attributes.** In body text use the target language's real quotation marks (German `„ … "`, English `" … "`). Never let a quote character inside running text collide with HTML attribute quotes. If you generate the HTML from a script, be careful that closing typographic quotes are not accidentally written as escaped ASCII quotes — that corrupts both the string and any nearby `class="…"`. The safe approach: type real `„ … "` / `" … "` glyphs in prose, and reserve `"` strictly for HTML attributes.
|
||
3. **`--footer-label` must be a quoted CSS string**, e.g. `--footer-label: "Nutrition Guide";`. An unquoted value breaks the `@page` rule.
|
||
4. **Keep code blocks and callouts short enough to fit one page.** `page-break-inside: avoid` prevents splitting but cannot shrink an oversized block; it will overflow the page. Break large blocks up.
|
||
5. **Multibyte text is fine** (umlauts, accents, CJK, symbols) — the fonts and UTF-8 charset handle it. But if you ever measure string length in a generator script, count characters, not bytes.
|
||
6. **Chapter `<h1>` feeds the running header.** Keep it short; a very long chapter title wraps awkwardly in the 8pt header.
|
||
7. **Don't add web fonts or external assets.** The look depends on the system-font stack already specified. Adding fonts changes metrics and spacing.
|
||
8. **One accent, three shades.** Don't introduce extra brand colors. Variety comes from the callout colors (green/red/gold), which are fixed and meaningful — not decorative.
|
||
9. **Tables and code don't split** (`page-break-inside: avoid`). If a table is very long, either let it be its own short section or split it into two.
|
||
|
||
---
|
||
|
||
## 9. Build & verify
|
||
|
||
1. Write `guide.html` (single file, embedded `<style>`, UTF-8).
|
||
2. Convert:
|
||
```
|
||
weasyprint guide.html guide.pdf
|
||
```
|
||
3. **Verify visually.** Render a few pages to images and look at them — do not trust the HTML alone:
|
||
```
|
||
pdftoppm -png -r 80 -f 1 -l 1 guide.pdf cover # cover
|
||
pdftoppm -png -r 80 -f 2 -l 2 guide.pdf toc # contents
|
||
pdftoppm -png -r 80 -f 5 -l 6 guide.pdf body # a content spread
|
||
```
|
||
Check: cover gradient and title; TOC alignment; a part divider; at least one callout; at least one table; (if technical) one code block with highlighting; the running header showing the chapter title; no block overflowing a page edge; quotes and special characters rendering as glyphs, not boxes.
|
||
4. If something is off, fix the HTML and rebuild. Two or three iterations is normal.
|
||
5. Deliver **both** `guide.html` and `guide.pdf`.
|
||
|
||
---
|
||
|
||
## 10. Quick checklist
|
||
|
||
- [ ] Single self-contained HTML, embedded `<style>`, no external assets
|
||
- [ ] `:root` accent shades + `--footer-label` set for the topic
|
||
- [ ] Cover: logo glyph, title, subtitle, meta-row, faint deco glyph
|
||
- [ ] TOC lists every part and every chapter, sequential numbering
|
||
- [ ] Each part has a divider; each chapter starts with `.chapter-head` + `.lead`
|
||
- [ ] Callouts used sparingly (≤ ~2 per chapter), correct flavor
|
||
- [ ] Tables for comparisons/lists; code blocks only if technical, short, escaped
|
||
- [ ] Prose in the reader's language; terms explained on first use
|
||
- [ ] Built with WeasyPrint and **visually verified** by rendering pages
|
||
- [ ] Both HTML and PDF delivered
|
||
|
||
## 11. Dark mode (required)
|
||
|
||
The app toggles dark mode by setting `data-theme="dark"` on `<html>` inside its preview. Print/PDF always stays light.
|
||
|
||
- Define every color through the `:root` variables only (`--ink`, `--muted`, `--line`, `--bg-soft`, accent trio).
|
||
- Ship an additional override block:
|
||
|
||
```css
|
||
@media screen {
|
||
html[data-theme="dark"] {
|
||
--ink: #e6e8ee; --muted: #9aa3b2; --line: #2c3038; --bg-soft: #23262e;
|
||
/* lift accent shades so headings/links stay readable on dark */
|
||
}
|
||
html[data-theme="dark"] body { background: #15171c; }
|
||
}
|
||
```
|
||
|
||
- Do NOT use `prefers-color-scheme` — the app controls the attribute.
|
||
- Do not hardcode callout/infobox backgrounds — or darken them explicitly in the dark block (e.g. to `var(--bg-soft)`); the colored border stays.
|
||
- Keep dark rules inside `@media screen` only, so WeasyPrint/PDF renders the light theme.
|
||
- Elements with light text on accent backgrounds (table headers, cover) may keep their light-theme background.
|