惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
Scott Helme
Scott Helme
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Schneier on Security
I
Intezer
Spread Privacy
Spread Privacy
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
Cloudbric
Cloudbric
V2EX - 技术
V2EX - 技术
Google Online Security Blog
Google Online Security Blog
L
Lohrmann on Cybersecurity
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LINUX DO - 热门话题
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
博客园 - 【当耐特】
Recent Announcements
Recent Announcements
Security Archives - TechRepublic
Security Archives - TechRepublic
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Latest news
Latest news
B
Blog
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
L
LangChain Blog
GbyAI
GbyAI
Last Week in AI
Last Week in AI
S
Security Affairs
Apple Machine Learning Research
Apple Machine Learning Research
N
Netflix TechBlog - Medium
Security Latest
Security Latest
Vercel News
Vercel News
Y
Y Combinator Blog
G
GRAHAM CLULEY
S
Securelist
T
Troy Hunt's Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
雷峰网
雷峰网

Matthias Ott

Hello Again, World This, Still Not for Everyone The Shape of Friction WeissKlang L1 – Punching Above Its Weight Continvoucly Morged Value Webspace Invaders To Affinity and Beyond The Mystery of Storytelling Amateurs! Echoes of Connection Linear() Is Not (That) Linear View Transitions: The Smooth Parts Adding AVIF and WebP Support to My Craft CMS Site Challenge Acoustic Room Treatment and Building Sound Panels, Part 1: Planning Play On Overshoot The HTML Output Element Listening Closely The Lifeblood of the Web What Could Go Wrong? That’s My Rank Making Space CSS :is() :where() the Magic Happens Visual Regression Testing for External URLs With Playwright Jane Goodall’s Famous Last Words European Tech Alternatives 🇪🇺 Independent Type Foundry Advent Calendar – Day 24: NaN Independent Type Foundry Advent Calendar – Day 23: Typotheque Independent Type Foundry Advent Calendar – Day 22: 205TF Independent Type Foundry Advent Calendar – Day 21: HvD Fonts Independent Type Foundry Advent Calendar – Day 20: Frere-Jones Type Independent Type Foundry Advent Calendar – Day 19: Fontwerk Independent Type Foundry Advent Calendar – Day 18: Vectro Independent Type Foundry Advent Calendar – Day 17: Studio René Bieder Independent Type Foundry Advent Calendar – Day 16: R-Typography Independent Type Foundry Advent Calendar – Day 15: David Jonathan Ross Independent Type Foundry Advent Calendar – Day 14: Interval Type Independent Type Foundry Advent Calendar – Day 13: Newglyph Independent Type Foundry Advent Calendar – Day 12: Swiss Typefaces Independent Type Foundry Advent Calendar – Day 11: Sharp Type Independent Type Foundry Advent Calendar – Day 10: Colophon Foundry Independent Type Foundry Advent Calendar – Day 9: Commercial Type Independent Type Foundry Advent Calendar – Day 8: Letters from Sweden Independent Type Foundry Advent Calendar – Day 7: Lineto Independent Type Foundry Advent Calendar – Day 6: Ohno Type Company Independent Type Foundry Advent Calendar – Day 5: Milieu Grotesque Independent Type Foundry Advent Calendar – Day 4: TypeMates Independent Type Foundry Advent Calendar – Day 3: Klim Type Foundry Independent Type Foundry Advent Calendar – Day 2: Dinamo Independent Type Foundry Advent Calendar – Day 1: Grilli Type The Independent Type Foundry Advent Calendar 2022 A Conversation With ChatGPT ChatGPT, please explain websites in the words of William Shakespeare Transient Frameworks Leaving Twitter Behind Converting Your Twitter Archive to Markdown The Wrong Question It Wasn’t Written Syndicating Posts from Your Personal Website to Twitter and Mastodon Suspension None of Your Business Shitty Code Prototypes Doing Our Part Patch That Package Brain Dump Generating Accessibility Test Results for a Whole Website With Evaluatory The CSS Cascade, a Deep Dive Updates About Updates How to Delete Your Commit History in Git Unblocking Your Writing Blocks, Part 2: I’m Not an Expert nor a “Thought Leader” Connections No Wrong Notes Better Options Design Debt Finite and Infinite Games Don’t Assume, Validate. Necessity Is the Ultimate Teacher One Egg Go Deep There Is No Secret Code Balancing Risk Blue Eyes, Brown Eyes The Shortcut Boomerang My RSS Feed Collection of Personal Websites Frequency The Illusion of Control The Decisions Journey Write It Down Nownownow Into the Personal-Website-Verse Considering the Opposite What is it for? Unlimited Bowling. Never done. We Are Team Internet. We Need to Save #NetNeutrality. Progressive Search Data loss (also) by JavaScript Books I Will Definitely Maybe Read in 2017 Starting to Write Notes
Compressed Fluid Typography
Matthias Ott · 2025-10-13 · via Matthias Ott

