Build better responsive designs in less code.

Scale utilities smoothly between breakpoints with CSS clamp().

index.html
<main class="~p-6/10 p-6 md:p-8 lg:p-10">
<div class="max-w-4xl mx-auto">
<div class="grid grid-cols-2 gap-4 ~mb-4/6 mb-4 md:mb-5 lg:mb-6">
<img src="/beach-house.jpg" alt="" class="w-full h-52 object-cover rounded-lg" />
<img src="/beach-house-interior-1.jpg" alt="" class="w-full h-52 object-cover rounded-lg" />
</div>
<div class="flex gap-4 items-center">
<div class="flex-1">
<span class="text-sm leading-4 font-medium text-slate-500 dark:text-slate-400">Entire house</span>
<p class="block ~text-lg/2xl text-lg md:text-xl lg:text-2xl font-semibold text-slate-900 dark:text-white">Beach House in Collingwood</p>
</div>
<button type="button" class="bg-slate-900 flex gap-1 items-center dark:bg-white text-white dark:text-slate-900 text-sm leading-6 font-medium p-2 lg:px-3 rounded-lg">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z" />
</svg>
<span class="hidden lg:inline">Save</span>
</button>
</div>
<dl class="text-xs font-medium flex items-center">
<dt class="sr-only">Reviews</dt>
<dd class="flex items-center">
<svg width="24" height="24" fill="none" aria-hidden="true" class="mr-1 stroke-current">
<path d="m12 5 2 5h5l-4 4 2.103 5L12 16l-5.103 3L9 14l-4-4h5l2-5Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<span>4.89 <span class="text-slate-400 font-normal">(128)</span></span>
</dd>
<dt class="sr-only">Location</dt>
<dd class="flex items-center">
<svg width="2" height="2" aria-hidden="true" fill="currentColor" class="mx-3 text-slate-300">
<circle cx="1" cy="1" r="1" />
</svg>
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1 text-slate-400 dark:text-slate-500" aria-hidden="true">
<path d="M18 11.034C18 14.897 12 19 12 19s-6-4.103-6-7.966C6 7.655 8.819 5 12 5s6 2.655 6 6.034Z" />
<path d="M14 11a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z" />
</svg>
Collingwood, Ontario
</dd>
</dl>
<p class="mt-4 text-sm leading-6 dark:text-slate-400">
This sunny and spacious room is for those traveling light and looking for a comfy and cosy place to lay their head for a night or two. This beach house sits in a vibrant neighborhood littered with cafes, pubs, restaurants and supermarkets and is close to all the major attractions such as Edinburgh Castle and Arthur's Seat.
</p>
</div>
</main>
build.css
.\~p-6\/10 {
padding: clamp(1.5rem, 1.15rem + 1.74vw, 2.5rem)
}
.\~text-lg\/2xl {
font-size: clamp(1.125rem, 0.9rem + 0.75vw, 1.5rem);
line-height: clamp(1.75rem, 1.6rem + 0.5vw, 2rem)
}
/* ... */

Features

Installation

  1. Install the package

    Install fluid-tailwind via npm.

    Terminal window
    npm install -D fluid-tailwind
  2. Add the plugin and extractor

    The custom extractor lets you use the new ~ modifier in your Tailwind classes.

    tailwind.config.js
    import fluid, { extract } from 'fluid-tailwind'
    export default {
    content: {
    files: [/* ... */],
    extract
    },
    // ...
    plugins: [
    fluid
    ]
    }
  3. Continue reading to the end of the limitations section

    There may be additional installation steps depending on your Tailwind configuration.

Basic usage

Your browser isn't wide enough to see the full effect

<button class="bg-sky-500 ~px-4/8 ~py-2/4 ~text-sm/xl ...">Fluid button</button>

Here’s a quick overview:

  • The ~ modifier makes a utility fluid
  • Fluid utilities scale between their start/end values when the viewport is between the start and end breakpoints
  • The start and end breakpoints default to the smallest and largest screen, but they can be customized or overridden per-utility

Limitations

Due to CSS restrictions, fluid utilities require the start/end values and breakpoints to be length literals (i.e. 1rem) with the same unit.

Values with different units

<h1 class="~p-[1rem]/[18px]">

Values with different units than breakpoints

<h1 class="~text-[1rem]/[2rem]">
tailwind.config.js
export default {
// ...
theme: {
screens: {
'sm': '320px',
'2xl': '1280px'
}
}
// ...
}

Non-literal values like calc()

<h1 class="~text-base/[calc(1.5rem-2px)]">

Non-lengths like colors

<h1 class="~text-white/red-500">

Tailwind’s default theme

Tailwind v3’s default breakpoints are in px, while its default spacing and font sizes are in rem. This doesn’t bode well with the CSS restrictions mentioned above, so fluid-tailwind provides rem replacements for the default breakpoints:

tailwind.config.js
import fluid, { extract, screens } from 'fluid-tailwind'
export default {
// ...
theme: {
screens
},
// ...
}

