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-textLive · keyboard ready
- 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-textAdds 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.



