Skip to content

koyev/fluidwind

Repository files navigation

Fluidwind 🍃

npm version npm downloads CI status license node version tailwindcss version

A Tailwind CSS plugin that replaces stair-step breakpoints with smooth, mathematically perfect fluid scaling using CSS clamp().


Why Fluidwind?

Standard Tailwind uses adaptive design: values jump at fixed breakpoints (sm:, md:, lg:). Fluidwind uses fluid design: values scale linearly between any two viewport widths with zero JavaScript at runtime.

<!-- Before: 3 classes, 2 hard jumps -->
<h1 class="text-2xl md:text-4xl lg:text-6xl">Hello</h1>

<!-- After: 1 class, perfectly smooth -->
<h1 class="fw-text-[24px-60px]">Hello</h1>

The CSS output is a single clamp() declaration:

font-size: clamp(1.5rem, 3.38vw + 0.77rem, 3.75rem);

Installation

npm install fluidwind

Add the plugin to your Tailwind config:

// tailwind.config.js
module.exports = {
  plugins: [require("fluidwind")],
};

With a custom remBase (if your root font-size is not 16px):

plugins: [require("fluidwind")({ remBase: 10 })],

Configuration

// tailwind.config.js
module.exports = {
  plugins: [require("fluidwind")],
  theme: {
    fluidwind: {
      // Default viewport range (used when no modifier is provided)
      defaultRange: ["375px", "1440px"],

      // Named ranges - reference them with the /name modifier
      ranges: {
        post: ["600px", "1000px"],
        wide: ["1024px", "1920px"],
      },

      // Named font-size scale — use as fw-text-md, fw-text-xl, etc.
      fontSize: {
        sm:    ["14px", "18px"],
        md:    ["16px", "24px"],
        lg:    ["20px", "32px"],
        xl:    ["28px", "48px"],
        "2xl": ["36px", "64px"],
      },

      // Shared spacing scale — drives padding, margin, gap, and sizing utilities
      spacing: {
        xs: ["4px",  "8px"],
        sm: ["8px",  "16px"],
        md: ["16px", "32px"],
        lg: ["24px", "64px"],
      },
    },
  },
};

Syntax

fw-{utility}-[{minValue}-{maxValue}]/{range}
Part Description Example
fw- Required prefix fw-
{utility} Any supported utility name text, p, bg
[{min}-{max}] Fluid range in any supported unit [16px-32px]
/{range} Optional - named or inline viewport range /post, /[400-1200]

Usage Examples

Typography

<!-- font-size: clamp(1rem, 1.5vw + ..., 2rem) -->
<h1 class="fw-text-[16px-32px]">Fluid Heading</h1>

<!-- Using rem units -->
<p class="fw-text-[1rem-1.5rem]">Fluid Body</p>

Spacing

<!-- padding -->
<section class="fw-p-[16px-64px]">...</section>

<!-- margin, shorthand and per-side -->
<div class="fw-mt-[8px-32px] fw-px-[16px-48px]">...</div>

Sizing

<div class="fw-w-[200px-800px] fw-h-[100px-400px]">...</div>

Negative Values

<!-- Fluid negative margin for overlapping layouts -->
<div class="fw-mt-[-20px--60px]">...</div>

Color Interpolation

Smoothly transition between two colors as the viewport grows:

<!-- background shifts from red → blue -->
<div class="fw-bg-[#ff0000-#0000ff]">...</div>

<!-- text shifts from black → white -->
<span class="fw-text-[#000000-#ffffff]">...</span>

Generated CSS:

background-color: color-mix(
  in srgb,
  #ff0000 calc(clamp(0, 100 * (1440 - tan(atan2(100vw, 1px))) / 1065, 100) * 1%),
  #0000ff
);

Range Modifiers

<!-- Named range from config -->
<p class="fw-text-[16px-24px]/post">Post body text</p>

<!-- Inline viewport range -->
<p class="fw-text-[16px-24px]/[400-1200]">Custom range</p>

Semantic Scales

Define named fluid values in your config and use them by key — no more repeating pixel ranges:

<!-- instead of fw-text-[16px-24px] everywhere -->
<h1 class="fw-text-xl">Heading</h1>
<p  class="fw-text-md">Body text</p>

<!-- spacing scale drives padding, margin, gap, and sizing -->
<section class="fw-p-lg fw-gap-sm">...</section>

Arbitrary [min-max] values still work alongside named keys — they are never replaced.

With Tailwind Breakpoints

Fluidwind utilities compose with standard Tailwind breakpoints:

<p class="fw-text-[14px-18px] lg:fw-text-[18px-28px]">...</p>

With tailwind-merge

Install tailwind-merge and wrap your setup with withFluidwind() so fw-* classes correctly conflict with — and override — standard Tailwind classes (and vice-versa):

import { extendTailwindMerge } from 'tailwind-merge'
import { withFluidwind } from 'fluidwind/tailwind-merge'

export const twMerge = extendTailwindMerge(withFluidwind())

// twMerge('p-4 fw-p-[1rem-2rem]')            → 'fw-p-[1rem-2rem]'
// twMerge('text-sm fw-text-[16px-32px]')     → 'fw-text-[16px-32px]'
// twMerge('fw-gap-[4px-16px] gap-8')         → 'gap-8'
// twMerge('fw-p-[4px-8px] fw-p-[8px-16px]') → 'fw-p-[8px-16px]'

tailwind-merge is an optional peer dependency — only needed if you use class merging.


Supported Utilities

Category Utilities
Typography fw-text-*
Padding fw-p-* fw-px-* fw-py-* fw-pt-* fw-pb-* fw-pl-* fw-pr-*
Margin fw-m-* fw-mx-* fw-my-* fw-mt-* fw-mb-* fw-ml-* fw-mr-*
Sizing fw-w-* fw-h-* fw-min-w-* fw-max-w-* fw-min-h-* fw-max-h-*
Layout fw-gap-* fw-gap-x-* fw-gap-y-*
Decoration fw-border-* fw-rounded-*
Color fw-bg-* fw-text-*

Supported Units

Values can be expressed in any combination of: px, rem, vw, %, or unitless (treated as px).

<div class="fw-p-[10px-2rem]">Mix and match units</div>

The Math

slope     = (vMax - vMin) / (wMax - wMin)
intercept = vMin - slope * wMin
output    = clamp(vMin_rem, slope×100vw + intercept_rem, vMax_rem)

All math runs in px internally; CSS output uses rem for accessibility (respects user font-size preferences).


Browser Support

All fluid numeric utilities rely on clamp() - supported in all modern browsers since 2020.

Color interpolation (fw-bg-*, fw-text-* with hex values) uses color-mix():

Chrome Firefox Safari Edge
111+ 113+ 16.2+ 111+

Contributing

Contributions are welcome. See CONTRIBUTING.md for setup instructions and guidelines.


Made with ❤️ by koyev