Skip to content

bzaman/fluid-text

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

FluidText

A lightweight, polymorphic React component that applies fluid typography using CSS clamp(). Font size scales smoothly between a minimum and maximum value as the viewport grows — no media queries needed.


Installation

There is no npm package — just copy the file directly into your project.

  1. Copy FluidText.tsx anywhere in your source tree, e.g. src/components/FluidText.tsx
  2. Import and use it:
import FluidText from './components/FluidText'

Prefer plain JavaScript? Copy FluidText.jsx instead — same behaviour, no TypeScript.

Requirements:

  • React 18 or 19
  • TypeScript is required for the .tsx version (generics power the polymorphic type safety); the .jsx version works in any JS project

That's it. No dependencies beyond React itself.


How it works

Under the hood, FluidText generates a single clamp() expression:

clamp(<min>px, calc(1vw * (<max> * 100 / <viewportWidth>)), <max>px)
  • Below the breakpoint the font stays at minFontSize.
  • Inside the range it scales linearly with the viewport.
  • Above viewportWidth it caps at maxFontSize.

Basic usage

import FluidText from './component/FluidText'

// Renders a <p> that scales from 16px → 32px across a 1440px viewport
<FluidText minFontSize={16} maxFontSize={32}>
  Hello, fluid world.
</FluidText>

Changing the element with as

FluidText is polymorphic — the as prop controls which HTML element is rendered. TypeScript automatically infers the correct props for each element.

// Headings
<FluidText as="h1" minFontSize={32} maxFontSize={72}>
  Page title
</FluidText>

<FluidText as="h2" minFontSize={24} maxFontSize={48}>
  Section heading
</FluidText>

// Inline elements
<FluidText as="span" minFontSize={14} maxFontSize={18}>
  Inline fluid text
</FluidText>

// Anchor — href is type-safe because as="a"
<FluidText as="a" href="https://example.com" minFontSize={14} maxFontSize={20}>
  A fluid link
</FluidText>

// Button — type and disabled are inferred correctly
<FluidText as="button" type="submit" disabled minFontSize={14} maxFontSize={18}>
  Submit
</FluidText>

// Label — htmlFor is available
<FluidText as="label" htmlFor="email" minFontSize={12} maxFontSize={16}>
  Email address
</FluidText>

Custom viewport width

The default viewport width is 1440px. Override it with viewportWidth:

// Scale relative to a 1280px design baseline
<FluidText minFontSize={16} maxFontSize={28} viewportWidth={1280}>
  Scaled to 1280px baseline
</FluidText>

// Tighter range for a mobile-first layout
<FluidText minFontSize={14} maxFontSize={20} viewportWidth={768}>
  Mobile-first fluid text
</FluidText>

Without fluid sizing

Omit minFontSize / maxFontSize entirely and the component becomes a simple polymorphic wrapper — useful when you want the fluid-text class or the as convenience without any size computation.

<FluidText as="p" className="body-copy">
  Static size, polymorphic element.
</FluidText>

Composing with className and style

The component always adds a fluid-text class so you can target it globally in CSS. Any className or style you pass is merged in.

// Extra class
<FluidText as="h1" minFontSize={32} maxFontSize={64} className="hero-title">
  Big title
</FluidText>

// Inline style — your values are spread after the computed fontSize,
// so they take precedence (e.g. to pin a specific size in one context)
<FluidText
  minFontSize={16}
  maxFontSize={32}
  style={{ color: 'rebeccapurple', letterSpacing: '0.02em' }}
>
  Styled fluid text
</FluidText>

Real-world example — a full typography scale

export function ArticlePage() {
  return (
    <article>
      <FluidText as="h1" minFontSize={32} maxFontSize={72} className="article-title">
        The Future of Web Typography
      </FluidText>

      <FluidText as="p" minFontSize={16} maxFontSize={20} className="article-lead">
        An introduction to fluid type and why it matters.
      </FluidText>

      <FluidText as="h2" minFontSize={22} maxFontSize={40}>
        Why CSS clamp?
      </FluidText>

      <FluidText as="p" minFontSize={15} maxFontSize={18}>
        The <code>clamp()</code> function lets a value scale between a minimum
        and maximum, relative to a viewport unit — with zero JavaScript.
      </FluidText>

      <FluidText as="a" href="/docs" minFontSize={14} maxFontSize={16}>
        Read the full docs →
      </FluidText>
    </article>
  )
}

Props

Prop Type Default Description
as ElementType 'p' HTML element to render
minFontSize number Minimum font size in px
maxFontSize number Maximum font size in px
viewportWidth number 1440 Viewport width (px) at which maxFontSize is reached
className string Merged with the automatic fluid-text class
style CSSProperties Merged with the computed fontSize (your values win)
...props element props All other props forwarded to the underlying element

Fluid sizing is applied only when both minFontSize and maxFontSize are provided.


Contributing

Got an idea to improve FluidText? PRs are very welcome.

Here are some directions the component could grow — pick one up or bring your own:

  • minViewport / maxViewport props — let callers control the breakpoint range rather than deriving it from viewportWidth alone
  • Line-height scaling — fluid line-height alongside fluid font size
  • forwardRef support — expose a ref to the underlying element
  • CSS custom properties output — optionally emit --fluid-min, --fluid-max variables for easier theming
  • Storybook stories — visual documentation and an interactive prop explorer
  • More test coverage — edge cases, zero values, negative guard rails

To open a pull request:

  1. Fork the repo and create a branch: git checkout -b feat/your-idea
  2. Make your changes and add tests for any new behaviour
  3. Run npm test and npm run lint — both must pass
  4. Open a PR with a short description of what you changed and why

All contributions, big or small, are appreciated.

About

Polymorphic React component for fluid typography using CSS clamp(). Zero dependencies, full TypeScript support.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors