<h1>h1: Almost before we knew it, we had left the ground.</h1>
<h2>h2: Almost before we knew it, we had left the ground.</h2>
<h3>h3: Almost before we knew it, we had left the ground.</h3>
<h4>h4: Almost before we knew it, we had left the ground.</h4>
// Learn more about what's going on here:
// @link https://dev.to/5t3ph/generating-font-size-css-rules-and-creating-a-fluid-type-scale-2553

// Select a ratio to preview
// Default is "perfectFourth"
$type-ratios: (
  "minorSecond": 1.067,
  "majorSecond": 1.125,
  "minorThird": 1.2,
  "majorThird": 1.25,
  "perfectFourth": 1.333,
  "augmentedFourth": 1.414,
  "perfectFifth": 1.5,
  "goldenRatio": 1.618
);

@function type-ratio($key) {
  @return map-get($type-ratios, $key);
}

// Recommended
$type-base-size: 1rem;

// Select by key of map, or use a custom value
$type-size-ratio: type-ratio("perfectFourth");

// List in descending order to prevent extra sort function
// Limited to high-touch heading styles
$type-levels: 4, 3, 2, 1;

// Create map with h[x] as key
// and computed font-size as value
$type-styles: ();
$level-size: $type-base-size;
@each $level in $type-levels {
  $level-size: $level-size * $type-size-ratio;
  $type-styles: map-merge($type-styles, (#{"h"}$level: $level-size));

  // Output heading styles
  // Assign to element and create utility class
  h#{$level},
  .h#{$level} {
    // Fallback for browsers that don't support min / max
    font-size: $level-size;

    // Recommendation courtesy of this brilliant work:
    // @link https://hugogiraudel.com/2020/05/18/using-calc-to-figure-out-optimal-line-height/
    line-height: calc(2px + 2ex + 2px);

    // Set with `em` to be relative to current `font-size`
    margin-bottom: 0.65em;

    // Limit in place based on testing -> smaller ratios are
    // prematurely or unnecessarily reduced
    @if ($type-size-ratio > 1.2) {
      // Fluid type styles

      // As of May 2020, min / max has better support than clamp
      // particularly for mobile devices

      // `unquote` is required due to built-in Sass min/max functions
      // incorrectly assuming intent

      // Remove unit for calculations
      $level-unitless: $level-size / ($level-size * 0 + 1);

      // Set minimum size to a percentage less than $level-size
      // Reduction is greater for large font sizes (> 4rem) to help
      // prevent overflow due to font-size on mobile devices
      $fluid-reduction: if($level-size > 4, 0.5, 0.33);
      $fluid-min: $level-unitless - ($fluid-reduction * $level-unitless);

      // Prevent dropping lower than 1rem (body font-size)
      $fluid-min: if($fluid-min > $type-base-size, $fluid-min, 1);

      // Adjust max modifier slightly per level to prevent "same" size
      // or lower levels appaering larger than higher levels
      // 4vw was selected by testing from $type-ratios map, YMMV 🙃
      $fluid-scaler: ($level-unitless - $fluid-min) + 4vw;

      font-size: unquote(
        "min(max(#{$fluid-min}rem, #{$fluid-scaler}), #{$level-size})"
      );
    }
  }
}

@function type-style($key) {
  @return map-get($type-styles, $key);
}

// Test retrieving heading style value from the map
// @debug 'h3:'#{type-style(h3)};

p,
li,
h1,
h2,
h3,
h4 {
  // Help prevent overflow of long words/names/URLs
  word-break: break-word;

  // Optional, not supported for all languages:
  hyphens: auto;

  // Clear top margin
  margin-top: 0;
}

* {
  box-sizing: border-box;
}

body {
  // Ensure base applied as default for all other elements
  font-size: $type-base-size;
  font-family: "Merriweather Sans", sans-serif;
  line-height: 1.5;

  // Demo styles
  min-height: 100vh;
  display: grid;
  place-content: center;
  margin: 0 2rem;
  color: #333;
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.