Adding Safari 14 Support to Tailwind's Aspect Ratio

Henry Bley-Vroman, Former Senior UI Developer

Article Categories: #Code, #Front-end Engineering

Posted on

Writing an advanced Tailwind CSS's plugin to add backwards compatibility to the core aspect ratio plugin is pleasantly straight forward.

Tailwind CSS's aspect ratio plugin uses the aspect-ratio property, an exciting newish addition to CSS (MDN page). What front-end developers have been doing with extra elements or extra pseudoelements, tricky padding, and relative positioning which can have rippling effects complicating markup, now takes just a single style rule.

As long as the browsers you support support it.

For developers using Tailwind CSS v3 (so developers not supporting Internet Explorer; Tailwind CSS browser support docs) the notable risk is desktop Safari 14 and mobile Safari on iOS 14 (caniuse page for aspect-ratio). Global usage is dropping as more and more users upgrade to version 15, and it won't be too long before iOS 14 disappears from new contracts, visual QA checklists, and browserlist target configurations.

As of this writing, Viget is in a transitional phase: our UI developers — early Tailwind CSS adopters and still true believers — have become accustomed to the ease of using .aspect-… classes on the newest work where full iOS support starts at 15 (with very good to excellent experience on 14), but we do also still have active projects where iOS 14 is fully supported. On those projects we want to use Tailwind CSS's aspect ratio plugin, but we can't.

I recently happened on a single-selector aspect ratio solution that makes clever use of floated pseudoelements. Credit to https://github.com/takamoso/postcss-aspect-ratio-polyfill

el::before {
  content: "";
  float: left;
  padding-top: <100% / aspect ratio>;
}

el::after {
  clear: left;
  content: "";
  display: block;
}

Single-selector?? That got me thinking that a Safari 14-compatible aspect ratio plugin might be possible. Ideally it would

  • use the double-pseudoelement float solution for browsers which don't support the aspect-ratio CSS property (but which Tailwind CSS v3 supports; the plugin will not bring .aspect-... to Internet Explorer)
  • use the aspect-ratio CSS property for browsers which support it
  • use the aspectRatio Tailwind CSS configuration object
  • be easily removed when support for Safari <15 is dropped

Here's my solution, in the context of a Tailwind CSS configuration file:

// tailwind.config.js

module.exports = {
  // …
  corePlugins: {
    aspectRatio: false,
    // …
  },
  plugins: [
    ({ matchUtilities, theme /* … */ }) => {
      // …
      matchUtilities(
        // https://gist.github.com/olets/9b833a33d01384eed1e9f1e106003a3b
        {
          'aspect': (value) => ({
            '@supports (aspect-ratio: 1 / 1)': {
              aspectRatio: value,
            },
            '@supports not (aspect-ratio: 1 / 1)': {
              // https://github.com/takamoso/postcss-aspect-ratio-polyfill

              '&::before': {
                content: '""',
                float: 'left',
                paddingTop: `calc(100% / (${value}))`,
              },
              '&::after': {
                clear: 'left',
                content: '""',
                display: 'block',
              }
            },
          }),
        },
        { values: theme('aspectRatio') }
      )
    },
  ],
}

Try it and heart it on Codepen. Try it on Tailwind Play.

This is an advanced Tailwind CSS plugin. But a beauty of Tailwind CSS v3 is that even advanced plugins are legible. Here's how it works:

  • corePlugins({ aspectRatio: false }) disables the core aspect ratio plugin (corePlugins docs). The aspectRatio theme configuration is still intact, but no CSS will be generated based on it.

  • matchUtilities registers utilities that map to values defined in the user’s theme configuration (matchUtilities docs).

  • Skipping down to the end, { values: theme('aspectRatio') } says "this plugin's configuration lives in theme.aspectRatio".

  • Moving back up, the first matchUtilities argument is a key/value pair where the key is the class prefix and the value is a function expression which receives the configuration values.

    • 'aspect': says "this plugin will generate CSS for the classes .aspect-<configuration key>" (with the default theme.aspectRatio, that's the default Tailwind CSS aspect ratio classes .aspect-auto, .aspect-square, and .aspect-video).

    • Everything inside (value) => ({ ... }) is the CSS used in the generated classes, written in the Tailwind CSS CSS-in-JS idiom where selectors are keys and rules are key/value pairs. @supports checks for browser support of CSS features (MDN page). Note the content: '""'content: "" would compile to the CSS content: ;.

This plugin enhances Tailwind CSS aspect ratio classes with support for Safari <15. Use it on projects that support older versions of Safari. When the projects drop support for those browsers, remove the plugin and the corePlugins.aspectRatio; CSS weight will decrease with no markup changes necessary.

You're welcome to use this plugin in your own projects! Its license, which requires attribution, disallows commercial use without prior agreement, and disallows use in projects which violate human rights, can be found at the gist.

Related Articles