DraftPreview
StableA drafted message shown like an email, with headers and a body that opens up to the full text.
Also known as preview, draft, review before send
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Email style with headers
- To
- support@aerohome.example
- From
- meera@example.com
- Subject
- Refund request for Aero 12 blender
<DraftPreview
className="w-full max-w-md"
headers={[
{ label: "To", value: "support@aerohome.example" },
{ label: "From", value: "meera@example.com" },
{ label: "Subject", value: "Refund request for Aero 12 blender" },
]}
wordCount={102}
footer="Sent from your email. Nothing goes out without your tap."
>
{`Hello Priya, I bought the Aero 12 blender on 2 September and it stopped working after four days. The motor hums but the blades do not turn. I have attached the invoice and a short video. Under the Consumer Protection Act I am asking for a full refund of ₹2,340 to my original payment method. Please reply by 27 October 2026. If I do not hear back by then I will raise the matter with the consumer helpline. I would much prefer to settle this directly, so please tell me what you need from me. Thank you for your time, Meera Shah`}
</DraftPreview>The headers sit above the body. Only the first four lines show until the person presses the button.
When to use it
Makes sure nobody approves a message they have not read. The draft looks like what it is, and the full text is one press away.
Made of: Header rows · Body · Fade · Expand button · Footer note
Use it for
- An email or letter an agent drafted, shown before it is sent.
- A message with To, From and Subject lines.
- Any long generated text that needs a read-before-approve step.
Not for
- A hidden section of ordinary content: use Collapsible.
- The approve and deny buttons themselves: use ApprovalPrompt.
- A note or warning in the page: use Callout.
Do and avoid
- To
- support@shop.example
- Subject
- Refund for order 4471
- To
- support@shop.example
- Subject
- Refund for order 4471
Do
- Show the real To, From and Subject so the draft looks like the sent message.
- Tell people what happens next in the footer, such as when it is sent.
- Keep approval off until the person has had the chance to open the draft.
Avoid
- Do not open it for the person. It stays collapsed until they choose.
- Do not give a made-up word count.
- Do not use it for text the person wrote themselves.
On a phone
- It shows the first few lines (4 by default) with a fade, and the show more button is at least 44px tall on a touch screen.
- The header rows and the text break long words, so an address or link cannot push the card wider than the screen.
- The line count is measured again when the width changes, so the button disappears if the text fits.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| headers | { label: string; value: ReactNode }[] | none | Rows such as To, From and Subject, drawn as a labelled definition list above the body. |
| childrenrequired | ReactNode | none | The body text. |
| wordCount | number | none | Shown in the expand button, such as Read all 95 words. |
| previewLines | number | 4 | How many lines show before the text fades out. |
| defaultOpen | boolean | false | Starting state when you do not control open. |
| open | boolean | none | Controlled open state. Pair it with onOpenChange. |
| onOpenChange | (open: boolean) => void | none | Called with the new state when the button is pressed. |
| expandLabel | (words: number | undefined) => string | Read all N words | Text of the button while collapsed. Receives wordCount. |
| collapseLabel | string | "Show less" | Text of the button while open. |
| footer | ReactNode | none | A line under the message, such as Nothing goes out without your tap. |
| className | string | none | Classes for the outer card. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one DraftPreview switches all of this from its properties panel, across 2 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Open | open | On or off |
| Text | children | Any text |
| Footer | footer | Any text |
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- collapsed
- Only the first previewLines lines show, with a fade at the bottom. This is the starting state unless defaultOpen or open is set.
- expanded
- The full text shows, the fade is gone and the button reads Show less.
- fits
- When the text is no longer than the preview lines, there is no fade and no button.
- focus
- A visible focus ring shows on the expand button when reached by keyboard.
- hover
- The expand button text is underlined under the pointer.
Tokens
Change one of these and every part that uses it follows.
--surface--surface-sunken--border--fg--fg-muted--dur-fast--radius-card
Built in
- The collapsed text is only clipped by size and a fade. Screen readers still read all of it.
- The fade is hidden from screen readers.
- The expand control is a real button with aria-expanded and aria-controls pointing at the body.
- Headers are a definition list, so each label is tied to its value.
- The button is at least 44px tall on touch screens.
- When the text fits in the preview lines, no button is drawn.
What you need to do
- Put the approve button after the preview, so it comes later in the reading order.
- Pass a wordCount that is true, since it appears in the expand button.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide