The concept of Fluid Typography was tossed around a couple of years ago. The main idea is that if you know what size your font is at two different viewport sizes, then you can have the font scaling smoothly between the two sizes. We had a jQuery solution for this in FitText (meant of headings, of course) until the calc()
function was shipped giving us a pure CSS solution.
p {
font-size: calc(16px + (24 - 16)*(100vw - 400px)/(800 - 400));
}
The important numbers here are 24px (the larger font up to 800px viewports) and 16px (the smaller font size down to 400px viewports). I wouldn’t use the terms “minimum” or “maximum” to describe font sizes and viewports in this context because it is a little misleading. In fact, you still need to provide a default font size for viewports smaller than 400px and bigger than 800px — otherwise, the font will keep getting smaller (or bigger) with the same scale of the equation. Or, if you are fancy, you could define another scale for bigger screen sizes.
It works really well and you should definitely check the math behind this.
Padding and line height?
I liked the concept of Fluid Typography so much that I asked myself if it could work with other properties. And it does! You can’t use a percentage, but as long as you stick with px, em or rem units, then it’s OK. I’m a pixel guy, so all my experiments have used those, but there is this neat generatorthat helps you with conversions if you need it.
So, back to padding
and line-height
. Using the same calc()
logic, we can achieve a fully fluid *layout* with fixed values for defined screen sizes.
I implemented this idea on this website, but kept it to font-size
and line-height
. And, yes, it gets easier to look past all that math and put to use the more you work with it.
A digression about “Hero” components
If you’re like me, then you might take issue with what we all have come to know as the hero component. It’s ubiquitous to the extent that it’s become a staple in design systems like Bootstrap.
My main gripe concerns the best way to make them responsive. If it’s not a *full screen* hero (i.e. takes over the entire viewport at any size), then I usually ask the designer how the page should behave. There’s often a proportion of the hero page that works fine, so that allows me to use padding-bottom
in % with absolute positioning of the inner content. That tactic works most of the time,
This is fun and it works fine especially on the desktop version of a website. You end up with a neat hero section, the proportion is good and the content is centered.
But what happens when you start shrinking the viewport? The hero remains readable up to a point, you really need to change the proportion.
Assuming we are working with a desktop-first responsive approach, we could start with a horizontal rectangle that scales down to the point where we’re left with a vertical rectangle on small screens.
This is a PITA because you could end up with many lines of CSS to have a nice and readable hero section at various breakpoints.
There has to be a better way, right? What if the hero could increase its height while the page width gets narrower?
Back to fluidity!
So, I turned back to the calc()
function that worked in those other situations, like making the browser handle the math and scale things accordingly as the viewport changes.
Here’s the CSS from the Fluid Typography example we started with:
p {
width: 100%;
max-width: 1200px;
margin: 0 auto;
font-family: 'Open Sans', sans-serif;
font-size: calc(24px + (18 - 24)*(100vw - 400px)/(1200 - 400));
line-height: 1.5;
padding: 10px;
}
Here’s what we want: a hero component that gets bigger while you shrink the page. I used pixel units for the fluid part and percentages everywhere else.
Here’s a screencast of how the solution I landed on:
This is pretty useful when you want to give more vertical space to the text. Shrinking the viewport width from large to small will end up having more lines for the same text, since the font size can’t be too small.
Pretty nice, right? Yet another way calc()
proved it could solve a scenario for me.
Yes, there are some caveats
Browser support is very good, tallying almost 93% of the users (at the time of writing), with the main exception being Opera mini.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
19* | 4* | 11 | 12 | 6* |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
126 | 127 | 126 | 6.0-6.1* |
Also, remember that this calc()
technique supports only px, em, and rem units. But the examples we covered here are pretty easy to convert units for things like padding-bottom
percentages to pixels since the hero is typically 100% in width.
Oh! And remember to reset your values before and after the breakpoints in the calc()
function. Otherwise you’ll end up with either very big or very small values for the target properties.
What say you?
This is probably just one way we can handle the situation and it was primarily driven by my interest in the calc()
function. So, that begs the question: how have you handled scaling hero component height? Have you put calc()
to use for it? Do you prefer wrangling breakpoints at various widths? Is there something else you use? Light up the comments!
You should definitely have a look at this use of calc and vm in this post. https://medium.com/@jakobud/css-polyfluidsizing-using-calc-vw-breakpoints-and-linear-equations-8e15505d21ab That has really been an eye opener and I use the poly fluid approach for type but also for components/elements and everything just flows fully fluidly and beautifully.
This approach has also taught me to approach design requirements with a different perspective. Meaning I get an image of what things should look like at vw 320 and at vw 1024, but between these 2 images, most of the time it is up to me to decide how things scale between these two points and this is exactly when poly fluid sizing does a great job.
I have written some proportionally sized hero elements before without needing all of the maths or breakpoints.
There are probably situations that your approach is more adept at handling, but there is also an amount of complexity gained from having all of that extra code that may not be always required.
I also work within a team where I need to be able pass my styling off to other team members, and if I pass over complex calcs like that, readability/understandability can be an issue.
Hi Timothy, thanks for the follow up. The thing is that with your code the hero gets smaller while you shrink the page. My goal was to have it taller while shrinking the page. I you want to work without media queries you could do something like this:
Clearly, if you need something simpler no
calc()
is needed but, as far as I know, there is no other (simple) way to have something grow while the page is getting smaller.Why bother absolute positioning your readable content? Instead of giving the image background a height, give the content control over the height, then you don’t need any calc at all.
Use grid/flexbox and place-content: center with a min-height (if you feel like it), and some padding for pretties. Then no height is needed at all on any break point. I think you made this harder than it needs to be.
Hi Vanderson, I agree that this is a borderline scenario. The main idea was to use calc() with “inverted values” so something gets larger while the page is getting smaller. But what if I need the hero component to be proportional when the screen gets larger than x?
This is probably a big hack, but without any media queries, you can use flex to set the height of the hero to an intrinsic aspect ratio when the content fits comfortably, and when it doesn’t (smaller screens), the content height will set the hero height. Basically, at any given screen size, the taller item wins. The only real downside (I know of) is that you need an empty div (spacer) for it to work in all browsers, including IE.
I like this method because you don’t have to have any idea or restrictions on the amount of content at any screen size, and it can be applied to all kinds of components beyond just heros – split columns, galleries, etc. It just kinda works.
I haven’t looked deeper into the cause, but the fluid typography examples do not work in Safari. (v.12.0. on MacOS 10.13.6)
How often do people resize their browsers? Mobile users don not, Windows users maximizes their browsers, Mac usually set to a favorite size. The only people who do (and I know) are the devs testing for different viewports.
Hi Michail! It’s absolutely true that the only people that resize their browsers are us devs :) But since you can’t predict the actual size of the screen that the user is going to have I think it’s best to have all the sizes covered. And so a lot of time is spent resizing the browsers.
On mobile devices and tablets, some people consume content in landscape orientation some of the time. Rotating a device between portrait and landscape is akin to resizing a desktop browser.
“remember to reset your values before and after the breakpoints in the calc() function”
Would you explain more about this, or give an example?
Hey Martino! This is an awesome technique. Never thought of using the fluid typography approach for other properties.
The application on padding and line-height seems a little overkill to me (I’d just use em and unitless respectively), but totally loved the responsive height on the “hero” component. kudos.
Why using position absolute instead of flexbox to center the elements?
I would like to see this technique using flexbox and not position absolute.
I always use https://websemantics.uk/tools/responsive-font-calculator/ for generating fluid type, it’s absolutely brilliant and a massive headache avoider and time saver.
Thank you!
Can you explain the mathematics behind the equation in more detail? For example, say I want a shorter fluid hero.
Nice article and thanks for the formula which works really well in most cases.
I’ve play with and made a little scss-mixin to test and simplify the use, redaction…
I really hate sites that make font size smaller on smaller screens. It makes no sense! If anything, font should be larger on a mobile device.
The pixels on a mobile device are already smaller. These “fluid typography” tricks are usually just a way for a developer to show off his “responsive” skills.
Wouldn’t that be more an issue with the implementation than the method?
Totally right. Hence I do it the other way round. Often the curve goes from big (small screen) to medium (medium screen) and then back to big on super large screens. People do not realize how much easier it is to intake information with the font size being easily legible. I can only encourage devs to be daring and not go under 18px on the smallest screen. My experience so far has been that initially this seems huge, though when you get used to it and find a site that has 12px on a 320px wide screen you really have to squint and have a hard time reading the content. Go big or go fish! ;)
I took a stab at something like this awhile back and ended up being interested in controlling an aspect ratio for Mobile, a different aspect ratio for Desktop, and then using Calc to move form one to the other. I did it with the element’s height and used min- and max- to set my “locks”: https://codepen.io/jhogue/pen/ZpNqZm
gentelmen please stop hacking and using vw, vh units for font sizes(its really rare case when it needed), I know a lot of developers did manage to start using this hack unfortunatly it cauzes a lot of problems and dont play well on different devices.
If you define font size in pixel value it will not be on small full hd mobile device screen size 16px big.
there is happening this kind of magic on mobile devices. pixels will be converted by default to the cm and mm.
https://www.unitconverters.net/typography/pixel-x-to-centimeter.htm
if you will find some screen where everything should be larger or smaller (for example smart tv browser) just do zoom adjustment with media query.
if you have screen like this
https://www.overclockers.co.uk/aoc-agon-ag352ucg-35-3440×1440-ips-g-sync-100hz-widescreen-ultrawide-curved-led-monitor-black-si-mo-04f-ao.html
and font size is based on vieewPortWidth it will be really large and page will be broken.
if you are sure that want to see font across all screens same size use cm or mm value and it always will be. Its not css trick its css hacks what you are doing