When it comes to web typography, I’m a sucker for fluid type. I love that it creates a harmonious rhythm for the typography of a project. I love how it speeds up the responsive design process in the browser. And that it feels like you are working with the grain of the web, not against it. Instead of trying to control every typographic detail, you are defining boundaries that make sure your design works well – regardless of the end device. Fluid type is a textbook example of what Jeremy likes to call declarative design.

Fluid type can sometimes also be tricky, though. I’ve worked with teams who struggled with it or even abandoned it completely because it didn’t work for their more imperative design process. One of the issues that some people have with it, for example, is that you have to be fine with giving up control over how large your typography is on a particular screen. That uncertainty is something not everyone feels comfortable with. But once you do you don’t want to go back.

Basic Assumptions #

But there’s another issue that’s not so easily dismissed: how fluid type responds to changes in the base font size. By default, most browsers set this base to 16px, and many fluid typography solutions simply assume that value when performing their calculations. Just take the most common approach to fluid type, using rem units and clamp():

font-size: clamp(1rem, 0.6522rem + 1.7391vw, 2rem);

We add a fraction of the viewport (or container) width to the base font size. Using rem ensures that our text remains responsive to any user-defined font size changes. And the clamp() ensures the font-size never gets smaller than our minimum and never larger than the maximum value.

This idea of fluidly scaling our type is then combined with a much older concept: the typographic scale. In the sixteenth century, European typographers developed proportional systems of type sizes to create harmony between different levels of text. The principle is simple: numbers related by consistent ratios feel balanced, much like intervals in music.

Fluid type 001

In a modular scale, we use a multiplier to generate a set of harmonious values. For example, a multiplier of 2 produces an “octave.” Or, if we use 1.5 as our multiplier, we get a “perfect fifth“.

Fluid type 003

On the Web, these calculations almost always start from the assumption that the base font size is 16px. We use multiples of 16 in our design files, we use multiples of 16 in our CSS, we are treating 16px almost as a universal standard. And while that assumption usually holds true, it only works – until it doesn’t.

Assuming that 16px is always the base font size for fluid typography in CSS is problematic because it overlooks how people actually use the web. Many users adjust their browser’s default font size for comfort or accessibility. And the moment someone bumps their default up to 24px, our carefully tuned “fluid” type system can quickly fall apart. If we are using rems – which we should – the type will scale up, which is great. But headlines that were already large can become comically huge, throwing off visual hierarchy and breaking the overall design.

Authors and Users #

This raises an important question: how do we balance author intent with user preferences? As a designer, might want a headline to feel bold and impactful. That’s a legitimate aesthetic choice. But if someone looking at my design in the browser prefers to read my text in a larger font size, does this also mean that the whole typographic scale has to grow and increase to an almost unusable size? That probably wasn’t my intent at all. Ideally, our designs should respect someone’s preferred base font size without becoming unusable at larger settings. That’s why I was curious about what Miriam had to say about it. And wow, she really dives deep! I highly recommend reading her series of blog posts on the Oddbird site, in particular Designing for User Font-size and Zoom.

Adding a Scale Factor #

So, I was reading through Miriam’s excellent articles and thought about her idea of negotiating between user settings and design intent using expressions like max(1em, 20px) or clamp(1em, 20px, 1.25em), when all of a sudden a question popped into my head: now that we can divide by units in CSS calc() – which is already possible today thanks to Jane Ori’s clever tan/atan2 hack and likely to be supported natively soon in all browsers – couldn’t this be a way to create a weighted fluid type scale? Or, to use a metaphor from audio engineering: a compressed fluid type scale? Here’s the idea: instead of multiplying the base font size by a ratio and letting it grow ever larger, what if we could introduce a damping factor into the calculation that compresses the fluid type scale the larger the base font size gets? It could look like this, for example, dividing the assumed base font size by the actual base font size:

:root {
  --scale-factor: calc(16px / 1rem);
}

