Skip to content
WANCRAFT

C.162Typography & Text

Read More

Line-clamped text that expands to its measured height, with a fade at the clamp and a button only when it overflows.

NewReactCSSTailwindintermediate

Overview

Read More clamps any text to a set number of lines and expands it with a smooth height animation measured from the real content — no max-height guesses, so the speed is right for one extra line or twenty. Collapsing animates from the full height to the exact clamped height and only then re-applies the clamp, so the ellipsis never appears mid-motion. A mask fades the last visible line into whatever background sits behind it. A ResizeObserver checks whether the clamp actually hides anything, and the Read more / Show less button only appears when it does. Optionally the whole collapsed block expands on click. Focus is preserved: if focus sits inside text that is about to be hidden, it moves to the toggle, and tabbing to a link hidden by the clamp expands the text.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/read-more.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/read-more.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/read-more.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { ReadMore } from "@/components/ui/read-more";

export function ReadMoreExample() {
  return (
    <ReadMore className="max-w-prose text-[15px] leading-relaxed">
      <p>
        The workshop opens at seven, before the street is awake. A chair takes eleven days
        from rough board to finished seat, and most of that time is spent waiting: for glue
        to cure, for oil to soak in, for the wood to settle after it has been asked to bend.
      </p>
    </ReadMore>
  );
}

03Props

PropTypeDefaultDescription
childrenReactNode—Required. The text; multiple paragraphs and inline markup are fine.
linesnumber3Lines shown while collapsed.
durationnumber380Expand / collapse duration in ms (0 = instant).
fadebooleantrueMask-fade the last visible line (works on any background).
expandOnClickbooleanfalseExpand when the collapsed text itself is clicked (links and selections are respected).
moreLabel / lessLabelstring"Read more" / "Show less"Button labels for the collapsed and expanded states.
expanded / defaultExpandedboolean— / falseControlled or initial state.
onExpandedChange(expanded: boolean) => void—Called when the reader toggles the text.
accentstring"#FF7448"Button hover and focus ring colour.
buttonClassNamestring—Extra classes for the toggle button.

04Features

  • 01Height animated between measured values (Web Animations API), interruptible mid-motion
  • 02The clamp and its ellipsis are re-applied only after the collapse finishes
  • 03Mask-based fade at the clamp edge that eases away as the text expands
  • 04Button appears only when the content really overflows (ResizeObserver + font loading)
  • 05Optional click-anywhere expand that ignores links, buttons and text selections
  • 06Controlled or uncontrolled; keeps the toggle on screen after collapsing a long passage
motion · tweeninput · clickinput · keyboardinput · focus

05Accessibility

  • The toggle is a native <button> with aria-expanded and aria-controls pointing at the text region.
  • Collapsing while focus is inside the text moves focus to the toggle, so it is never lost in hidden content.
  • Tabbing to a link that the clamp hides expands the text, so focus is always visible.
  • Clamped text stays in the accessibility tree; screen readers can read the full passage at any time.
  • Focus-visible draws an accent outline; prefers-reduced-motion makes expand and collapse instant.

06Limitations

  • The clamp uses -webkit-line-clamp, which counts lines across block children in Chromium and Safari; Firefox treats nested blocks slightly differently.