cssviewport-units

How to implement max-font-size?


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?


Solution

  • 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