This --scale-factor changes depending on the user’s base font size setting. At the default 16px, it evaluates to 1, so there’s no change. But if the base font-size is changed, for instance to 24px, the scale factor is now 16px / 24px = 0.666, effectively compressing the scale and preventing oversized text from overwhelming the layout.

Please be aware that division between typed values to produce a unit-less number isn’t yet possible in all browsers at the time of writing this article. Chrome added it in version 140, Safari in version 26, Firefox doesn’t support it yet, for example. So make sure to use aforementioned technique by Jane Ori as a fallback, for example.

Now that we have this —scale-factor, we can start integrating it into our fluid type calculations. The idea is to multiply a usual fluid formula by this factor, so the overall growth rate of the typography slows down as the user’s base font size increases. Take this basic example:

font-size: clamp( 1rem, 1rem + (3vw * var(--scale-factor)), 3rem);

Here, the var(--scale-factor) acts as a kind of “compressor” for the fluid range. At the default base size, it doesn’t change anything – the text scales just as before. But as the base font size grows, the scale factor reduces the influence of the viewport width (3vw in this case), so headings stay visually balanced rather than scaling up uncontrollably. If we apply this approach to a set of fluid type calculations, it will reduce the ratio of the type scale, which we assume to be what we want. The body copy text can still grow. Effectively, we are reducing the “dynamic range“ of our typography.

Bringing It All Together #

And if you want, you can put all of this into a calculation that’s split up into individual custom properties. This definitely makes it look more intimidating – but it is also mich nicer to work with and easier to adjust parameters to your liking.

:root {
  
  /* This is our scaling factor based on the user font size */
  --scale-factor: calc(16px / 1rem);
  /* How aggressively should we compress the scale? 1 = full compression, 0 = no compression */
  --compression-strength: 0.7;
  
  /* The adaptive ratio multiplier that combines scale factor and compression strength and dampens the type scale at larger user font sizes */
  --ratio-dampener: calc(1 - ((1 - var(--scale-factor)) * var(--compression-strength)));
  
  /* A few ratios to choose from */
  --minorSecond: 1.06666; /* Semitone: 16:15 */
  --majorSecond: 1.125; /* 9:8 */
  --minorThird: 1.2; /* 6:5 */
  --majorThird: 1.25; /* 5:4 */
  --perfectFourth: 1.33333; /* 4:3 */
  --augmentedFourth: 1.414; /* √2/1 */
  --perfectFifth: 1.5; /* 3:2 */
  --minorSixth: 1.6; /* 8:5 */
  --goldenRatio: 1.618; /* (1+√5):2 */
  --majorSixth: 1.66666; /* 5:3 */
  --minorSeventh: 1.8; /* 9:5 */
  --majorSeventh: 1.875; /* 15:8 */
  --octave: 2; /* 2:1 */
  
  /* Base values for font-size, ratio, viewport widths */
  --baseFontSizeMin: 1rem;
  --ratioMin: var(--majorSecond);
  --minViewportWidth: 320;
  
  --baseFontSizeMax: 1.3125rem;
  --ratioMax: var(--perfectFourth);
  --maxViewportWidth: 1800;
  
  /* Apply dampening to the ratio spread */
  --compressedRatioMin: calc( 1 + (var(--ratioMin) - 1) * var(--ratio-dampener) );
  --compressedRatioMax: calc( 1 + (var(--ratioMax) - 1) * var(--ratio-dampener) );
  
  /* Min and max sizes of the scales are calculated based 
  on the provided ratio with pow(): 
  https://developer.mozilla.org/en-US/docs/Web/CSS/pow */
  
  /* Scale min */
  --size-min-4: calc(var(--baseFontSizeMin) * pow(var(--compressedRatioMin), 4));
  --size-min-3: calc(var(--baseFontSizeMin) * pow(var(--compressedRatioMin), 3));
  --size-min-2: calc(var(--baseFontSizeMin) * pow(var(--compressedRatioMin), 2));
  --size-min-1: calc(var(--baseFontSizeMin) * pow(var(--compressedRatioMin), 1));
  --size-min-0: calc(var(--baseFontSizeMin) * pow(var(--compressedRatioMin), 0));
  
  /* Scale max */
  --size-max-4: calc(var(--baseFontSizeMax) * pow(var(--compressedRatioMax), 4));
  --size-max-3: calc(var(--baseFontSizeMax) * pow(var(--compressedRatioMax), 3));
  --size-max-2: calc(var(--baseFontSizeMax) * pow(var(--compressedRatioMax), 2));
  --size-max-1: calc(var(--baseFontSizeMax) * pow(var(--compressedRatioMax), 1));
  --size-max-0: calc(var(--baseFontSizeMax) * pow(var(--compressedRatioMax), 0));

  /* This is how the fluid value is calculated: calc([value-min] + ([value-max] - [value-min]) * ((100vw - [breakpoint-min]) / ([breakpoint-max] - [breakpoint-min]))) */
  
  --fluid-4: clamp(var(--size-min-4), var(--size-min-4) + ( var(--size-max-4) - var(--size-min-4) ) * ((100vw - var(--minViewportWidth)) / (var(--maxViewportWidth) - var(--minViewportWidth))), var(--size-max-4));
  --fluid-3: clamp(var(--size-min-3), var(--size-min-3) + ( var(--size-max-3) - var(--size-min-3) ) * ((100vw - var(--minViewportWidth)) / (var(--maxViewportWidth) - var(--minViewportWidth))), var(--size-max-3));
  --fluid-2: clamp(var(--size-min-2), var(--size-min-2) + ( var(--size-max-2) - var(--size-min-2) ) * ((100vw - var(--minViewportWidth)) / (var(--maxViewportWidth) - var(--minViewportWidth))), var(--size-max-2));
  --fluid-1: clamp(var(--size-min-1), var(--size-min-1) + ( var(--size-max-1) - var(--size-min-1) ) * ((100vw - var(--minViewportWidth)) / (var(--maxViewportWidth) - var(--minViewportWidth))), var(--size-max-1));
  --fluid-0: clamp(var(--size-min-0), var(--size-min-0) + ( var(--size-max-0) - var(--size-min-0) ) * ((100vw - var(--minViewportWidth)) / (var(--maxViewportWidth) - var(--minViewportWidth))), var(--size-max-0));
}

