
Effective Print Design
- 269 installs
- Updated February 12, 2026
- sebastian-software/effective-print-design
Helps with design & ui/ux tasks.
About
effective-print-design is a Claude Code skill for design & ui/ux. It helps solo builders move faster with AI-assisted coding.
- effective-print-design
- Design & UI/UX
- AI-coding skill
Effective Print Design by the numbers
- 269 all-time installs (skills.sh)
- +31 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #852 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sebastian-software/effective-print-design --skill effective-print-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 269 |
|---|---|
| Last updated | February 12, 2026 |
| Repository | sebastian-software/effective-print-design ↗ |
What it does
Helps with design & ui/ux tasks.
Files
Effective Print Design
You are an expert in CSS print design, paged media, and web-to-print production. You think like an InDesign operator and bring that precision to HTML/CSS. You are fluent in typography principles (Bringhurst, Butterick, Tschichold) and the full vocabulary of traditional and digital typesetting. Your goal is HTML/CSS that achieves print-quality results rivaling dedicated DTP software.
Quick Decision Guide
| Task | Approach |
|---|---|
| Simple print stylesheet | @media print block in existing CSS |
| Screen preview matching print | Paper-simulation technique (see layout.md) |
| Resume / single-page document | Fixed-dimension <article> elements |
| Multi-page article or book | Multiple .page elements with page-break-after |
References
| Category | Reference |
|---|---|
| Typography | references/typography.md — font sizes, stacks, OpenType, text-wrap, hyphenation |
| Layout | references/layout.md — layers, reset, @page, simulation, grid, fragmentation |
| Page Features | references/page-features.md — headers/footers, counters, bleed, links, element styles |
| Locale | references/locale.md — quotation marks, dashes, numbers, spacing per locale, preprocessing tools |
Print vs. Screen
| Desktop | Mobile | ||
|---|---|---|---|
| Units | pt | rem / px | rem / px |
| Layout | Fixed page (A4 / Letter) | Fluid, max-width | Fluid, single-column |
| Colors | OKLCH; B&W first, CMYK gamut | OKLCH / RGB | OKLCH / RGB |
| Typography | 11pt, serif, justify, hyphens | 16px+, variable | 16px+, left-align |
| Line-height | 1.35–1.4 (tight) | 1.5–1.6 | 1.5–1.6 |
| Images | 300 DPI | 72–96 DPI | 72–96 DPI, lazy load |
| Backgrounds | Stripped by browser | Free | Free |
| Font-weight | Min 400 (thin vanishes) | Free | Free |
| Interaction | None — hide all UI | Full | Touch |
| Line length | Page margins control | max-width / container | Full width |
Architecture
- Use
@layerto separate print from screen — eliminates!importantwars - Start with a clean-slate reset: strip backgrounds, shadows, filters to
transparent/none - Blacklist approach: explicitly hide what doesn't belong (nav, sidebar, ads, buttons)
- Use
print-color-adjust: exactonly where backgrounds carry meaning - Generous whitespace — too much rarely hurts, too little is fatal (cramped layouts are the #1 amateur mistake)
Typography
- Use
ptunits in@media print(CSS pt = Word pt = 1/72 inch) - Default: 11pt body, line-height 1.35–1.4 (tighter than screen)
- Target 45–75 characters per line (ideal: 66)
text-wrap: prettyfor body (multi-line optimizer),balancefor headings- Justify only with line length ≥50 chars and
hyphens: auto— both are mandatory; usetext-align: leftfor narrow columns font-optical-sizing: autofor variable fonts — thickens strokes at 8pt, refines at 24ptfont-size-adjustto normalize x-height across fallback fonts in stacksfont-synthesis: none— prevent browser from generating faux bold/italic (ugly on paper)- OpenType:
oldstyle-numsfor body,lining-nums tabular-numsfor tables,lining-numsfor headings - Never track lowercase body; add
letter-spacing: 0.05–0.12emto CAPS/small-caps - Font-weight never below 400 (thin/light vanish on paper)
- Fewer heading levels than screen (3 suffice), modest ~1.2x scale (minor third)
- Max 2–3 typefaces; pair by matching x-heights and historical period
- Books: use
text-indent: 1emonp + p(notmargin-bottom) for paragraph separation - Choose body faces with low-to-medium stroke contrast and open apertures — high contrast (Bodoni) for display only
- Avoid Times New Roman; prefer Georgia, Charter, Palatino
- Quotation marks, dashes, number formatting, and spacing rules vary by locale — set
quotesperlangor usequotes: auto; use a typographic preprocessor (SmartyPants, richtypo.js) for automated character substitution at build time
Colors
- Design for black & white first — most users print monochrome
- OKLCH works for print — browser converts to sRGB/PDF; L-channel maps directly to perceived gray value
- Browsers strip backgrounds by default; restore selectively with
print-color-adjust: exact - Keep chroma low for print — high-chroma OKLCH values may fall outside CMYK gamut
- Replace box-shadows with borders; replace colored backgrounds with border patterns
- Use pure black (
#000) for body text — prints as 100% K (black ink only). Avoid "rich black" (CMYK 60/40/40/100) on text — causes registration issues and blurring at small sizes
Fragmentation
break-after: avoidon headings (keep with following content)break-inside: avoidon figures, tables, pre, blockquotes, cardsorphans: 3; widows: 3on paragraphsbreak-before: page(orrightfor books) on major sections
Images
- Print = 300 DPI (screen = 96); for 2-inch print width → 600 px source
break-inside: avoidon figures; hide decorative images- CSS background images don't print by default (good for decorative)
Links
- Never dump raw URLs inline — clutter the layout, nobody types them
- Use numbered footnotes (CSS counters) or a single QR code, or both
- Expand abbreviations on paper (see page-features.md)
Tables
thead { display: table-header-group; }— repeats headers on every page- Minimize borders (one direction only); don't force
width: 100%— size columns to data font-variant-numeric: lining-nums tabular-nums,line-height: 1in cellstr { break-inside: avoid; }
Code Blocks
- Override dark syntax themes to light-on-white (dark backgrounds waste ink)
white-space: pre-wrap; break-inside: avoid
Testing
1. Chrome DevTools: Cmd+Shift+P > "Emulate CSS print media type" 2. Print Preview: Cmd+P (shows actual pagination) 3. Playwright: page.emulateMedia({ media: 'print' }) + page.pdf() for automated PDF generation and visual regression tests 4. Actual printers — laser and inkjet render differently
Sources
Bringhurst (Elements of Typographic Style), Butterick's Practical Typography, Rutter (Web Typography), van Aaken (Webtypobuch), Stein (Webfont Handbook), Santa Maria (On Web Typography), Smashing Magazine, CSS-Tricks, A List Apart, Adrian Roselli, MDN, Piccalilli, Pimp my Type, Gutenberg CSS.
MIT License
Copyright (c) 2026 Sebastian Software GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Effective Print Design
Print CSS that doesn't look like print CSS.
A Claude Code skill that turns Claude into an InDesign operator — fluent in Bringhurst, Butterick, and Tschichold, building with pure HTML/CSS.
The Problem
Most printed web pages look terrible. Chopped headings, orphaned lines, invisible backgrounds, raw URLs nobody will type, rivers of whitespace in justified text. Print is an afterthought — and it shows.
What This Skill Does
Gives Claude deep knowledge of print typography, paged media, and web-to-print production. Ask for a resume, invoice, article, book, or report and get CSS that holds up at 300 DPI.
Typography — proper pt sizing, optical sizing, OpenType features, hyphenation, justified text done right, locale-correct quotation marks for 12 Western languages.
Layout — @page rules, page simulation on screen, baseline grids, fragmentation control, sidebar + main grid patterns.
Page Features — running headers/footers, CSS counters, crop marks, footnote-style links, table header repetition, drop caps.
Install
Via skill.sh:
npx skill.sh install sebastian-software/effective-print-designOr directly via Claude Code:
claude skill add effective-print-designActivates automatically when you mention @media print, @page, print stylesheets, or ask for a document that needs to look good on paper.
What's Inside
| File | What it covers |
|---|---|
SKILL.md | Core principles, quick decision guide, print vs. screen |
references/typography.md | Font stacks, sizing, OpenType, text-wrap, hyphenation, hierarchy |
references/layout.md | @page, page simulation, grids, fragmentation, baseline rhythm |
references/page-features.md | Headers/footers, counters, bleed, links, tables, code blocks |
references/locale.md | Quotes, dashes, number formatting, spacing for 12 locales |
Built On
Bringhurst's Elements of Typographic Style, Butterick's Practical Typography, Rutter's Web Typography, van Aaken's Webtypobuch, Stein's Webfont Handbook, Santa Maria's On Web Typography — plus MDN, Smashing Magazine, A List Apart, and years of collective print CSS pain.
License
MIT — Copyright (c) 2026 Sebastian Software GmbH
Layout Reference
Detailed CSS for print layout and page structure. See SKILL.md for principles.
CSS Layer Strategy
Use @layer to cleanly separate print overrides from screen styles. Layer order trumps specificity — eliminates !important wars.
@layer base, components, print;
/* Or import a dedicated print stylesheet into its layer */
@import url("print.css") layer(print) print;All print styles go inside @media print { @layer print { ... } }.
Print Reset
Start every print stylesheet with a clean-slate reset.
@media print {
@layer print {
*,*::before,*::after {
color: #000;
background: transparent;
box-shadow: none;
text-shadow: none;
filter: none;
-webkit-filter: none;
}
body {
background: #fff;
margin: 0;
padding: 0;
}
}
}Then selectively restore backgrounds with print-color-adjust: exact. If not using @layer, use !important on reset properties.
Page Setup
@page {
size: A4 portrait; /* or: letter, A3, legal, 210mm 297mm */
margin: 20mm 25mm; /* top/bottom left/right */
}
@page :first {
margin-top: 35mm; /* extra space for title page */
}
/* Book-style asymmetric margins (gutter for binding) */
@page :left { margin-left: 20mm; margin-right: 30mm; }
@page :right { margin-left: 30mm; margin-right: 20mm; }Paper sizes: A4 = 210 x 297 mm, Letter = 8.5 x 11 in, A3 = 297 x 420 mm, Legal = 8.5 x 14 in.
Margins: Office documents 20–25 mm. Minimum safe 5 mm (printer hardware limit). Book interiors 20 mm outer, 30 mm gutter.
Page Simulation on Screen
Show exact page boundaries on screen so the design matches print 1:1.
@media screen {
body { background: #e0e0e0; padding: 10mm 0; }
.page {
background: white;
width: 210mm; min-height: 297mm;
margin: 10mm auto;
padding: 20mm 25mm;
box-sizing: border-box;
box-shadow: 0 0 5mm rgba(0,0,0,.2);
}
}
@media print {
@page { size: A4; margin: 20mm 25mm; }
body { background: white; margin: 0; padding: 0; }
.page {
width: auto; min-height: auto; margin: 0; padding: 0;
box-shadow: none;
page-break-after: always;
}
.page:last-child { page-break-after: auto; }
}Hide / Show Elements
Blacklist approach — explicitly hide what doesn't belong on paper.
@media print {
nav, .sidebar, .comments, .ads, .cookie-banner,
.social-share, footer nav, video, audio, button,
.no-print { display: none !important; }
}
.print-only { display: none; }
@media print { .print-only { display: block; } }Fragmentation
@media print {
h1, h2, h3, h4, h5, h6 { break-after: avoid; }
figure, table, pre, blockquote, img,
.card, .entry, .job-entry { break-inside: avoid; }
p { orphans: 3; widows: 3; }
.chapter { break-before: page; }
.chapter-start { break-before: right; } /* book-style: right page */
}Baseline Grid / Vertical Rhythm
All vertical spacing should be multiples of the base line-height. This creates the "invisible grid" that makes a page cohesive — exactly like InDesign's baseline grid. More achievable in print than on screen because page dimensions are fixed.
The technique: Pick a base unit = body line-height. All margins, paddings, and line-heights for every element must be exact multiples.
@media print {
:root { --rhythm: 15.4pt; } /* 11pt * 1.4 line-height */
body { font-size: 11pt; line-height: var(--rhythm); }
p { margin: 0 0 var(--rhythm); }
h2 { font-size: 16pt; line-height: calc(2 * var(--rhythm));
margin-top: calc(2 * var(--rhythm)); margin-bottom: var(--rhythm); }
h3 { font-size: 13pt; line-height: var(--rhythm);
margin-top: calc(2 * var(--rhythm)); margin-bottom: 0; }
figure { margin: var(--rhythm) 0; }
blockquote { margin: var(--rhythm) 0; padding: 0 var(--rhythm); }
}Headings with larger font-sizes get line-height set to 2x or 3x the rhythm unit. This ensures body text baselines across adjacent columns stay aligned.
Duplex printing: When printing on both sides of translucent paper (especially ≤80 gsm), baseline-aligned text on recto and verso prevents show-through distortion. Without alignment, faint reversed text bleeds through and degrades readability.
Don't be slavish: Images, figures, and embedded media may have natural dimensions that break the rhythm. Let them — resume the grid beneath. The baseline grid governs text flow, not every element on the page.
Resume: Sidebar + Main Grid
.resume {
display: grid;
grid-template-columns: 1fr 2fr;
width: 210mm;
min-height: 297mm;
padding: 15mm 20mm;
box-sizing: border-box;
}
@media screen {
body { background: #e0e0e0; display: flex; justify-content: center; padding: 2rem 0; }
.resume { background: white; box-shadow: 0 4px 20px rgba(0,0,0,.15); }
}
@media print {
body { background: none; margin: 0; padding: 0; }
.resume { box-shadow: none; max-width: none; }
}Sidebar Patterns
/* Border separator */
.sidebar { border-right: 0.5pt solid #ccc; padding-right: 15mm; }
/* Or with background */
.sidebar {
background: #f5f5f5;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
padding: 15mm;
}Grid for Structured Sections
CSS Grid is reliable in print. Use it for structured sections (bio, metadata, figure+text).
@media print {
.two-col-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15pt;
break-inside: avoid;
}
.figure-text {
display: grid;
grid-template-columns: 2fr 3fr;
gap: 12pt;
break-inside: avoid;
align-items: start;
}
}Avoid CSS `column-count` for flowing text — page breaks inside columns are fragile. Use Grid with break-inside: avoid instead.
Linearize Screen Layouts
Multi-column layouts from screen should collapse to single-column for print.
@media print {
.grid-layout { display: block; }
.flex-layout { flex-direction: column; }
main { width: 100%; margin: 0; padding: 0; }
}Chapter Starts (Books/Reports)
.chapter { break-before: right; }
.chapter h1 {
margin-top: 30%;
text-align: center;
font-size: 28pt;
line-height: 1.1;
text-wrap: balance;
break-after: avoid;
}Responsive Fallback for Mobile Viewing
@media screen and (max-width: 65rem) {
.resume, .page {
grid-template-columns: 1fr;
width: auto;
min-height: auto;
padding: 2rem;
}
}Locale Conventions Reference
Locale-specific typography rules for Western languages. See SKILL.md for principles.
Quotation Marks
Set correct quotation marks per language using the CSS quotes property:
html[lang="de"] { quotes: "„" "\201C" "‚" "\2018"; }
html[lang="en"] { quotes: "\201C" "\201D" "\2018" "\2019"; }
q { quotes: auto; } /* let browser pick from lang attribute */| Locale | Primary | Unicode | Secondary | Unicode |
|---|---|---|---|---|
| en-US | "…" | U+201C / U+201D | '…' | U+2018 / U+2019 |
| en-GB | '…' | U+2018 / U+2019 | "…" | U+201C / U+201D |
| de-DE/AT | „…" | U+201E / U+201C | ‚…' | U+201A / U+2018 |
| de-CH | «…» | U+00AB / U+00BB | ‹…› | U+2039 / U+203A |
| fr-FR | « … » | U+00AB nbsp … nbsp U+00BB | "…" | U+201C / U+201D |
| es-ES | «…» | U+00AB / U+00BB | "…" | U+201C / U+201D |
| it-IT | «…» or "…" | varies | '…' | U+2018 / U+2019 |
| pt-PT | «…» | U+00AB / U+00BB | "…" | U+201C / U+201D |
| pt-BR | "…" | U+201C / U+201D | '…' | U+2018 / U+2019 |
| nl-NL | '…' (modern) | U+2018 / U+2019 | "…" | U+201C / U+201D |
| nl-BE | «…» | U+00AB / U+00BB | "…" | U+201C / U+201D |
| pl-PL | „…" | U+201E / U+201D | «…» | U+00AB / U+00BB |
German guillemets point inward (»text«). French guillemets require nbsp inside (« text »). Swiss guillemets follow French direction (outward) but without internal spacing.
Thin space ( , U+2009) between nested quotation marks to prevent visual merging: "She said 'hello' " — insert thin space between ' and ".
Dashes
| Locale | Parenthetical | Spacing | Range |
|---|---|---|---|
| en-US | Em dash — (U+2014) | No spaces | En dash, no spaces |
| en-GB | En dash – (U+2013) | Spaced: word – word | En dash, no spaces |
| de-DE/AT/CH | En dash – (U+2013) | Spaced: Wort – Wort | En dash, no spaces |
| fr-FR | En dash – (U+2013) | Spaced: mot – mot | En dash, no spaces |
| es-ES | Em dash — (U+2014) | Space before, not inside | En dash, no spaces |
| it-IT | Em or En dash | Spaced | En dash, no spaces |
| pt-PT/BR | Em dash — (U+2014) | Spaced | En dash, no spaces |
| nl-NL | En or Em dash | Spaced | En dash, no spaces |
| pl-PL | Em dash — (U+2014) | Spaced | En dash, no spaces |
Number Formatting
| Locale | Decimal | Thousands | Example | Currency position |
|---|---|---|---|---|
| en-US/GB | . | , | 1,234.56 | Before, no space: $1,234.56 |
| de-DE/AT | , | . | 1.234,56 | After, space: 1.234,56 € |
| de-CH | . | ' | 1'234.56 | After, space: 1'234.56 CHF |
| fr-FR | , | (nnbsp) | 1 234,56 | After, nnbsp: 1 234,56 € |
| es-ES | , | . | 1.234,56 | After, space: 1.234,56 € |
| it-IT | , | . | 1.234,56 | After, space: 1.234,56 € |
| pt-PT | , | . | 1.234,56 | After, space: 1.234,56 € |
| pt-BR | , | . | 1.234,56 | Before, no space: R$1.234,56 |
| nl-NL | , | . | 1.234,56 | Before, space: € 1.234,56 |
| pl-PL | , | (nbsp) | 1 234,56 | After, space: 1 234,56 zł |
Unit spacing: Non-breaking space between number and unit: 10 kg, 100 %, 20 °C (ISO 80000-1). Use narrow no-break space (U+202F) for tighter fit.
French Punctuation Spacing
French is unique among Western languages — mandatory non-breaking space before high punctuation:
| Mark | Space before | Type |
|---|---|---|
: (colon) | Yes | No-break space (U+00A0) |
; ? ! | Yes | Narrow no-break space (U+202F) |
« (opening) | Space after | No-break space (U+00A0) |
» (closing) | Space before | No-break space (U+00A0) |
The colon takes a full nbsp; semicolon, question mark, and exclamation take a narrow nbsp (Imprimerie nationale rules). Handle via preprocessor or lang="fr" specific rules.
Typographic Preprocessing
Automate character substitution at build time instead of manual Unicode input:
| Tool | Key features |
|---|---|
| SmartyPants / smartypants.js | Curly quotes, en/em dashes, ellipsis (English-centric) |
| richtypo.js | Locale-aware — per-language rule packages, widow prevention |
| Tipograph | Locale-aware — configurable presets per locale |
| Typeset | Hanging punctuation, optical alignment, ligatures, soft hyphens |
| typogr.js | Wraps elements in <span> for CSS styling (ampersands, caps, widows) |
Common transformations: " → "…", ' → '…', -- → –, --- → —, ... → …, widow prevention (nbsp between last two words). Locale-aware tools handle quote direction, dash style, and spacing rules per language automatically.
Page Features Reference
Print-specific page features and element styles. See SKILL.md for principles.
Page Headers, Footers, and Page Numbers
Chrome 131+.
@page {
margin: 20mm;
@top-center {
content: "Document Title";
font-size: 9pt; color: #666;
}
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
font-size: 8pt;
}
}
@page :first {
@top-center { content: none; }
@bottom-right { content: none; }
}Book-Style Running Headers
@page :right {
@top-right { content: "Document Title"; font-size: 9pt; font-style: italic; }
@bottom-right { content: counter(page); font-size: 9pt; }
}
@page :left {
@top-left { content: "Book Title"; font-size: 9pt; font-style: italic; }
@bottom-left { content: counter(page); font-size: 9pt; }
}
@page :first {
@top-left { content: none; } @top-right { content: none; }
@bottom-left { content: none; } @bottom-right { content: none; }
}
@page :blank {
@top-center { content: none; }
@bottom-center { content: "This page intentionally left blank"; font-size: 9pt; color: #999; }
}Named Pages
Different sections can have different page configurations.
.title-page { page: title; }
.chapter { page: chapter; }
.appendix { page: appendix; }
@page title {
margin: 0;
@top-center { content: none; }
@bottom-center { content: none; }
}
@page chapter {
@bottom-center { content: counter(page); }
}
@page appendix {
@top-center { content: "Appendix"; font-size: 9pt; }
@bottom-center { content: "A-" counter(page); font-size: 9pt; }
}CSS Counters
body { counter-reset: chapternum figurenum tablenum; }
h1.chapter::before {
counter-increment: chapternum;
content: "Chapter " counter(chapternum) " — ";
}
figcaption::before {
counter-increment: figurenum;
content: "Figure " counter(chapternum) "." counter(figurenum) ". ";
}
.table-caption::before {
counter-increment: tablenum;
content: "Table " counter(chapternum) "." counter(tablenum) ". ";
}Bleed and Crop Marks (Professional Printing)
@page {
bleed: 3mm;
marks: crop cross;
}
.full-bleed {
width: calc(100% + 6mm);
margin-left: -3mm;
margin-right: -3mm;
}Links on Paper
Never dump raw URLs inline. Better approaches:
1. Numbered footnotes — superscript numbers, collected URL list at bottom. Use CSS counters. In React, render conditionally for print. 2. QR code — single QR code linking to the original page URL, shown via .print-only. 3. Combination — footnote numbers for important links + one QR code for the full page.
Footnote Styling
@media print {
/* Sub/superscripts: see OpenType section in typography.md */
sup.fnref {
font-size: 75%;
line-height: 0;
vertical-align: super;
padding-left: 0.1em;
}
.footnotes {
margin-top: 2em;
border-top: 0.5pt solid #000;
padding-top: 1em;
font-size: 8pt;
break-inside: avoid;
}
.footnotes li { word-break: break-all; margin-bottom: 0.2em; }
}Abbreviations
@media print {
abbr[title]::after { content: " (" attr(title) ")"; }
}Images
@media print {
img {
max-width: 100% !important;
height: auto !important;
break-inside: avoid;
}
figure { break-inside: avoid; margin: 8pt 0; }
figcaption { font-size: 9pt; font-style: italic; margin-top: 4pt; }
/* Hide decorative images */
img[role="presentation"], img[alt=""] { display: none; }
}Use <picture> with media="print" source for high-res print variants.
Tables
Design principles: Size columns to their data, not the page width. Minimize borders — use rules in one direction only when needed. Group related rows with whitespace (Gestalt law of proximity), not zebra stripes.
@media print {
table { border-collapse: collapse; break-inside: auto; }
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr { break-inside: avoid; }
caption { caption-side: bottom; font-size: 9pt; font-style: italic; margin-top: 4pt; }
td, th {
border-bottom: 0.5pt solid #ccc;
padding: 0.125em 0.5em 0.25em 0.5em; /* asymmetric: less top, more bottom */
font-size: 10pt;
line-height: 1; /* tight in cells — columns are narrow */
font-variant-numeric: lining-nums tabular-nums;
}
thead th {
border-bottom: 1pt solid #000;
font-weight: 700;
}
}Decimal Alignment (CSS Text Level 4)
td.numeric { text-align: "." center; } /* align on decimal point */Not yet widely supported in browsers — works in Prince/WeasyPrint. Progressive enhancement.
Oblique Column Headings
When headings are longer than data, rotate to save horizontal space:
th.rotated {
transform: rotate(-60deg);
transform-origin: bottom left;
white-space: nowrap;
}Code Blocks
@media print {
pre {
white-space: pre-wrap;
word-wrap: break-word;
overflow: visible;
break-inside: avoid;
background: #f8f8f8 !important;
color: #333 !important;
border: 1px solid #ccc;
padding: 8pt;
font-size: 9pt;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
:not(pre) > code {
background: #f0f0f0 !important;
color: #333 !important;
padding: 1pt 3pt;
font-size: 90%;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}Blockquotes
@media print {
blockquote {
border-left: 3pt solid #333;
padding-left: 12pt;
margin-left: 0;
font-style: italic;
break-inside: avoid;
}
}Section Breaks / Fleurons
For scene or section breaks within chapters, style <hr> as a decorative separator:
@media print {
hr {
border: none;
text-align: center;
margin: 1.5em 0;
}
hr::after {
content: "* * *"; /* or a fleuron: "❧" or "§" */
letter-spacing: 0.5em;
font-size: 12pt;
}
}Print Source Notice
.print-source-notice { display: none; }
@media print {
.print-source-notice {
display: block;
margin-top: 2em;
padding-top: 1em;
border-top: 0.5pt solid #ccc;
font-size: 8pt;
color: #666 !important;
}
}Typography Reference
Detailed CSS for print typography. See SKILL.md for principles.
Font Sizes
CSS pt and Word pt are identical: 1 pt = 1/72 inch. Different fonts at the same pt size appear visually different — always verify; adjust by half-points (10.5pt, 11.5pt) if needed.
| Element | Size | Line-height | Notes |
|---|---|---|---|
| Body text | 11pt | 1.35–1.4 | Serif or sans — depends on CI |
| h1 | 22–24pt | 1.05–1.1 | Below 1.0 OK for all-caps display |
| h2 | 16–18pt | 1.1–1.15 | |
| h3 | 13–14pt | 1.15–1.2 | |
| h4 | 11pt bold | 1.2–1.3 | Same size as body, bold |
| Sidenotes / marginalia | 8pt | 1.2–1.25 | |
| Captions / footnotes | 8–9pt | 1.2–1.25 | Never below 75% of body size |
| Code blocks | 9pt | 1.3–1.4 | Monospace |
Heading line-height below 1.0 is acceptable for large display headings (24pt+). Touching ascenders and descenders creates visual cohesion. For all-caps headings there are no descenders, so line-height can go even tighter.
Line-height validation: Look at a capital H, mentally double its height. If the doubled letter reaches the baseline of the line above, line-height is too small for body text (van Aaken's "double cap-height" test).
Typographic color: Heavier/darker typefaces need more line-height; lighter ones need less. The fixed 1.35–1.4 range is a starting point — adjust by squinting at the text block and evaluating its overall gray value.
Low x-height as space saver: Fonts with small x-heights optically increase line spacing without changing the value. For tight page budgets (resumes, dense reports), deliberately choose a low-x-height font.
Functional text (captions, labels, footnotes) should never be smaller than 75% of body size. Choose faces with open, distinct letterforms where I/l/1 and O/0 are clearly distinguishable.
Document-Type Variations
| Property | Resume / CV | Article / Blog | Book / Report |
|---|---|---|---|
| Body size | 9.5–10.5 pt | 11 pt | 11–12 pt |
| Line-height | 1.3–1.4 | 1.35 | 1.4–1.5 |
| Body font | Serif or Sans (CI) | Serif preferred | Serif preferred |
| Heading font | Contrasting pair | Sans-serif | Sans-serif or matching serif |
| Text-align | Left (not justified) | Justify | Justify |
| Hyphens | No | Yes | Yes |
Modular Scale Ratios
Use named musical intervals as type scale ratios for intentional hierarchy:
| Ratio | Name | Use case |
|---|---|---|
| 1.125 | Major second | Compact/dense (resumes, data-heavy) |
| 1.200 | Minor third | General print (default recommendation) |
| 1.250 | Major third | Slightly more dramatic |
| 1.333 | Perfect fourth | Editorial, magazine layouts |
| 1.414 | Augmented fourth | Academic papers, formal publications |
For print, minor third (1.2) or major second (1.125) work best — print needs less size contrast than screen.
Double-stranded scale: For more nuanced sizing, use two meaningful values — e.g., body size (11pt) and column width (in pt) — with your chosen ratio to generate sizes with built-in relationships to the page geometry (Tim Brown's technique).
Font Choice
Both serif and sans-serif work well in print. The choice depends on brand identity. Classic pairings:
- Serif body + sans-serif headings — traditional, bookish
- Sans-serif body + serif headings — modern, clean
- All-serif — academic, literary
- All-sans — corporate, technical
Selection Criteria for Body Text
- Stroke contrast: Low-to-medium contrast creates smooth reading rhythm. High contrast (Bodoni, Didot) produces uneven texture — reserve for display/headlines only.
- Aperture openness: Open counters and apertures (the openings in c, e, s, a) aid legibility, especially at small sizes (8–9pt). Closed apertures (Helvetica) make letterforms hard to distinguish.
- Typographic color: Aim for a medium gray value when squinting at a text block. Too dark (heavy strokes) or too light (thin strokes) both reduce readability.
- Personality restraint: Body type must be restrained — any visual quirk (whimsical tail on g, unusual ligatures) repeats thousands of times and becomes a distraction. Save personality for display type.
Headlines
- Condensed or slightly narrow typefaces fit more characters per line on fixed-width print pages, preventing single-word orphan lines and awkward breaks that
text-wrap: balancecannot always solve. - Condensed widths also create strong visual contrast with regular-width body text.
Font Pairing Rules
- Match x-heights between paired typefaces — if x-heights differ, fonts look mismatched even at correct point sizes
- Match historical period — pair humanist serif with humanist sans (e.g., Palatino + Gill Sans), not with geometric sans (Futura)
- Match structural skeleton — typefaces sharing the same geometry (dynamic/rational/geometric) pair well regardless of serif/sans classification (Rutter's skeleton/flesh/skin model)
- Contrast on one axis only — vary serif/sans or weight or width, not all three
- Max 2–3 typefaces per document — each additional font dilutes hierarchy
- Superfamilies are the safest pairing: Source Sans + Source Serif, Noto Sans + Noto Serif, Lucida Sans + Lucida Serif
- Same designer — typefaces from the same designer often share subtle stylistic thumbprints (e.g., Eric Gill's Joanna + Gill Sans)
- Same foundry — some foundries explicitly design complementary families and list recommended pairings
- Never pair two similar fonts — two different serifs next to each other creates tension without clear hierarchy
Font Safety
Never declare font-weight: bold or font-style: italic unless the loaded font actually includes that weight/style variant. Browsers synthesize faux bold (by geometrically thickening strokes) or faux italic (by slanting) when the real variant is missing — the result looks crude, especially at 300 DPI on paper.
/* Prevent browser from generating faux bold/italic */
body { font-synthesis: none; }Verify in DevTools that every font-weight/font-style request is matched by an actual font file.
Font Stacks (System Fonts)
@media print {
/* Serif */
body {
font-family: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif;
font-size: 11pt;
line-height: 1.35;
font-size-adjust: 0.465; /* normalize x-height across fallbacks */
font-optical-sizing: auto; /* enable optical size axis on variable fonts */
font-synthesis: none; /* prevent faux bold/italic */
}
/* Sans-serif (alternative) */
body {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', Arial, sans-serif;
font-size: 11pt;
line-height: 1.4;
}
code, pre {
font-family: 'Courier New', Courier, monospace;
font-size: 9pt;
}
}Selective Glyph Substitution
Use unicode-range to mix decorative glyphs from a different typeface — a classic typographic technique (e.g., a distinctive ampersand):
@font-face {
font-family: DecorativeAmpersand;
src: url(spumante.woff2) format("woff2");
unicode-range: U+26; /* only the & character */
}
h1, h2 {
font-family: DecorativeAmpersand, Charter, serif;
}The browser uses the decorative font only for matching characters; everything else falls through to the next font in the stack.
Optical Sizing
Variable fonts with an opsz axis automatically adapt stroke weight and detail to the rendered size. German typography distinguishes three size categories:
- Konsultationsgrößen (6–8pt) — captions, footnotes: thicker strokes, open counters
- Lesegrößen (9–13pt) — body text: balanced for sustained reading
- Schaugrößen (14pt+) — headings, display: refined details, tighter letterfitting
body { font-optical-sizing: auto; } /* default — enable for all sizes */
/* Manual control for variable fonts */
h1 { font-variation-settings: 'opsz' 48; }
caption { font-variation-settings: 'opsz' 8; }
/* Custom axes (beyond standard wght/wdth/opsz/ital/slnt) */
.display { font-variation-settings: 'opsz' 48, 'XHGT' 1.0, 'CONT' 50; }Fonts with good optical sizing: Roboto Flex, Source Serif 4, Fraunces, Recursive.
font-size-adjust
Normalizes x-height across fallback fonts. If Charter is missing and Georgia loads, the browser adjusts Georgia's size to match Charter's x-height ratio. Now in Baseline (Chrome 127+, Firefox 3+, Safari 17.4+).
body {
font-family: Charter, Cambria, Georgia, serif;
font-size-adjust: 0.465; /* Charter's x-height / font-size ratio */
}Even 0.5pt differences in apparent size matter on paper — font-size-adjust prevents this.
Text Wrapping & Hyphenation
@media print {
/* Headings: balanced lines (prevents single-word last line) */
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
hyphens: none;
}
/* Body: pretty wrapping (multi-line optimizer like InDesign's paragraph composer) */
p, li, dd, blockquote {
text-wrap: pretty;
text-align: justify;
hyphens: auto;
-webkit-hyphens: auto;
hyphenate-limit-chars: 6 3 2; /* word min 6, 3 before / 2 after hyphen */
-webkit-hyphenate-limit-before: 3;
-webkit-hyphenate-limit-after: 2;
hyphenate-limit-lines: 2; /* max 2 consecutive hyphenated lines */
-webkit-hyphenate-limit-lines: 2;
hyphenate-limit-zone: 8%; /* allow slight rag to reduce hyphenation */
hyphenate-limit-last: always; /* prevent stub of hyphenated word as last line */
}
/* Captions: balanced like headings */
figcaption, caption, summary {
text-wrap: balance;
}
}`text-wrap: pretty` — Chrome 117+, Safari 19+. `text-wrap: balance` — Chrome 114+, Firefox 121+, Safari 17.5+. `hyphens: auto` requires lang attribute on <html>.
`<wbr>` (word break opportunity) — insert in HTML where long unbreakable strings (URLs, chemical names, identifiers) may break across lines without inserting a hyphen. Useful for print where long strings must fit within the measure.
`­` (soft hyphen, U+00AD) — suggests a break point where a hyphen will appear if the browser breaks there. Use for compound words, technical terms, or proper nouns that hyphens: auto gets wrong. The hyphen is invisible unless the break occurs. For large-scale automation, server-side hyphenation libraries (e.g., Hypher, Hyphenopoly) can insert soft hyphens at build time.
Justified Text: Mandatory Preconditions
Justify only when both conditions are met:
1. Line length ≥ 50 characters (ideally 60+) 2. Functioning hyphenation (hyphens: auto + lang attribute)
Without both, word-spacing rivers are unacceptable. For narrow columns (sidebars, captions), always use text-align: left.
Word Spacing for Justified Text
A small negative word-spacing reduces rivers in justified text, mimicking InDesign's "desired word spacing" (~85–90% of default).
p {
text-align: justify;
word-spacing: -0.05em; /* tighten default word gaps slightly */
}Paragraph Separation
Two conventions — choose based on document type:
Spacing (Web/Article Convention)
p { margin-bottom: 0.8em; }Default for articles, reports, resumes. Familiar from screen.
Indentation (Book/Literary Convention)
Traditional European/German book typography. Saves vertical space, better for long-form reading.
p { margin: 0; }
p + p { text-indent: 1em; } /* indent all paragraphs except the first */Rules:
- First paragraph after heading, figure, blockquote, or list: no indent (
p + phandles this automatically) - Indent size: 1em (Bringhurst standard). For optical precision, match the line-height (e.g., if line-height is 15.4pt, use
text-indent: 15.4pt) to create a "neat square of white space." - Never combine indentation with vertical spacing — use one or the other
Standfirst / Lead Paragraph
A short opening paragraph set in a larger or different style to introduce the article. Common in editorial/magazine design.
article > p:first-of-type {
font-size: 1.25em;
line-height: 1.3;
font-weight: 300; /* or a different typeface */
}Hanging Indent for Bibliographies
For reference lists, use a hanging indent so the first line protrudes while continuation lines are indented:
.bibliography li {
text-indent: -1.5em;
margin-left: 1.5em;
}OpenType Features
@media print {
body {
font-variant-ligatures: common-ligatures;
font-kerning: normal;
}
/* Old-style (lowercase) figures for body text */
p, li {
font-variant-numeric: oldstyle-nums proportional-nums;
}
/* Lining (uppercase) figures for tables and headings */
table {
font-variant-numeric: lining-nums tabular-nums;
}
h1, h2, h3 {
font-variant-numeric: lining-nums; /* old-style looks wrong next to capitals */
}
/* Proper subscripts and superscripts (font glyphs, not browser shrink/shift) */
sub { font-variant-position: sub; }
sup { font-variant-position: super; }
@supports (font-variant-position: sub) {
sub, sup { vertical-align: baseline; font-size: inherit; }
}
/* Small caps for abbreviations */
abbr {
font-variant-caps: all-small-caps;
letter-spacing: 0.05em;
}
/* Hanging punctuation (Safari only — progressive enhancement) */
article p {
hanging-punctuation: first allow-end last;
}
/* Cross-browser fallback: negative indent sized in ch units */
blockquote p { text-indent: -1ch; }
/* Drop cap */
article > p:first-of-type::first-letter {
initial-letter: 3;
-webkit-initial-letter: 3;
font-weight: bold;
margin-right: 0.05em;
}
/* Variants: sunken cap (extends above text) and raised cap */
/* initial-letter: 3 2; — spans 3 lines, baseline on line 2 (sunken) */
/* initial-letter: 3 1; — spans 3 lines from baseline of line 1 (raised) */
/* Alternative opening: first-line run-in with small caps */
/*
article > p:first-of-type::first-line {
font-variant-caps: small-caps;
}
*/
}Warning: The font shorthand property resets font-kerning to its initial value. If you use font:, re-declare font-kerning: normal afterward.
Stylistic Alternates
For fonts with OpenType alternates — stylistic sets, swash, contextual alternates:
@font-feature-values "My Font" {
@styleset { clean: 1; } /* map name to ss01 */
}
body {
font-variant-alternates: styleset(clean); /* use named set */
font-variant-alternates: contextual; /* context-sensitive alternates */
}
/* Or directly via font-feature-settings */
h1 { font-feature-settings: 'ss01' 1, 'swsh' 1; }Special Characters
- Hyphen
-only for compound words and hyphenation - En dash
–for ranges without spaces: 10–20, Jan.–Mar. Prevent orphaned dashes with before the dash - Narrow no-break space (U+202F,
 ) between numbers and units (38 cm, 128 kB/s, § 21), abbreviation parts (z. B., d. h.), and initials (D. H. Lawrence). Prefer U+202F (non-breaking) over U+2009 (breaking thin space) - Multiplication sign
×(U+00D7,×) for dimensions: 210 × 297 mm — never the letter x - Minus sign
−(U+2212,−) for subtraction — not a hyphen (shorter, sits higher) - Ellipsis
…(U+2026), not three periods - Apostrophe
'(U+2019), never a prime' - Brackets in italic text: Keep parentheses and brackets upright — italic brackets have an awkward unbalanced stance
Locale-specific conventions (quotation marks, dashes, number formatting, French spacing) and typographic preprocessing tools — see locale.md.
Letter-Spacing
- Body text:
normal(never track lowercase body) - ALL CAPS / small-caps:
letter-spacing: 0.05emto0.12em+font-variant-ligatures: no-common-ligatures(ligatures must be disabled when letter-spacing is applied — a ligature is a single glyph whose internal spacing doesn't change with tracking, causing uneven gaps) - Large display headings:
letter-spacing: -0.01em(tighten) - Big, bold, wide display:
letter-spacing: -0.03em(the bigger/bolder/wider the font, the tighter the tracking) - Centered tracked text: Apply
margin-rightequal to the negativeletter-spacingto compensate for the trailing space on the last character:
h1 {
text-align: center;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-right: -0.1em; /* eliminate trailing space for true centering */
}Hierarchy Principles
1. Use fewer heading levels in print than on screen (3 usually suffice) 2. Differentiate by weight, style, caps, or spacing — not just size 3. More space before headings than after to group them with following content 4. Gestalt law of proximity: A multi-line heading's internal line-height must always be tighter than its margin-top/margin-bottom — otherwise readers mis-group heading lines with surrounding content 5. Modest scale (~1.2x between levels); print needs less contrast than screen 6. Use `text-box-trim` (Chrome 133+, Safari 18.2+) for optically precise vertical spacing
Resume Typography
h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.01em; } /* Name: 18–22pt */
h2 { font-size: 1.2rem; text-transform: uppercase; letter-spacing: 0.08em; /* Sections: 11–12pt */
border-bottom: 0.5pt solid #000; }
h3 { font-size: 1rem; font-weight: 700; } /* Job titles: 10–11pt */
.dates { font-size: 0.9rem; color: #555; text-align: right; } /* 9–10pt */
.contact { font-size: 0.9rem; }rem Scaling Trick
Set everything in rem so one root change scales the entire document. If the layout overflows, reduce to 9.5pt — everything shrinks proportionally.
html { font-size: 10pt; }
h1 { font-size: 2rem; } /* 20pt */
h2 { font-size: 1.2rem; } /* 12pt */
h3 { font-size: 1rem; } /* 10pt */
p { font-size: 1rem; }
.entry { margin-bottom: 0.6rem; }ATS (Applicant Tracking) Optimization
For resumes parsed by automated systems:
- Use semantic HTML5 (
<section>,<article>,<header>) - Avoid images for text content
- Disable ligatures:
text-rendering: optimizeSpeed - Use standard heading hierarchy (h1 for name, h2 for sections, h3 for entries)