Similarly, the line heights used for Tailwind’s default font sizes 5xl9xl are unitless, which can’t be fluidized. fluid-tailwind provides rem replacements for those as well:

tailwind.config.js
import fluid, { extract, screens, fontSize } from 'fluid-tailwind'
export default {
// ...
theme: {
screens,
fontSize
},
// ...
}

Troubleshooting

Tailwind doesn’t currently provide any error reporting tools for plugins, so if a fluid utility fails, it will output an empty rule with a comment containing the reason:

.\~mt-\[10px\]\/\[1\.5rem\] /* error - Start `10px` and end `1.5rem` units don't match */ {}

You should be able to see this if you have the official IntelliSense plugin installed and hover over the class. Otherwise, you can see it if you click the location for the empty rule in your browser’s inspector.

View fluid errors in the inspector

Configuration

Custom default breakpoints

The default start/end breakpoints can be set with a tuple of length literals [start, end]. Either can be omitted, in which case the plugin will use your smallest and largest breakpoint, respectively.

tailwind.config.js
// ...
export default {
// ...
theme: {
/** @type {import('fluid-tailwind').FluidThemeConfig} */
fluid: ({ theme }) => ({
defaultScreens: ['20rem', theme('screens.lg')]
})
},
// ...
}

Advanced

Customize breakpoints per-utility

You can customize the start/end breakpoints for a fluid utility with the included ~ variant. For example:

Your browser isn't wide enough to see the full effect

Quick increase
<h1 class="~md/lg:~text-base/4xl">Quick increase</h1>

You can omit either start or end breakpoint to use your defaults:

Set start breakpoint to md, end breakpoint to default

<div class="~md:~text-base/4xl">

Set end breakpoint to lg, start breakpoint to default

<div class="~/lg:~text-base/4xl">

Arbitrary start breakpoint

If you want to set an arbitrary start breakpoint with the ~ variant, you have to use ~min-[] (just as you’d have to use min-[] to set an arbitrary breakpoint):

Trying to use ~[]: to set an arbitrary start breakpoint

<div class="~[20rem]/lg:~text-base/4xl">

Using ~min-[] to set an arbitrary start breakpoint

<div class="~min-[20rem]/lg:~text-base/4xl">

Negative values

To negate fluid utilities, the dash comes after the fluid ~ modifier:

Negating a fluid utility

<div class="~-mt-3/5">

Container queries

If you have the official container query plugin installed, you can make fluid utilities scale between the nearest @container widths rather than screen breakpoints by using the ~@ variant:

<h1 class="~@md/lg:~text-base/4xl">Relative to container</h1>

This may look confusing if you use named containers. Sorry about that; there’s only so many ways to pass data into Tailwind. In general, when you see the fluid ~ modifier, you know the / denotes a start/end pair.

Just like the ~ variant, both start and end containers are optional and will use your defaults if unset.

Set end container to lg, start container to default

<div class="~@/lg:~text-base/4xl">

Custom default containers

The default containers can be set in the same way as breakpoints. Either can be omitted, in which case the plugin will use your smallest and largest container, respectively.

tailwind.config.js
// ...
export default {
// ...
theme: {
/** @type {import('fluid-tailwind').FluidThemeConfig} */
fluid: ({ theme }) => ({
defaultContainers: [, theme('containers.2xl')]
})
},
// ...
}

Fluid type accessibility errors

By default, the plugin will not generate fluid type that would fail WCAG Success Criterion 1.4.4. You can configure this with the checkSC144 option:

tailwind.config.js
// ...
export default {
// ...
plugins: [
fluid({
checkSC144: false // default: true
})
]
}

Combining with media queries

To really get crazy, you can combine fluid utilities with container or media queries, as such:

Your browser isn't wide enough to see the full effect

Whoa!
<h1 class="~/md:~text-base/4xl lg:~lg:~text-4xl/base">Whoa!</h1>

Here’s how this works:

  1. We scale our font-size between base and 4xl between our smallest and md breakpoints
  2. We scale in the opposite direction between our lg and largest breakpoints

Using with a custom prefix or separator

If you’re using a custom prefix or separator, you’ll need to pass them in to the extractor as well:

tailwind.config.js
import fluid, { extract } from 'fluid-tailwind'
export default {
prefix: 'tw-',
separator: '_',
content: {
files: [/* ... */],
extract: extract({
prefix: 'tw-',
separator: '_'
})
},
// ...
}
index.html
<div class="~tw-text-sm/xl">

Integrations

tailwind-merge

fluid-tailwind officially supports tailwind-merge via a plugin:

npm install --save @fluid-tailwind/tailwind-merge

For best results, include it after any other tailwind-merge plugins:

import { extendTailwindMerge } from 'tailwind-merge'
import { withFluid } from '@fluid-tailwind/tailwind-merge'
export const twMerge = extendTailwindMerge(/* ... */, withFluid)