Fixed font sizes break at the edges – too small on mobile, uncomfortably large on wide monitors. CSS clamp() solves this with a single line of math, and WordPress block themes make it trivially easy to roll it out site-wide through theme.json. This guide walks through the full process: understanding the formula, building a modular type scale, wiring it into your block theme, and testing it properly across breakpoints.
What Is Fluid Typography – and Why Does It Matter for Block Themes
Traditional responsive typography works in steps. You set one font size for mobile, a slightly larger one for tablet, and your “desktop” size for everything above 1024px. The result is three discrete jumps, often visible as a jarring reflow when a layout snaps between breakpoints.
Fluid typography eliminates those jumps. Instead of three fixed sizes, you define a range: a minimum, a maximum, and a continuous interpolation between them that tracks viewport width. From 320px to 1280px, a heading might scale from 2rem to 4.5rem without a single media query in sight.
The mechanism is CSS clamp(). Block themes – specifically WordPress’s theme.json format – support fluid font sizes natively since WordPress 6.1. When you combine the two, your entire typographic system responds to viewport width automatically, in every block, without extra CSS. This also connects directly to how Full Site Editing handles global styles – the same theme.json architecture that powers FSE is what makes site-wide fluid typography possible with a few lines of JSON.
Three Problems Fluid Typography Fixes
- Mobile readability: Body copy set at 18px for desktop often lands at 14px on 320px screens after the browser scales it. With fluid type you guarantee a floor – it never drops below your minimum.
- Large-screen aesthetics: On 1440px+ monitors, fixed heading sizes feel undersized against the whitespace. Fluid type fills the space proportionally.
- Maintenance overhead: Updating a fixed-size system means editing values at every breakpoint. Fluid type has one definition per size step – change the min and max, done.
How CSS clamp() Works – The Math Behind Fluid Type
The clamp() function takes three arguments: clamp(minimum, preferred, maximum). The browser uses the preferred value if it falls within the min/max bounds, clamps to min if preferred is smaller, and clamps to max if preferred is larger.
For fluid typography the preferred value is a viewport-relative expression – typically a vw unit plus a rem offset. This is where the math comes in.
The Slope-Intercept Formula
You want font size to be exactly minSize at minViewport and exactly maxSize at maxViewport. The preferred value is a linear interpolation between those two points – a line in the form slope * viewport + intercept.
Working in rem throughout (converting px viewports to rem by dividing by 16):
slope = (maxSize – minSize) / (maxViewport_rem – minViewport_rem)
intercept = minSize – slope x minViewport_rem
preferred = slope x 100vw + intercept
result = clamp(minSize rem, preferred, maxSize rem)
Example for a 1rem-to-1.5rem body text range across 320px to 1280px viewports:
- minViewport in rem = 320 / 16 = 20rem
- maxViewport in rem = 1280 / 16 = 80rem
- slope = (1.5 – 1) / (80 – 20) = 0.5 / 60 = 0.00833
- intercept = 1 – 0.00833 x 20 = 1 – 0.1667 = 0.8333rem
- result = clamp(1rem, 0.8333vw + 0.8333rem, 1.5rem)
You do not need to run this arithmetic manually. The JavaScript calculator in the code snippet below handles any combination of min/max sizes and viewport bounds, and generates the full type scale at once.
Run this locally, paste the output into theme.json, and you have a mathematically consistent scale rather than a collection of guesses.
Enabling Fluid Typography in theme.json
WordPress 6.1 added a single top-level flag to opt the entire site into fluid typography: "fluid": true inside settings.typography. When this flag is set, WordPress generates fluid clamp() values from the fluid.min and fluid.max sub-keys on each font size definition.
The structure looks like this:
What WordPress Does with This Definition
When "fluid": true is set globally and a font size entry includes a fluid object, WordPress builds the clamp() expression itself at build time and outputs it as a CSS custom property:
--wp--preset--font-size--large: clamp(1.125rem, 1rem + 0.625vw, 1.5rem);
Every block that uses the “Large” font size preset automatically picks this up. You change one value in theme.json and the update propagates to every Heading, Paragraph, Cover, and Query Loop block on the site.
If you also set an explicit size string (with your own clamp() expression), WordPress uses that string directly. This gives you an escape hatch when you want manual control over the preferred expression rather than WordPress’s generated formula.
Per-Size Opt-Out
The global "fluid": true flag applies to all sizes that have a fluid sub-object. Sizes that do not include a fluid key are left as fixed values. This is useful for small utility sizes like captions where fluid scaling adds no value and might produce fractional pixel results that look blurry at screen resolutions.
Building a Modular Fluid Type Scale
A modular type scale is a set of font sizes where each step is a consistent ratio apart from the next. Common ratios are 1.25 (Major Third), 1.333 (Perfect Fourth), and 1.5 (Perfect Fifth). The idea is that if your body text is 1rem, your H4 is 1.25rem, your H3 is 1.563rem, your H2 is 1.953rem, and your H1 is 2.441rem – each value follows a formula rather than being picked by eye.
For fluid typography, you apply this ratio discipline to both the minimum and maximum ends of the scale. A common pattern is to use a tighter ratio (e.g. 1.2 Minor Third) at the minimum viewport and a wider one (e.g. 1.333 Perfect Fourth) at the maximum, so the scale “opens up” as screens get larger.
A Practical 7-Step Scale for Block Themes
The following scale covers everything from caption text to hero headings. The min values are sized for comfortable reading on a 320px screen; the max values suit a 1280px desktop layout. Each slug maps directly to WordPress’s expected naming convention so the Site Editor labels make sense to content editors.
| Slug | Label | Min | Max | Usage |
|---|---|---|---|---|
| xs | XS – Caption | 0.75rem | 0.875rem | Captions, labels, metadata |
| sm | SM – Small Text | 0.875rem | 1rem | Secondary body, footnotes |
| base | Base – Body | 1rem | 1.125rem | Main body copy |
| lg | LG – Lead / H4 | 1.125rem | 1.5rem | Lead paragraphs, H4 |
| xl | XL – H3 | 1.5rem | 2rem | H3 headings |
| 2xl | 2XL – H2 | 2rem | 3rem | H2 headings, section titles |
| 3xl | 3XL – H1 / Hero | 2.5rem | 4.5rem | H1, hero text, page titles |
The full theme.json definition for this scale, including the styles.elements mappings that wire each heading level to the right font size preset:
The styles.elements Section Explained
The settings.typography.fontSizes section creates the presets. The styles.elements section applies them to semantic HTML elements globally. When you map h2 to var(--wp--preset--font-size--2xl), every H2 block on the site uses the fluid 2XL size – even blocks you have not touched yet. Add a new post tomorrow and the headings are already fluid.
Line height is worth setting here too. Large fluid type benefits from tighter line height – a 4.5rem heading at wide viewport needs about 1.1 line height, not the 1.5 you might use for body text. The example above uses 1.1 for H1 and steps up to 1.4 for H4, which tracks with common editorial conventions.
Viewport-Responsive Heading Hierarchy in Practice
Setting up the scale in theme.json is the technical part. Getting the heading hierarchy right visually across breakpoints is a design decision, and it is worth understanding what can go wrong.
The Scale Compression Problem
With a large-ratio scale (say 1.5), an H1 at 4.5rem and an H2 at 3rem look clearly differentiated on desktop. On mobile, if H1 shrinks to 2.5rem and H2 shrinks to 2rem, the difference is only 0.5rem – the hierarchy becomes murky. This is the scale compression problem.
Two approaches address it:
- Asymmetric clamping: Let higher heading levels compress more aggressively on mobile. H1 might drop from 4.5rem to 2rem (a large range), while H2 drops from 3rem to only 2rem (a smaller range). The visual distance on mobile is preserved.
- Weight and spacing differentiation: On mobile, use font-weight and letter-spacing to distinguish heading levels that are close in size. H1 at 700 weight vs H2 at 500 weight reads as a clear hierarchy even at similar sizes.
Fluid Spacing to Match Fluid Type
Fluid typography works best when the surrounding spacing is also fluid. A heading that scales from 2rem to 4.5rem looks off when the padding above it is a fixed 48px regardless of viewport. The site’s spacing scale should follow the same approach – define minimum and maximum values and let them interpolate.
In theme.json, settings.spacing.spacingSizes supports fluid values in the same way as font sizes. Set a spacer scale alongside your type scale and both will respond proportionally. The way shadcn/ui handles design tokens – uniform naming conventions applied across the whole system – is a useful model for structuring both your type and spacing scales in theme.json.
Fluid typography only feels right when the space around type scales with it. Fixed padding around fluid headings breaks the proportional logic the scale was designed to provide.
Testing Fluid Type Across Breakpoints
You cannot test fluid typography by checking only the mobile and desktop breakpoints. The whole point is that it scales continuously – if something looks wrong at 768px, it will not show up in a standard two-breakpoint test. Here is a systematic approach.
Browser DevTools Viewport Resize
In Chrome or Firefox, open DevTools and use the responsive design mode. Drag the viewport width handle slowly from 320px to 1400px. Watch each heading level in isolation – you are looking for:
- Minimum clamp points: At 320px, each size should be exactly your defined minimum. Check that the clamp is clamping correctly and not letting the vw component pull below the floor.
- Maximum clamp points: At 1280px (or your defined maximum viewport), sizes should have reached their ceiling. Drag wider and confirm they stop growing.
- Mid-range behavior: At 768px, the scale should feel balanced. Headings should be clearly distinguishable from each other and from body text.
- Line breaks: Watch for headings that wrap awkwardly at certain widths – a 3-word heading that fits on one line at 400px and at 900px but wraps at 600px is a sign the preferred slope is too steep.
The Testing CSS Snippet
This utility stylesheet adds debug indicators to your type scale during development. It highlights the minimum boundary at 320px (red outline) and maximum boundary at 1280px (green outline), so you can immediately confirm whether clamping is working. Drop it as a child theme style or a Custom CSS entry during testing, then remove it before going live.
Real Device Testing
Browser DevTools are accurate but not the whole picture. Test on actual hardware at two points in the range:
- Phone (375-390px): This is where the minimum values activate. Check body copy legibility and heading hierarchy clarity. A common failure mode is H1 looking like H2 because the gap closed too much at mobile.
- Laptop (1280-1440px): This is where maximums activate. Check that hero headings do not feel oversized relative to body text – a 4.5rem H1 against 1.125rem body is a 4:1 ratio which is at the upper limit of comfortable hierarchy.
Checking CSS Custom Property Values in the Browser
WordPress outputs the fluid font size values as CSS custom properties on the :root element. In DevTools, inspect the root element and look for the --wp--preset--font-size--* variables. You will see the full clamp() string. Use the “Computed” tab to see the resolved value at the current viewport width – this is a direct readout of what the browser is rendering.
Common Mistakes and How to Fix Them
Mistake 1: Setting “fluid”: true Without fluid Sub-Objects
Setting "fluid": true at the top level tells WordPress you want fluid sizing, but it only activates for sizes that include a fluid sub-object with min and max keys. Without those, WordPress defaults to using 75% of the size as the min value, which often produces unexpected results. Always define explicit fluid.min and fluid.max.
Mistake 2: Using px Units in the fluid Object
The fluid.min and fluid.max values should be in rem, not px. WordPress converts them to rem for the clamp expression, but a pixel value like "min": "18px" can produce unexpected generated output. Stick to rem throughout.
Mistake 3: Forgetting the styles.elements Binding
Defining font sizes in settings only creates the presets – it does not automatically apply them to headings. You must also add the styles.elements.h1 through styles.elements.h6 bindings. Without them, headings fall back to browser defaults or whatever the active theme previously set.
Mistake 4: Too Much Range in the Preferred Expression
A steep slope means the font size changes rapidly with viewport width. If your H1 is 2.5rem at 320px and 4.5rem at 1280px, the slope is 2rem over 960px – about 0.2% per pixel of viewport. At exactly 700px it will be around 3.25rem. At 800px it will be 3.45rem. These are smooth, readable steps. But if you define 1rem min and 5rem max over the same range, the slope is so steep that the type feels jittery as you resize.
A reasonable guideline: the max should not be more than 2.5x the min for body text, or 3x for display headings. Beyond that, the scale loses its sense of stable proportion.
Mistake 5: Not Accounting for the User’s Root Font Size
Some users change their browser’s default font size for accessibility reasons. Using rem units respects this – if a user sets their base to 20px instead of 16px, all your rem values scale up proportionally, which is the correct behavior. Using px in your clamp expressions breaks this accessibility feature. Always work in rem.
Fluid Typography as Part of a Block Theme Design System
This article is part 2 of the Block Theme Design Systems series. Part 1 covered theme.json global settings and styles – the architecture that makes centralized control possible. Fluid typography is the first design token you build on top of that architecture.
The pattern established here – define a named scale in settings, apply it in styles.elements, reference it everywhere via CSS custom properties – extends to all other design tokens. Color palettes, spacing scales, border radii, font families: they all follow the same shape. Master fluid typography and you have mastered the methodology for the whole system. This is also why understanding block theme pattern overrides in WordPress 7.0 matters for design system work – patterns and typography share the same token-based architecture.
What Comes Next in the Series
Part 3 covers fluid spacing scales using the same clamp approach applied to settings.spacing.spacingSizes. Part 4 will look at color system architecture in theme.json – semantic color tokens, dark mode, and how the Site Editor exposes palette choices to clients without breaking your design constraints.
If you are building block themes for clients, the design systems approach pays off quickly. A single well-structured theme.json replaces hundreds of lines of media query CSS and gives your clients a coherent editing experience in the Site Editor – they see the same named type sizes and colors whether they are working on a homepage hero or a blog sidebar widget.
Frequently Asked Questions
Does fluid typography work in the WordPress Site Editor?
Yes. The Site Editor renders blocks using the same CSS the front end uses, so fluid font sizes display correctly in the editor. Block controls that expose font size pickers show your named scale slugs (Small, Medium, Large, etc.) – selecting one applies the --wp--preset--font-size--* custom property, which resolves to the clamp() expression at the current editor width.
Can I use fluid typography with a classic theme?
Classic themes do not support theme.json in the same way, so you cannot use the settings.typography.fontSizes approach. You can still use clamp() directly in your stylesheet – the CSS function works everywhere. The theme.json approach simply makes it easier to manage centrally and expose consistently through block controls.
What happens if I override a fluid size in the Site Editor?
When a user selects a specific font size for an individual block in the Site Editor, WordPress saves that size choice as a block attribute. If the size comes from a preset (e.g. “Large”), it uses the preset value – which is your clamp() expression, so it remains fluid. If the user types in a custom px value, that value is fixed. Preset-based sizing is always fluid; custom inline sizing is not.
Does fluid typography affect page load performance?
No. The clamp() expressions are pure CSS – there is no JavaScript involved. The browser resolves them natively, the same way it resolves any other CSS value. If anything, fluid type reduces performance overhead by eliminating the multiple media query blocks that a traditional breakpoint-based approach would require.
What is the minimum WordPress version for theme.json fluid typography?
The "fluid": true flag in settings.typography was added in WordPress 6.1. The fluid.min and fluid.max sub-keys are supported from 6.1 onwards. The CSS clamp() function itself is supported in all modern browsers (Chrome 79+, Firefox 75+, Safari 13.1+).
Summary and Quick Reference
Here is the complete process in a single checklist:
- Set
"fluid": trueinsidesettings.typographyin theme.json - Define each font size with a
fluidobject containingminandmaxvalues in rem - Use the slope-intercept formula (or the calculator snippet above) to generate the preferred vw expression
- Assign each heading level in
styles.elementsto the appropriate font size preset - Add fluid line-height values alongside font sizes to maintain readability at scale
- Test by dragging the DevTools viewport from 320px to 1400px and watching the continuous scale
- Verify min and max values are clamping correctly at the boundary viewports
- Check real devices – phone at 375px and laptop at 1280px are the two critical points
The full theme.json snippet, JavaScript calculator, and testing CSS are all in the Gist above. Copy them directly into your project and adjust the min/max values to match your design system’s needs.
Build Your Block Theme Design System
This is article 2 of the Block Theme Design Systems series on Brndle. If you found the fluid typography approach useful, the next article in the series covers building a consistent spacing scale using the same methodology – one definition per step, applied everywhere through CSS custom properties.
Subscribe to the Brndle newsletter to get each new article in the series as it publishes, along with practical theme.json snippets and Site Editor patterns you can use right away.
