Hiroki Yokoiwa
Hiroki Yokoiwa

Reputation: 355

Tailwind CSS fallback for new screen length types such as "lvh", "svh"

I wrote tailwind.config.js as bellows to use new CSS length types such as lvh, svh.

module.exports = {
  theme: {
    extend: {
      height: {
        "screen": "100dvh",
        "screen-small": "100svh",
        "screen-large": "100lvh"
      }
    }
  }
}

then it successfully exports

.h-screen {
    height: 100dvh;
}

But I want to get with fallback properties like

.h-screen {
  height: 100vh; /* fallback for Opera, IE and etc. */
  height: 100dvh;
}

Is there any nice way to export fallback properties with Tailwind CSS?

Upvotes: 22

Views: 10852

Answers (2)

rozsazoltan
rozsazoltan

Reputation: 7328

Since 2023, dynamic values have become part of the default template.

In general, Tailwind CSS v3.0 is designed for and tested on the latest stable versions of Chrome, Firefox, Edge, and Safari. It does not support any version of IE, including IE 11.


Source: Browser Support - Tailwind CSS Docs

Browsers released around 2022 started supporting dynamic viewport values. However, if you'd like to assign fallback values, you can use the CSS @supports and not operators to inject additional CSS, assigning fallback values to the mentioned 3 classes.

This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2015.


Source: @supports - MDN Docs

@supports not (height: 100dvh) {
  .h-dvh {
    height: 100vh;
  }

  /* ... */
}

With the help of a Tailwind CSS plugin, you can dynamically add fallback values for all screens:

TailwindCSS v3.4 or above

const plugin = require('tailwindcss/plugin');

module.exports = {
  plugins: [
    plugin(function ({ addUtilities }) {
      const fallbackHeightUtilities = {
        '@supports not (height: 100dvh)': {
          '.h-dvh': { height: '100vh' },
          '.min-h-dvh': { 'min-height': '100vh' },
          '.max-h-dvh': { 'max-height': '100vh' },
        },
        '@supports not (height: 100lvh)': {
          '.h-lvh': { height: '100vh' },
          '.min-h-lvh': { 'min-height': '100vh' },
          '.max-h-lvh': { 'max-height': '100vh' },
        },
        '@supports not (height: 100svh)': {
          '.h-svh': { height: '100vh' },
          '.min-h-svh': { 'min-height': '100vh' },
          '.max-h-svh': { 'max-height': '100vh' },
        },
      };

      addUtilities(fallbackHeightUtilities, ['responsive']);
    }),
  ],
};

TailwindCSS v3.3 or below

const plugin = require('tailwindcss/plugin');

module.exports = {
  plugins: [
    plugin(function ({ addUtilities }) {
      const heightUtilities = {
        '.h-dvh': { height: '100dvh' },
        '.h-lvh': { height: '100lvh' },
        '.h-svh': { height: '100svh' },

        '.min-h-dvh': { 'min-height': '100dvh' },
        '.min-h-lvh': { 'min-height': '100lvh' },
        '.min-h-svh': { 'min-height': '100svh' },

        '.max-h-dvh': { 'max-height': '100dvh' },
        '.max-h-lvh': { 'max-height': '100lvh' },
        '.max-h-svh': { 'max-height': '100svh' },
      };

      const fallbackHeightUtilities = {
        '@supports not (height: 100dvh)': {
          '.h-dvh': { height: '100vh' },
          '.min-h-dvh': { 'min-height': '100vh' },
          '.max-h-dvh': { 'max-height': '100vh' },
        },
        '@supports not (height: 100lvh)': {
          '.h-lvh': { height: '100vh' },
          '.min-h-lvh': { 'min-height': '100vh' },
          '.max-h-lvh': { 'max-height': '100vh' },
        },
        '@supports not (height: 100svh)': {
          '.h-svh': { height: '100vh' },
          '.min-h-svh': { 'min-height': '100vh' },
          '.max-h-svh': { 'max-height': '100vh' },
        },
      };

      addUtilities(heightUtilities, ['responsive']);
      addUtilities(fallbackHeightUtilities, ['responsive']);
    }),
  ],
};

Upvotes: 1

Ihar Aliakseyenka
Ihar Aliakseyenka

Reputation: 14183

Pass an array as property

module.exports = {
  theme: {
    extend: {
      height: {
        screen: ['100vh /* fallback for Opera, IE and etc. */', '100dvh'],
      }
    }
  }
}

This will generate

.h-screen {
  height: 100vh /* fallback for Opera, IE and etc. */;
  height: 100dvh;
}

DEMO

Note: I'm not 100% sure it is supposed way to do it as editor shows error because we are passing not string but array of strings.

Another way is to create utility with layer

@layer utilities {
  .h-my-screen {
    height: 100vh; /* fallback for Opera, IE and etc. */
    height: 100dvh;
  }
}

In case if you wish to use reserved h-screen name approach is similar but it will stack default (100vh) and new value (100dvh) in correct order you need - but it is just coincidence

@layer utilities {
  .h-screen {
    height: 100dvh;
  }
}

Same thing using Tailwind plugin

const plugin = require('tailwindcss/plugin')

/** @type {import('tailwindcss').Config} */
module.exports = {
  // ...

  plugins: [
    plugin(function ({ addBase, addComponents, addUtilities, theme }) {
      addUtilities({
        '.h-screen': {
          height: '100dvh',
        }
      })
    })
  ],
}

Created both way utility h-screen will now generate

.h-screen {
  height: 100vh;
  height: 100dvh;
}

Note: when using JS syntax object CANNOT contain similar keys (height in your case) but it may accept an array of values in defined order

// Wrong
addUtilities({
  '.h-my-screen': {
    height: '100vh /* fallback for Opera, IE and etc. */',
    height: '100dvh',
  }
})

// Correct
addUtilities({
  '.h-my-screen': {
    height: ['100vh /* fallback for Opera, IE and etc. */', '100dvh'],
  }
})

Upvotes: 35

Related Questions