Install
openclaw skills install @monikazapisek/type-scale-generatorUse when generating a full typographic scale (H1-H6, body, caption) from a base font size and a mathematical ratio or historical scale — each step gets a rounded size, a line-height and a tracking value. Outputs CSS custom properties or Tailwind config, or, with Figma access, variables and text styl
openclaw skills install @monikazapisek/type-scale-generatorGenerate a mathematically coherent set of text styles from a base size and a ratio (or a historical fixed-step scale): sizes rounded to avoid subpixel rendering, each with a line-height and tracking taken from the same typographic rules the rest of this skillset uses. Output is design tokens for code, or variables and text styles in Figma.
text-typesetting) or spacing between elements (vertical-spacing).base-size: default 16px. In Figma, taken from the selected text (Step 0).font-family-category: sans-serif / serif — needed for tracking, and for line-height when x-height isn't measured (Step 4). In Figma, read from the selection.x-height: ratio of x-height to font size, measured or read from the font (Step 4). Optional, but without it line-height is a fallback, not a computed value.scale-ratio: one of the named/numeric ratios below, or fibonacci / classic-garamond.steps: how many sizes up (headings) and down (caption/small) from base — default 4 up, 1 down (covers body, small, h4, h3, h2, h1).responsiveness: static or fluid (clamp()). Fluid applies to code output only.fontSize config, matching whichever convention the project already uses).This skill reads first, shows the proposed scale, and asks only when a missing value would change the result.
Ask one question at a time and wait for the answer. If the user already gave a value in their request (a ratio, a base, a step count), take it and say which one you took — don't ask again.
Outside Figma: use the stated base-size, or 16px.
In Figma, resolve the selection first:
| Selection | Do this |
|---|---|
| Nothing selected | Ask the user to select some text. Stop. |
| No text node in the selection | Say what is selected instead and ask for text. Stop. |
| One text node | Use it. |
| Several text nodes | List them by name and size and ask which one is the body text. Stop. |
Read the font and size from the node, never assume a typeface (getStyledTextSegments(["fontName", "fontSize"])). If the node holds more than one style, use the first segment and say so. Confirm the base with the user before computing: state family, style and size, and offer to use a different size.
Musical/geometric ratios (size = base × ratio^step):
| Ratio | Name | Character |
|---|---|---|
1.067 | Minor Second | Very compact — dense dashboards, data-heavy mobile UI. Pair with a small base (12–14px) so headings don't dominate. |
1.125 | Major Second | Standard for SaaS apps and complex dashboards — subtle, clean hierarchy. |
1.200 | Minor Third | Safe, universal default — works for both product UI and marketing pages. |
1.250 | Major Third | Blogs and marketing pages where headings need to clearly separate from body. |
1.333 | Perfect Fourth | Latin's recommended starting point for responsive web — very readable on desktop. |
1.414 | Augmented Fourth | Bold, dynamic, poster-like character. |
1.500 | Perfect Fifth | Aggressive — H1 becomes very large. Portfolio / product-launch pages. |
1.618 | Golden Ratio | Bringhurst's classic proportion. Grows extremely fast at higher steps — usually needs a smaller ratio on mobile breakpoints. |
Non-linear / historical scales (fixed steps, not a single multiplier):
fibonacci: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144... — map directly to px or rem (e.g. body 13/21px, H1 55/89px). Use when the layout grid itself is Fibonacci-proportioned, so type and grid share the same logic.classic-garamond: 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 21, 24, 36, 48, 60, 72 — the historical printer's type-size series (pre-digital, renaissance-era steps). Use when a project wants a traditional/editorial feel over a computed geometric curve; pick the nearest listed step rather than interpolating.If the user doesn't specify, default to 1.200 (Minor Third) — safe, universal, per the table above. When asking, offer three or four ratios that fit the stated use, not the whole table.
Out of scope: platform presets (fixed size lists published by a framework or an operating system) and scales built from two ratios. Neither comes from the sources this skill is based on. If the user asks for one, say so; build a two-ratio scale only on explicit request and label it as outside the documented method.
For geometric ratios: size(step) = base-size × ratio^step, where step is negative for sizes below base (caption/small) and positive above (headings). Show the raw (unrounded) result before applying Step 3.
For fibonacci/classic-garamond: pick the nearest sequence value to each target role rather than computing — these are lookup tables, not formulas.
16 × 1.333 = 21.328 → round to 22px, and prefer 24px if the project's spacing grid is 4px/8px-based and a 4px-multiple is within ~1px). State which rounding was applied — don't silently pick one without showing the raw number.12, 16, 20, 24 — an arithmetic sequence, no longer a ratio), the rounding grid is too coarse for the ratio. Say so, then fix it by rounding those steps to the nearest even integer instead of a 4px multiple, or by proposing the next wider ratio. Don't hand over a collapsed scale without naming it.12px, and never pair a sub-12px size with a font-weight below 400. Flag and refuse to emit a token that violates this, rather than emitting it silently.responsiveness: fluid, wrap the two endpoint sizes (mobile base, desktop computed size) in a clamp(min, preferred, max) — compute preferred as a vw-based interpolation between the two breakpoints; state the breakpoints assumed.A scale of sizes alone is half a type system. Give every step a line-height and a tracking value, using the rules of text-typesetting (Step 1 and Step 2, Branch B). They are restated here so this skill works on its own.
Line-height — a range per step, and a computed position inside it:
| Step | Range (LH_min–LH_max) |
|---|---|
| Caption / small (below base) | 1.2–1.4 (working range; no direct source found) |
| Body (base) | 1.2–1.45 |
| Heading (above base) | 1.1–1.3 (working range; no direct source found) |
Where a step falls inside its range depends on the typeface's x-height — the height of a lowercase x divided by the font size (xr). A face with a large x-height fills more of the line and needs more room; a face with a small x-height carries its own air. Compute it once for the font and use it for every step:
t = clamp((xr − 0.447) / (0.545 − 0.447), 0, 1)
LH = LH_min + t × (LH_max − LH_min)
The two anchors are the reference faces the W3C uses to explain x-height: Times, 0.447, as a low aspect value and Verdana, 0.545, as a high one (CSS Fonts Module, font-size-adjust). The direction is Butterick's: fonts that run small need less line spacing, and vice versa. The body range is Butterick's 120–145% of the point size. No direct source was found for the heading and caption ranges; they are working values of this skillset and must be reported as such.
How to get xr:
sxHeight / unitsPerEm from the font's OS/2 table; in a browser, 1ex measured against 1em.x in the font at 100px, flatten it to a vector (figma.flatten([node])), read its height, divide by 100, and remove the vector. This creates, flattens, and deletes a layer: ask for permission and wait before doing it. Checked values from this method: Inter 0.545, Roboto 0.528, IBM Plex Sans 0.516, Source Serif 4 0.452, EB Garamond 0.409.Convert to px (size × LH) and round the same way as sizes: nearest even integer, or a 4px multiple when one is within ~1px. Show xr, the ratio and the px value.
Tracking — sentence-case text:
0.-1.5% (sans-serif), -0.75% (serif).-2% (sans-serif), -1% (serif).These values assume sentence or title case, Regular/Medium weight and vertical trim off. A style meant for all-caps text needs positive tracking instead — TS = max(3%, min(12%, 160 / FS)), see text-typesetting Step 2 Branch A. Say which assumptions were used.
Print the whole scale as a table in the conversation: token, step, raw size, rounded size, line-height (ratio and px), tracking. Name the ratio and the rounding used. Flag anything Step 3 caught.
Nothing is created in a file or in Figma before the user has seen this table. Nobody can decide whether to commit a scale to tokens before they have seen the numbers.
Emit as CSS custom properties (or the project's existing token format if shown in the input):
--text-sm: 0.833rem; /* 13.33px → rounded 13px */
--text-base: 1rem; /* 16px */
--text-md: 1.2rem; /* 19.2px → rounded 20px */
--text-lg: 1.44rem; /* 23.04px → rounded 24px */
Add line-height and letter-spacing tokens per step when the project's convention has a place for them.
When running with Figma access. Each of the three writes below is separate: ask before each one, and do only what was agreed.
figma.getLocalTextStylesAsync()) and variable collections (figma.variables.getLocalVariableCollectionsAsync()). If a scale is already in use, show how the new one differs and ask whether to add to it, replace named styles, or stop. Never overwrite a style or variable that has the same name without asking.FLOAT variables for each step's font size and line-height in px, with scopes ["FONT_SIZE"] and ["LINE_HEIGHT"], never ALL_SCOPES. Use the rounded values from Steps 3–4, never the raw ratio output: Figma renders fractional sizes with the same subpixel risk as CSS.figma.createTextStyle()), using the family and style read in Step 0 — load the font first. Bind fontSize and lineHeight to the variables with textStyle.setBoundVariable("fontSize", variable) and ("lineHeight", variable) when variables were created; otherwise set them directly, lineHeight as { unit: "PIXELS", value }. Set letterSpacing as { unit: "PERCENT", value }. Match the file's existing style naming (heading/h1, Heading/H1, …); ask if there is none.0, 0. Sample text uses the styles — no local overrides — so the specimen stays true when a variable changes.font-size-adjust — aspect value (x-height divided by font size); Verdana 0.545, Times 0.447.figma.getLocalTextStylesAsync, figma.createTextStyle, TextStyle.setBoundVariable, VariableScope (FONT_SIZE, LINE_HEIGHT), TextNode.getStyledTextSegments.text-typesetting (line-height/tracking per size — source of Step 4), vertical-spacing (rhythm between the blocks this scale produces).