/* And this is how we could apply the sizes */
  
h1 {
	font-size: var(--fluid-4);
}
	
h2 {
	font-size: var(--fluid-3);
}
  
h3 {
	font-size: var(--fluid-2);
}
  
h4 {
	font-size: var(--fluid-1);
}
  
p {
	font-size: var(--fluid-0);
}

Did I mention that it looks a bit overwhelming? 😂 Have a look at this CodePen prototype – which goes even further – to see it in action:

https://codepen.io/matthiasott/pen/RNrLVbP

Now, as I mentioned, the cool thing is, that if we don’t change the base font size, the fluid type works as usual.

Screnshot of the Fluid Type CodePen at the default font size of 16px

But when we increase the base font size to 24px – for example, by setting Chrome’s “Appearance” → “Font size” option to Very Large – the type scale also responds as expected: the ratio becomes smaller, large headings no longer grow disproportionately, and the body text scales up nicely. And the nice thing is that scaling up our body copy to 200 % by zooming in the browser also works, because our fluid type calculations are still based on rem units.

Screenshot of the CodePen with the base font size set to 24px and a smaller ratio

That said, it’s worth noting that this approach currently is an experiment and does not fully satisfy the WCAG SC 1.4.4 Resize Text accessibility requirement, because although the smaller non-display sizes can be scaled up to 200 %, the larger headings can’t. So if you are considering using such an approach in production and you need to fulfil the WCAG accessibility, either don’t do it or provide a way to scale up the headings as well. Thanks to Adrian Roselli for the hint. Also, Safari handles zoom and font scaling slightly differently from Chromium or Firefox. Larger font sizes can appear more exaggerated there, likely due to the way Safari recalculates computed styles during zoom. So always be testing.

And that’s it. The first iteration of this idea. It’s definitely still experimental, but I wanted to share it in case others find it useful or inspiring. Maybe it sparks new ideas about how to make fluid type more adaptive and user-friendly. And I guess it will mature in my head over time – and if you know of someone who has explored something similar (or even better), I’d love to hear about it. There’s nothing new under the sun, you know…

Ultimately, it really feels like CSS has reached a point where we no longer need to chase the perfect solution for things like fluid typography. There’s no single technique to rule them all. Instead, we have an ever-growing toolkit that lets us be creatively combine approaches and bring together design and engineering to tailor our solutions to fit each project’s needs. And, of course, experiment with fluid type on our personal sites.

This is post 12 of Blogtober 2025.

~

52 Webmentions

14 Reposts

22 Likes

ⓘ Webmentions are a way to notify other websites when you link to them, and to receive notifications when others link to you. Learn more about Webmentions.