104

I want to specify my font size using vw, as in

font-size: 3vw;

However, I also want to limit the font size to say 36px. How can I achieve the equivalent of max-font-size, which does not exist--is the only option to use media queries?

Danield
  • 121,619
  • 37
  • 226
  • 255

5 Answers5

200

font-size: 3vw; means that the font size will be 3% of the viewport width. So when the viewport width is 1200px - the font size will be 3% * 1200px = 36px.

So a max-font-size of 36px can be easily implemented using a single media query to override the default 3vw font-size value.

Codepen demo (Resize Browser)

div {
  font-size: 3vw;
}
@media screen and (min-width: 1200px) {
  div {
     font-size: 36px;
  }
}
<div>hello</div>

Update: With the new CSS min() function, we can simplify the above code - without using media queries (caniuse)

div {
  font-size: min(3vw, 36px);
}

In the above example, the font-size will be at most 36px, but will decrease to 3vw if the the viewport is less than 1200px wide (where 3vw computes to a value less than 36px )


That being said, using viewport units for font-size in the above way is problematic because when the viewport width is much smaller - say 320px - then the rendered font size will become 0.03 x 320 = 9.6px which is very (too) small.

In order to overcome this problem, I can recommend using a technique called Fluid Type AKA CSS Locks.

A CSS lock is a specific kind of CSS value calculation where:

  • there is a minimum value and a maximum value,
  • and two breakpoints (usually based on the viewport width),
  • and between those breakpoints, the actual value goes linearly from the minimum to the maximum.

So let's say we want to apply the above technique such that the minimum font-size is 16px at a viewport width of 600px or less, and will increase linearly until it reaches a maximum of 32px at a viewport width of 1200px.

This can be represented as follows (see this CSS-tricks article for more details):

div {
  font-size: 16px;
}
@media screen and (min-width: 600px) {
  div {
    font-size: calc(16px + 16 * ((100vw - 600px) / 600));
  }
}
@media screen and (min-width: 1200px) {
  div {
    font-size: 32px;
  }
}

Alternatively, we could use this SASS mixin which does all of the math for us so that the CSS would look something like this:

/* 
     1) Set a min-font-size of 16px when viewport width < 600px
     2) Set a max-font-size of 32px when viewport width > 1200px and
     3) linearly increase the font-size from 16->32px 
     between a viewport width of 600px-> 1200px 
*/

div {
  @include fluid-type(font-size, 600px, 1200px, 16px, 32px);
}

// ----
// libsass (v3.3.6)
// ----

// =========================================================================
//
//  PRECISE CONTROL OVER RESPONSIVE TYPOGRAPHY FOR SASS
//  ---------------------------------------------------
//  Indrek Paas @indrekpaas
//
//  Inspired by Mike Riethmuller's Precise control over responsive typography
//                                                                         
//
//  `strip-unit()` function by Hugo Giraudel
//  
//  11.08.2016 Remove redundant `&` self-reference
//  31.03.2016 Remove redundant parenthesis from output
//  02.10.2015 Add support for multiple properties
//  24.04.2015 Initial release
//
// =========================================================================

@function strip-unit($value) {
  @return $value / ($value * 0 + 1);
}

@mixin fluid-type($properties, $min-vw, $max-vw, $min-value, $max-value) {
  @each $property in $properties {
    #{$property}: $min-value;
  }

  @media screen and (min-width: $min-vw) {
    @each $property in $properties {
      #{$property}: calc(#{$min-value} + #{strip-unit($max-value - $min-value)} * (100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)});
    }
  }

  @media screen and (min-width: $max-vw) {
    @each $property in $properties {
      #{$property}: $max-value;
    }
  }
}

// Usage:
// ======

// /* Single property */
// html {
//   @include fluid-type(font-size, 320px, 1366px, 14px, 18px);
// }

// /* Multiple properties with same values */
// h1 {
//   @include fluid-type(padding-bottom padding-top, 20em, 70em, 2em, 4em);
// }

////////////////////////////////////////////////////////////////////////////

div {
  @include fluid-type(font-size, 600px, 1200px, 16px, 32px);
}
@media screen and (max-width: 600px) {
  div {
     font-size: 16px;
  }
}
@media screen and (min-width: 1200px) {
  div {
     font-size: 36px;
  }
}
<div>Responsive Typography technique known as Fluid Type or CSS Locks. 
  Resize the browser window to see the effect.
</div>

Codepen Demo


Update: We can use the new clamp() CSS function (caniuse) to refactor the above code to simply:

div {
  font-size: clamp(16px, 3vw, 32px);
}

see MDN:

