Expandable text

Long text clamped to a few lines with a soft fade that glides open with Read more and closes with Show less, shown only when the text overflows.

pnpm dlx shadcn@latest add @uiarc/expandable-text
Live · keyboard ready
Two speckled stoneware cups, one with its lid resting against it

Speckled stoneware cup

$38 set of two

Thrown by hand in a small studio outside Porto, each cup is made from a coarse local stoneware that fires to a warm oatmeal tone. A satin glaze breaks over the iron speckles in the clay, so no two cups in a set are quite alike.

They hold 300 ml, which suits a flat white, a long black, or a generous pour of tea. The rim is rolled thin for drinking, the walls are thick enough to keep coffee warm, and the unglazed foot is sanded smooth so it will not scratch a table.

Dishwasher and microwave safe. Because every cup is made by hand, sizes can vary by a few millimetres and small marks from the kiln are part of the piece.

Reviews 4.7 from 128

  • Hannah WalshSep 18
    The glaze is even better in person

    I bought two sets after holding one at a friend's place, and they have quietly replaced every other mug in the cupboard. The speckled glaze pools slightly darker near the base, so each one looks a little different under morning light.

    They hold a proper 300 ml flat white with room to spare, stay warm for longer than my old porcelain cups, and the unglazed foot sits steady on the table. Two months of daily dishwasher runs and there is not a single chip or crack on any of them.

  • Daniel KimSep 9
    Lovely weight, small handle

    Beautifully made and heavier than they look, which I like. The handle only fits two fingers, so if you have larger hands you will end up holding the body of the cup instead. It does keep the heat, though, so I use a saucer.

  • Sofia RamirezAug 30
    Arrived fast

    Well packed and arrived in two days. Exactly as pictured.

  • A list of user reviews or comments where some entries are long.
  • A product, listing, or profile description that should not push the rest of the page down.
  • Any card where long text would make the cards uneven.
  • Use accordion for a set of titled sections or an FAQ.
  • Use expandable-card when the hidden part is structured content rather than running text.
  • Use scroll-area when the full text must stay in a fixed height box.
  • Do not clamp content people must read, such as legal terms or error details.

Installation

Add Expandable text with the shadcn CLI, or copy the source by hand.

pnpm dlx shadcn@latest add @uiarc/expandable-text

Adds the component and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:

example.tsx
import { ExpandableText } from "@/components/arc/expandable-text/expandable-text"; export function ReviewBody({ review }: { review: Review }) {  return (    <ExpandableText lines={2}>      {review.paragraphs.map(paragraph => <p key={paragraph}>{paragraph}</p>)}    </ExpandableText>  );}

ExpandableText

Long text clamped to a few lines with a soft fade over the last one. Read more opens it smoothly to its full height and Show less closes it again. The toggle only appears when the text really overflows.

PropTypeDefaultDescription
childrenRequiredReactNode–The text. Strings, paragraphs, and inline links all work. The whole text stays in the accessibility tree while clamped.
linesnumber3Lines shown while collapsed, measured in the text's own line height.
expandedboolean–Controlled open state.
defaultExpandedbooleanfalseStarts open when uncontrolled.
onExpandedChange(expanded: boolean) => void–Called when the toggle, or focus moving to a hidden link, opens or closes the text.
moreLabelstring"Read more"Toggle label while collapsed.
lessLabelstring"Show less"Toggle label while open.
...propsHTMLAttributes<HTMLDivElement>–Passed to the root element, including className and style.
Tab
Moves to links inside the text, then to the toggle. Focusing a link hidden below the clamp opens the text.
EnterorSpace
Opens or closes the text from the toggle.
  • The clamp is visual only: the full text stays in the accessibility tree, so screen readers read all of it without pressing the toggle.
  • The toggle is a native button with aria-expanded and aria-controls pointing at the text.
  • The two labels crossfade in one grid cell; the hidden one is aria-hidden, so the button's name is always just the current label.
  • Links or buttons inside the clamped part open the text when they receive keyboard focus, so focus is never hidden.
  • Height, the fade mask, and the chevron all follow one progress value, so they always move in step and a click mid-way reverses from the current height.
  • Opening takes 500 to 600ms and closing 400 to 450ms, growing gently with the distance, on a soft ease out (0.32, 0.5, 0.2, 1) that answers at once and spends most of its time settling. It never overshoots.
  • The fade over the last line dissolves as the text opens and only returns as it nears the clamp. While moving, newly revealed lines rise through the soft edge instead of being uncovered by a hard line.
  • Read more and Show less crossfade with a short drift and a brief blur, without changing the button's width.
  • Closing from far down the page scrolls the start of the text back into view together with the collapse.
  • At rest the clamp is plain CSS (max-height in lh units), so width and font changes need no JavaScript. With reduced motion the text opens and closes at once.
  • The clamp is measured in lines, so narrower screens show the same number of lines with fewer words; the toggle appears or disappears as wrapping changes.
  • The toggle sits under the text, left aligned, with a 28px tall hit area that works on touch.
  • One ResizeObserver per instance, watching the text and a zero width probe that is exactly the clamp height.
  • Only height and a mask gradient change while moving; at rest there is no inline height and, once open, no mask.
  • The server renders the toggle, assuming long text; short text drops it on the first measurement.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Use it for reviews, product descriptions, bios, comments, and release notes where the first lines are enough for most readers.
  • Pick lines by context: 2 in dense lists such as reviews, 3 to 4 for a product or profile description.
  • Keep one line height inside the text. Headings or large images inside break the line based clamp; use expandable-card or accordion for structured content.
  • Nothing needs measuring by the caller: a ResizeObserver decides whether the toggle is shown and keeps that correct as the layout changes.

The full library index for assistants is at /llms.txt.