Best for
- Use when defining type scales, setting up design tokens, reviewing font size choices, or when typography feels inconsistent or arbitrary.
dembrandt/dembrandt-skills/skills/modular-scale-typography/SKILL.md
Typography feels cohesive and intentional when font sizes follow a modular scale — a ratio-based sequence where every size is mathematically related to the others. Use when defining type scales, setting up design tokens, reviewing font size choices, or when typography feels inconsistent or arbitrary.
Decision brief
Typography feels cohesive when all font sizes are related to each other through a single mathematical ratio. Without a scale, sizes get picked arbitrarily and the result feels visually noisy — headings that don't contrast enough, body text too close in size to captions, labels t…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/dembrandt/dembrandt-skills --skill "skills/modular-scale-typography"Inspect the Agent Skill "modular-scale-typography" from https://github.com/dembrandt/dembrandt-skills/blob/643a0b75f2bacc2d77dd6bae07601ab40128bb12/skills/modular-scale-typography/SKILL.md at commit 643a0b75f2bacc2d77dd6bae07601ab40128bb12. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
[ ] Are all font sizes derived from a single base + ratio?
A modular scale starts from a base size and multiplies or divides by a ratio to generate every size in the system.
Default recommendation: 1.25 (Major Third) — enough contrast between steps to feel intentional without being theatrical.
Recover an existing scale, don't reverse-engineer it by hand (dembrandt engine, optional). If a brand already has type on the web, gettypography returns the real font sizes, weights, and line-heights computed off the live DOM — infer the underlying ratio from those, then regular…
Review the “Design Tokens (CSS custom properties)” section in the pinned source before continuing.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 47 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Typography feels cohesive when all font sizes are related to each other through a single mathematical ratio. Without a scale, sizes get picked arbitrarily and the result feels visually noisy — headings that don't contrast enough, body text too close in size to captions, labels that blend into content.
A modular scale starts from a base size and multiplies or divides by a ratio to generate every size in the system.
size(n) = base × ratio^n
Every size is thus a deliberate step away from the base — not a guess.
| Ratio | Name | Feel | Good for |
|---|---|---|---|
| 1.067 | Minor Second | Very tight | Dense data UIs, dashboards |
| 1.125 | Major Second | Subtle | Long-form reading, editorial |
| 1.200 | Minor Third | Balanced | Most UI applications |
| 1.250 | Major Third | Clear hierarchy | Marketing, landing pages |
| 1.333 | Perfect Fourth | Strong contrast | Display, hero sections |
| 1.414 | Augmented Fourth | Dramatic | Portfolios, branding |
| 1.500 | Perfect Fifth | Very dramatic | Use sparingly |
Default recommendation: 1.25 (Major Third) — enough contrast between steps to feel intentional without being theatrical.
Recover an existing scale, don't reverse-engineer it by hand (dembrandt engine, optional). If a brand already has type on the web,
get_typographyreturns the real font sizes, weights, and line-heights computed off the live DOM — infer the underlying ratio from those, then regularise it with the method below, instead of guessing which sizes were intended. Seeextract-design.
Starting from base = 16px, ratio 1.25:
| Step | Formula | Value | Rounded | Role |
|---|---|---|---|---|
| -2 | 16 ÷ 1.25² | 10.24px | 10px | Caption, label-xs |
| -1 | 16 ÷ 1.25 | 12.80px | 13px | Label, small |
| 0 | 16 | 16px | 16px | Body (base) |
| +1 | 16 × 1.25 | 20px | 20px | Body-lg, lead |
| +2 | 16 × 1.25² | 25px | 25px | H4 |
| +3 | 16 × 1.25³ | 31.25px | 31px | H3 |
| +4 | 16 × 1.25⁴ | 39.06px | 39px | H2 |
| +5 | 16 × 1.25⁵ | 48.83px | 49px | H1 |
| +6 | 16 × 1.25⁶ | 61.04px | 61px | Display |
Round to whole pixels or rem — the ratio provides the intent, exact pixel rounding is fine.
:root {
--text-xs: 0.625rem; /* 10px — caption */
--text-sm: 0.813rem; /* 13px — label */
--text-base: 1rem; /* 16px — body */
--text-lg: 1.25rem; /* 20px — lead */
--text-xl: 1.563rem; /* 25px — h4 */
--text-2xl: 1.938rem; /* 31px — h3 */
--text-3xl: 2.438rem; /* 39px — h2 */
--text-4xl: 3.063rem; /* 49px — h1 */
--text-5xl: 3.813rem; /* 61px — display */
}
// tailwind.config.js
fontSize: {
'xs': ['0.625rem', { lineHeight: '1rem' }],
'sm': ['0.813rem', { lineHeight: '1.25rem' }],
'base': ['1rem', { lineHeight: '1.5rem' }],
'lg': ['1.25rem', { lineHeight: '1.75rem' }],
'xl': ['1.563rem', { lineHeight: '2rem' }],
'2xl': ['1.938rem', { lineHeight: '2.25rem' }],
'3xl': ['2.438rem', { lineHeight: '2.5rem' }],
'4xl': ['3.063rem', { lineHeight: '1.1' }],
'5xl': ['3.813rem', { lineHeight: '1' }],
}
Without a scale, designers and developers pick sizes by eye or habit (14px, 16px, 18px, 24px, 32px, 48px). These feel subtly wrong because the intervals are uneven — the jump from 14→16 is small, 32→48 is large, and there is no underlying logic tying them together. The eye senses the inconsistency even when the viewer cannot name it.
With a modular scale, every size step carries the same visual weight of change. Hierarchy reads clearly because each level is a proportional step away from the next, not an arbitrary gap.
Body text base: 16px minimum. This is the browser default for good reason — it is the threshold below which reading comfort drops significantly, especially on screens.
In the modular scale, this means the base (step 0) should be 16px, and negative steps (step -1, step -2) should be used only for genuinely secondary content — never for body copy or primary labels.
The 1% heuristic. Sub-16px text is the rare exception (target: under ~1% of a page, never below 14px). Nobody counts characters, so apply it as a role test:
Check: scan one screen, count distinct sub-16px roles. Two or three from the whitelist is healthy; five or more — or any reading content — means you've over-shrunk. That's layout density, not a type problem: cut what's shown rather than shrink the type to fit.
Size and ratio set the structure; these details determine whether the type actually reads well on screen.
0. Adding it to running text slows reading and makes the type feel loose.0.04em, and reach for that only when the label genuinely needs air.≤ 0.04em) on uppercase labels — never a blanket value. Over-tracking reads as dated, not premium.Light text on dark appears optically thinner — a halation effect where bright type bleeds into the dark field. Step up one weight to compensate: where regular (400) works on light, use medium or semibold for the equivalent text on dark. This keeps perceived weight consistent across modes instead of dark-mode text looking frail.
Monospace is for technical content where character alignment matters — code, IDs, numeric tables, diffs. Don't use it as a default UI typeface, and avoid monospace + uppercase (even widths plus tall capitals are hard to scan).
A counter, a countdown, a price that updates, a column of figures — proportional digits give each number a different width, so the text jitters sideways on every tick and columns fail to line up. Switch to tabular figures anywhere digits update or stack; leave body text proportional, where tabular digits look gappy.
.stat, .price, td.numeric { font-variant-numeric: tabular-nums; }
Headings are the place to control the break. text-wrap: balance evens the lines of a short heading and stops the one-word last line; text-wrap: pretty only prevents the orphan and is the cheaper choice for paragraphs. Balance is capped at a handful of lines by the browser, so it is a heading tool, not a body tool.
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
Bind a word to what follows it with a non-breaking space — 10 kg, Figure 3, a name and its title. A unit stranded alone on the next line reads as a typo.
Landing pages and marketing surfaces benefit from large, expressive type — steps +4 to +6 for headlines create drama and brand presence.
Feature pages and application UI should use a more controlled range — steps +2 to +3 for headings, with body text at step 0. Oversized headings inside a functional UI distract from the content and make the layout feel unbalanced.
Match the ratio and scale usage to the purpose of the surface, not just the brand.
A successful heading scale uses more than just font size to distinguish levels. It also respects the cognitive limits of the page.
If headings only differ by small increments of size, they become hard to distinguish at a glance. Use these tools to create a more meaningful scale:
text-transform: uppercase) for small, lower-level headings (H4–H5) to give them visual weight without needing large sizes.letter-spacing (≤ 0.04em) — and only when the heading genuinely needs the air. Keep it subtle; over-tracking reads as dated, not premium.Most well-designed pages require only three levels of heading hierarchy (H1, H2, H3).
information-architecture).A page that needs 6 levels of headings is a page that most users will stop reading.
Typography is not just about size — it is about the rhythm and structure of the content.
For optimal reading comfort, keep body text between 45–75 characters per line (approx. 500–700px).
Line length, line-height (leading) and font size are not three independent knobs — they are one system. This is a foundational typographic principle (Bringhurst's The Elements of Typographic Style) repeatedly validated on digital platforms, and it underpins the line-height ramps in the Material and Apple HIG type scales.
The longer the measure, the more leading the eye needs to track from the end of one line back to the start of the next. A tight line-height that reads fine on a narrow column becomes tiring on a wide one.
| Measure | Recommended body line-height |
|---|---|
| Narrow (~45ch) | 1.4 |
| Comfortable (55–66ch) | 1.5 |
| Wide (~75ch) | 1.6–1.7 |
Practical rules:
In grids, cards, or lists with unpredictable content lengths, clamp text to keep a consistent visual rhythm and equal-height cards. Limit descriptions to 2–3 lines so all cards in a row stay the same height.
Multi-line clamp. Pair the standard line-clamp with the -webkit- fallback — the legacy -webkit-box form is still required for full browser support, so keep both.
.card-description {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
overflow: hidden;
}
Single-line clamp. For titles and labels that must never wrap, use the ellipsis pattern instead.
.card-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Inside flex or grid. Truncation silently fails when the item can't shrink below its content width. Add min-width: 0 (or min-inline-size: 0) to the clamped child so it is allowed to narrow.
Accessibility — never lose the content. Clamping hides text visually only; the full string stays in the DOM and is read in full by screen readers, but a sighted user can no longer see it. Always give them a path to the rest:
title attribute (or accessible tooltip) carrying the full text, orNever clamp text that the user must read to act (prices, errors, legal copy, primary instructions) — clamp only supporting descriptions where truncation is safe.
Use specific typographic roles to provide context and guide the user through the story:
| Role | Visual Treatment | Purpose |
|---|---|---|
| Pre-title (Eyebrow) | Small (12–13px), often all-caps, subtle letter-spacing (≤ 0.04em), muted colour | Provides context or category without distracting from the main heading |
| Heading | Large, bold, modular scale step +3 to +5 | The primary hook or subject |
| Ingress (Lead text) | Larger than body (step +1), slightly bolder or higher line-height | Summarises the core value; bridging the heading and the body copy |
| Body | Base size (16px), regular weight, comfortable line-height (1.5) | The primary reading experience |
The scale compresses on smaller viewports by tightening the ratio, not by manually overriding individual sizes. The floor (body, labels) stays stable — readability has a hard minimum. The ceiling (H1, display) shrinks significantly. Every heading level scales proportionally because the ratio changes; hierarchy stays internally coherent.
Rule: use a wider ratio on desktop (more drama), a tighter ratio on mobile (more restraint). The base stays the same. The top end gives way.
Never shrink the scale from the bottom. Body text at 16px is already a floor — compression always comes from the top.
clamp()Instead of stepping sizes at fixed breakpoints, let the top end scale smoothly between a floor and a ceiling using CSS clamp(MIN, PREFERRED, MAX). The viewport-relative middle term does the scaling; the floor and ceiling stop it from ever getting too small or too large.
:root {
--text-body: 1rem; /* the floor stays fixed */
--text-h1: clamp(2rem, 1.5rem + 3vw, 3.5rem);
}
MIN is the mobile size, the MAX is the desktop size — the same floor/ceiling thinking as the stepped scale, just interpolated.clamp() to the top of the scale (headings, display). Keep body and labels at a fixed size — readability has a hard minimum, so the floor must not move.rem term in the preferred value (e.g. 1.5rem + 3vw, not 3vw alone) so the text still responds to user zoom and root font-size — a pure vw value breaks zoom accessibility.0.04em and used only when air is needed?--text-body, --text-h1) or step (--text-base, --text-2xl), not by raw pixel value?title/tooltip or detail view), and is must-read content never clamped?clamp()) is used, is it applied only to the top of the scale, with a rem-based preferred term so zoom still works?| Anti-pattern | Problem | Fix |
|---|---|---|
| Sizes like 14, 15, 16, 17px used side by side | Steps too small to read as distinct levels | Use at minimum a 1.125 ratio so each step is perceptible |
| Arbitrary sizes with no relationship (13, 18, 27, 36px) | No underlying logic — hierarchy feels accidental | Regenerate from a single base and ratio |
| Pixel values hard-coded in components instead of tokens | Scale changes require hunting through every file | Define once as CSS custom properties or design tokens |
| Same scale used for display headings and dense data tables | One ratio rarely serves both extremes well | Use a tighter ratio (1.125) for data, wider (1.25–1.333) for marketing contexts |
| Letter-spacing added to running body text | Loosens the type and slows reading | Keep body tracking at 0; add at most 0.04em to uppercase labels when air is needed |
| Regular-weight white text on a dark background | Halation makes it look thin and frail | Step up one weight (medium/semibold) for light-on-dark text |
| Monospace as a default UI font, or monospace + uppercase | Hard to scan, reads as "unstyled" | Scope monospace to code/IDs/numeric data only |
Frequently asked questions
Typography feels cohesive when all font sizes are related to each other through a single mathematical ratio. Without a scale, sizes get picked arbitrarily and the result feels visually noisy — headings that don't contrast enough, body text too close in size to captions, labels t…
The source record exposes this install command: npx skills add https://github.com/dembrandt/dembrandt-skills --skill "skills/modular-scale-typography". Inspect the command and pinned source before running it.
Alternatives
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
coreyhaines31/marketingskills
When the user wants to reduce churn, build cancellation flows, set up save offers, recover failed payments, or implement retention strategies. Also use when the user mentions 'churn,' 'cancel flow,' 'offboarding,' 'save offer,' 'dunning,' 'failed payment recovery,' 'win-back,' 'retention,' 'exit survey,' 'pause subscription,' 'involuntary churn,' 'people keep canceling,' 'churn rate is too high,' 'how do I keep users,' or 'customers are leaving.' Use this whenever someone is losing subscribers o
prowler-cloud/prowler
PostgreSQL indexing best practices for Prowler: index design, partial indexes, partitioned table indexing, EXPLAIN ANALYZE validation, concurrent operations, monitoring, and maintenance. Trigger: When creating or modifying PostgreSQL indexes, analyzing query performance with EXPLAIN, debugging slow queries, reviewing index usage statistics, reindexing, dropping indexes, or working with partitioned table indexes. Also trigger when discussing index strategies, partial indexes, or index maintenance
oaustegard/claude-skills
Generate hierarchical _FEATURES.md files that describe what a codebase DOES from a user/consumer perspective, anchored to source symbols via tree-sitting. Supports large complex codebases through feature-driven decomposition into sub-feature files. Uses a multi-pass synthesis: orientation → detail → overview rewrite. Use when someone says "what does this do", "document features", "feature inventory", "_FEATURES.md", or needs to understand a codebase's purpose before modifying it. Complements tre