clamp() allows you to set a font-size that grows with the size of the viewport, but doesn't go below a minimum font-size or above a maximum font-size. It has the same effect as the code in Fluid Typography but in one line, and without the use of media queries.

p { font-size: clamp(1rem, 2.5vw, 1.5rem); }
<p>
If 2.5vw is less than 1rem, the font-size will be 1rem.
If 2.5vw is greater than 1.5rem, the font-size will be 1.5rem.
Otherwise, it will be 2.5vw.
</p>

--


Further Reading

Fluid Typography

How Do You Do max-font-size in CSS?

Fluid Responsive Typography With CSS Poly Fluid Sizing

Non-linear interpolation in CSS

Danield
  • 121,619
  • 37
  • 226
  • 255
  • 1
    @Danield Thanks for this great answer. I learned a lot from this. I wonder why you need to add the extra `@media` queries for `max-` and `min-width`. Isn't it enough to just use the fuild-type mixin only? – mesqueeb Jan 25 '18 at 03:16
  • 1
    @mesqueeb You are totally right. Those media queries weren't necessary because they are taken care of in the SASS mixin. I have updated the post and the relevant codepen. Thanks! – Danield Jan 25 '18 at 08:09
  • 1
    I use the polyfluid sizing one regularly, and it's great for scaling multiple things at once and keeping everything in check. Honestly, the fact it works is incredible. – Alex McCabe Dec 02 '19 at 20:24
  • @AlexMcCabe That polyfluid sizing one looks great, thanks! The clamp attribute looks like it does not allow all adjustments (linear interpolation implies 2 parameters). – Bertiewo Apr 28 '20 at 21:06
58

Here is another idea. The calc function uses double precision float. Therefore it exhibits a step function near 1e18. For example,

width: calc(6e18px + 100vw - 6e18px);

This will snap to values 0px, 1024px, 2048px, etc. see pen https://codepen.io/jdhenckel/pen/bQNgyW

The step function can be used to create abs value and min/max with some clever maths. For instance

max(x, y) = x - (x + y) * step(y - x)

Given step(z) is zero when z<0 and one otherwise.

just an idea, not very practical, but maybe fun to try.


(Caution: this technique depends on an implementation detail that is not in any specification; currently, it works in Chrome and Safari, but not in Firefox, Edge or Internet Explorer, which don’t use double-precision floats for CSS values.)


UPDATE: this post is no longer useful (was it ever?) since CSS now supports min, max, and clamp.

John Henckel
  • 10,274
  • 3
  • 79
  • 79
  • 1
    This has nothing to do with the question (interesting behavior though) – Sjeiti Jan 03 '19 at 21:41
  • 16
    This has _everything_ to do with the question. `font-size: max(3vw, 36px)` would be exactly what was asked, and this shows how to make `max(x, y)` a thing given clever maths, and doesn't even use media queries, which was a secondary (implicit) point in the question. – Félix Saparelli Dec 01 '19 at 14:01
  • Interesting. This seems to work in Chrome and Safari, but not Firefox, or at least not recent versions of FF. Looks like an artifact of the underlying rendering engine. I'm guessing if this used to work in FF, the Quantum engine fixed it. – Keith Gaughan Dec 02 '19 at 02:12
  • 3
    I'm upvoting this answer as it's very clever, but I would never use it in production as it's *very* obscure. Also, as a previous comment pointed out, not actually reliable. – MaxArt Dec 02 '19 at 08:56
  • Probably it doesn't work in Firefox because Firefox uses 32-bit numbers rather than 64-bit as Chrome does? – Ilya Streltsyn Dec 02 '19 at 10:06
  • 1
    Christ... I love this. No idea how it works, but nonetheless. – Alex McCabe Dec 02 '19 at 20:25
36

Another way increases font size slowly, this will not limit max font size, but even on very wide screens, it will look better. Does not answer question in perfect way, but its 1 line...

font-size: calc(16px + 1vw);

Update: CSS improved and i recommend using clamp(min, preferred, max) function:

font-size: clamp(12px, 2vw, 20px);
ViliusL
  • 4,589
  • 26
  • 28
4

At some point, the font-size exceeds the 36px scale right, find that. Assuming, it exceeds when the width: 2048px:

@media screen and (min-width: 2048px) {
  .selector {
     font-size: 36px;
  }
}

Yes, unfortunately you need to use the @media queries. I hope that doesn't affect anything.

Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
1

According to this website (there are ads in this site), If you don't want to use clamp():

font-size: 24px;  

font-size: min(max(3.5vw, 16px), 40px);

Line one for IE.
Line two for others, means font-size:3.5vw, max-font-size:40px, min-font-size:16px.

c j
  • 50
  • 7