Responsive Fluid Typography with Tailwind
--
Create smooth, responsive text that scales perfectly between breakpoints using CSS clamp().
CSS utility:
.text-fluid-sm { font-size: clamp(0.875rem, 0.8rem + 0.5vw, 1rem); /* Min: 14px, Preferred: scales with viewport, Max: 16px */}
.text-fluid-base { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); /* Min: 16px, Preferred: scales, Max: 18px */}
.text-fluid-lg { font-size: clamp(1.125rem, 1rem + 0.75vw, 1.5rem); /* Min: 18px, Max: 24px */}
.text-fluid-xl { font-size: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem); /* Min: 24px, Max: 36px */}
.text-fluid-2xl { font-size: clamp(2rem, 1.5rem + 2.5vw, 3rem); /* Min: 32px, Max: 48px */}
.text-fluid-3xl { font-size: clamp(2.5rem, 2rem + 3vw, 4rem); /* Min: 40px, Max: 64px */}Tailwind plugin:
import plugin from 'tailwindcss/plugin';
export default { // ... your config plugins: [ plugin(function ({ addUtilities }) { addUtilities({ '.text-fluid-sm': { 'font-size': 'clamp(0.875rem, 0.8rem + 0.5vw, 1rem)', }, '.text-fluid-base': { 'font-size': 'clamp(1rem, 0.9rem + 0.5vw, 1.125rem)', }, '.text-fluid-lg': { 'font-size': 'clamp(1.125rem, 1rem + 0.75vw, 1.5rem)', }, '.text-fluid-xl': { 'font-size': 'clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem)', }, '.text-fluid-2xl': { 'font-size': 'clamp(2rem, 1.5rem + 2.5vw, 3rem)', }, '.text-fluid-3xl': { 'font-size': 'clamp(2.5rem, 2rem + 3vw, 4rem)', }, }); }), ],};Usage:
<!-- Old way: Multiple breakpoints --><h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl"> Heading</h1>
<!-- New way: One class, smooth scaling --><h1 class="text-fluid-3xl"> Heading</h1>
<!-- Paragraph text --><p class="text-fluid-base"> This text smoothly scales from 16px to 18px based on viewport width.</p>Visual comparison:
Old approach (breakpoints):320px: 16px ─┐640px: 16px │ Jump!641px: 18px ─┘1024px: 18px
New approach (fluid):320px: 16.0px480px: 16.8px ← Smooth640px: 17.6px ← Smooth1024px: 18.0pxCalculate clamp values:
function fluidType(minSize, maxSize, minWidth = 320, maxWidth = 1280) { const minSizeRem = minSize / 16; const maxSizeRem = maxSize / 16; const slope = (maxSize - minSize) / (maxWidth - minWidth); const yAxisIntersection = -minWidth * slope + minSize; const yAxisIntersectionRem = yAxisIntersection / 16; const slopeVw = slope * 100;
return `clamp(${minSizeRem}rem, ${yAxisIntersectionRem.toFixed(2)}rem + ${slopeVw.toFixed(2)}vw, ${maxSizeRem}rem)`;}
console.log(fluidType(16, 24, 320, 1280));// Output: clamp(1rem, 0.67rem + 0.83vw, 1.5rem)Advanced: Fluid line height
.prose-fluid { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); line-height: clamp(1.5rem, 1.4rem + 0.75vw, 1.75rem); /* Line height scales proportionally */}Accessibility note:
/* Respect user's font size preferences */html { font-size: 100%; /* Don't override! */}
/* clamp() respects user zoom and font size settings ✅ */.text-fluid-base { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);}When to use:
- Headings that need to scale smoothly
- Hero text on landing pages
- Paragraph text for better readability
- Any text that looks awkward with breakpoint jumps
When NOT to use:
- UI elements with fixed sizes (buttons, badges)
- Small text where precision matters
- When you need exact sizes at breakpoints