Files
singular-particular-space/skills/typography/css-templates.md
JL Kruger 5422131782 Initial commit — Singular Particular Space v1
Homepage (site/index.html): integration-v14 promoted, Writings section
integrated with 33 pieces clustered by type (stories/essays/miscellany),
Writings welcome lightbox, content frame at 98% opacity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:09:22 +02:00

7.2 KiB
Raw Blame History

CSS Typography Templates

Read this file when generating CSS for any web project, building a design system, or auditing existing stylesheets. These templates encode every layout and formatting rule from the skill.


Complete Baseline Template

Copy-paste starting point. Every property maps to a specific typographic rule.

/* =============================================
   TYPOGRAPHY BASELINE
   Rules from Practical Typography (Butterick)
   ============================================= */

*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: clamp(16px, 2.5vw, 20px);       /* 1525px range, fluid */
  -webkit-text-size-adjust: 100%;              /* prevent iOS resize */
}

body {
  font-family: /* your-font, */ Georgia, 'Times New Roman', serif;
  line-height: 1.38;                           /* 120145% sweet spot */
  color: #1a1a1a;
  background: #fefefe;
  text-rendering: optimizeLegibility;          /* enables kern + liga */
  font-feature-settings: "kern" 1, "liga" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ---- TEXT CONTAINER: LINE LENGTH CONTROL ---- */

main, article, .prose {
  max-width: min(65ch, 90vw);                  /* 4590 chars enforced */
  margin: 0 auto;
  padding: 0 clamp(1rem, 4vw, 2rem);
}

/* ---- PARAGRAPHS ---- */
/* Choose ONE: space-between OR first-line-indent. Never both. */

/* Option A: Space between (default for web) */
p { margin: 0 0 0.75em 0; }                   /* 50100% of font size */

/* Option B: First-line indent (uncomment to use instead)
p { margin: 0; }
p + p { text-indent: 1.5em; }
*/

/* ---- HEADINGS: SUBTLE, SPACED, BOLD ---- */

h1, h2, h3, h4 {
  line-height: 1.15;                           /* tighter than body */
  hyphens: none;                               /* never hyphenate headings */
  page-break-after: avoid;                     /* keep with next paragraph */
  font-weight: 700;                            /* bold, not italic */
}
h1 {
  font-size: 1.5em;                            /* smallest increment needed */
  margin: 2.5em 0 0.5em;                       /* space above > below */
}
h2 {
  font-size: 1.25em;
  margin: 2em 0 0.4em;
}
h3 {
  font-size: 1.1em;
  margin: 1.5em 0 0.3em;
}

/* ---- EMPHASIS ---- */

em { font-style: italic; }                     /* gentle emphasis (serif) */
strong { font-weight: 700; }                   /* strong emphasis */
/* NEVER: strong em, em strong, or u for emphasis */

/* ---- ALL CAPS: ALWAYS LETTERSPACED ---- */

.caps {
  text-transform: uppercase;
  letter-spacing: 0.06em;                      /* 512% range */
  font-feature-settings: "kern" 1;
}

/* ---- SMALL CAPS: REAL ONLY ---- */

.small-caps {
  font-variant-caps: small-caps;               /* requires font with smcp */
  letter-spacing: 0.05em;
  font-feature-settings: "smcp" 1, "kern" 1;
}

/* ---- BLOCK QUOTES ---- */

blockquote {
  margin: 1.5em 2em;                           /* indent 25em */
  font-size: 0.92em;                           /* slightly smaller */
  line-height: 1.3;
}

/* ---- TABLES: CLEAN, NOT CLUTTERED ---- */

table {
  border-collapse: collapse;
  width: 100%;
}
th, td {
  padding: 0.5em 1em;                          /* generous cell padding */
  text-align: left;
  vertical-align: top;
  border: none;                                /* remove cell borders */
}
thead th {
  border-bottom: 1.5px solid currentColor;     /* thin rule under header */
  font-weight: 600;
}

/* Numeric data tables: tabular lining figures */
.data-table td {
  font-feature-settings: "tnum" 1, "lnum" 1;
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- LISTS ---- */

ul, ol {
  padding-left: 1.5em;
  margin: 0 0 1em;
}
li { margin-bottom: 0.3em; }

/* ---- HORIZONTAL RULES ---- */

hr {
  border: none;
  border-top: 1px solid currentColor;          /* 0.51pt, no patterns */
  opacity: 0.3;
  margin: 2em 0;
}

/* ---- LINKS ---- */

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;              /* subtle, not heavy */
  text-underline-offset: 2px;
}
a:hover { opacity: 0.8; }

/* ---- CODE ---- */

code {
  font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
  font-size: 0.88em;
  padding: 0.1em 0.3em;
  border-radius: 3px;
  background: rgba(0,0,0,0.04);
}
pre code {
  display: block;
  padding: 1em;
  overflow-x: auto;
  line-height: 1.5;
}

/* ---- RESPONSIVE ---- */

@media (max-width: 600px) {
  blockquote { margin: 1em 1em; }
  table { font-size: 0.9em; }
  th, td { padding: 0.4em 0.6em; }
}

/* ---- PRINT ---- */

@media print {
  body { font-size: 11pt; line-height: 1.3; }
  main { max-width: none; }
  h1, h2, h3 { page-break-after: avoid; }
  p { orphans: 2; widows: 2; }
}

Responsive Typography Patterns

Fluid Typography with Clamp

body {
  font-size: clamp(16px, 2.5vw, 20px);
}
main {
  max-width: min(65ch, 90vw);
  margin: 0 auto;
  padding: 0 clamp(1rem, 4vw, 2rem);
}
h1 { font-size: clamp(1.5rem, 4vw, 2.5rem); }
h2 { font-size: clamp(1.25rem, 3vw, 1.75rem); }

Breakpoint-Based Alternative

body { font-size: 16px; }
@media (min-width: 600px)  { body { font-size: 17px; } }
@media (min-width: 900px)  { body { font-size: 18px; } }
@media (min-width: 1200px) { body { font-size: 19px; } }

Key Principles

  1. Scale font-size and container width together
  2. Always max-width on text containers — never edge-to-edge
  3. Don't use ch unit for exact line length (only measures zero width)
  4. Use vw units with clamp() for bounds
  5. Mobile minimum: padding: 0 1rem on text containers

OpenType Features Reference

/* Body text */
.body {
  font-feature-settings:
    "kern" 1,       /* kerning pairs — always on */
    "liga" 1,       /* standard ligatures */
    "calt" 1;       /* contextual alternates */
}

/* Body text with oldstyle figures */
.prose {
  font-feature-settings:
    "kern" 1, "liga" 1, "calt" 1,
    "onum" 1;       /* oldstyle (lowercase-height) numbers */
}

/* Data tables */
.data-table td {
  font-feature-settings:
    "kern" 1,
    "tnum" 1,       /* tabular (fixed-width) numbers */
    "lnum" 1;       /* lining (capital-height) numbers */
}

/* Small caps */
.small-caps {
  font-feature-settings:
    "kern" 1,
    "smcp" 1;       /* real small caps */
  letter-spacing: 0.05em;
}

/* All caps with capital spacing */
.all-caps {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-feature-settings:
    "kern" 1,
    "cpsp" 1;       /* capital spacing (if font supports) */
}

Dark Mode Considerations

@media (prefers-color-scheme: dark) {
  body {
    color: #e0e0e0;
    background: #1a1a1a;
    font-weight: 350;           /* slightly lighter — dark bg makes text appear heavier */
    -webkit-font-smoothing: auto; /* let system decide in dark mode */
  }
}

React/JSX Inline Pattern

When generating React components with text, apply entities directly:

// WRONG
<h2>IMPORTANT NOTICE</h2>
<p>"Hello," she said. "It's a beautiful day..."</p>
<p>Price: $12 x 4 = $48</p>
<p>Pages 1-10</p>

// RIGHT
<h2 style={{ letterSpacing: '0.05em' }}>IMPORTANT NOTICE</h2>
<p>&ldquo;Hello,&rdquo; she said. &ldquo;It&rsquo;s a beautiful day&hellip;&rdquo;</p>
<p>Price: $12 &times; 4 = $48</p>
<p>Pages 1&ndash;10</p>