<!-- Protocore Design System — Spoiler -->
# Spoiler

- **Category:** Data Display (`data-display`)
- **Slug:** `data-display/spoiler`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Spoiler } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/data-display/spoiler

> Clamps content to a max height with a fade, revealing the rest behind a Show more / Show less toggle.

## When to use it

`Spoiler` is for **one long block you want to preview** — a verbose changelog entry, a long error stack, a policy paragraph — where a full accordion header would be overkill. When you have *several* independently collapsible sections, use **Accordion**; when the hidden content is a distinct panel, use a **Collapse**-style disclosure.

It measures the real content height and only shows the toggle when the content actually overflows the clamp, so it never offers a "Show more" that reveals nothing. The fade uses the canvas colour, so place it on the page canvas or a matching surface.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | `ReactNode` | no | — | The (potentially long) content to clamp. |
| `className` | `string` | no | — | — |
| `defaultExpanded` | `boolean` | no | `false` | Initial expanded state (uncontrolled). |
| `expanded` | `boolean` | no | — | Controlled expanded state. |
| `hideLabel` | `ReactNode` | no | `Show less` | Toggle label shown while expanded. |
| `maxHeight` | `string \| number` | no | `120` | Collapsed height in px (number) or any CSS length. |
| `onExpandedChange` | `((expanded: boolean) => void)` | no | — | Fires when the user toggles; receives the next expanded state. |
| `showLabel` | `ReactNode` | no | `Show more` | Toggle label shown while collapsed. |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

`Spoiler` clamps its children to `maxHeight`, fades the cut edge, and offers a mono UPPERCASE **Show more** toggle. When the content already fits, no toggle appears — the clamp is inert, so short content looks untouched.

```tsx
import { Spoiler, Text } from "@protocore/pds";

export default function SpoilerBasics() {
  return (
    <Spoiler maxHeight={72}>
      <Text>
        Reconciliation run #4821 processed 12,904 transactions across three settlement rails. The
        ledger matched the bank feed on all but two entries, both flagged for manual review.
        Currency normalisation applied the 04:00 UTC rate table. The cleared batch was settled at
        04:12 UTC and the audit log was sealed. No discrepancies remained after the second pass, and
        the downstream reporting job was triggered automatically.
      </Text>
    </Spoiler>
  );
}
```

### Custom labels & height

Tune `maxHeight` and swap `showLabel` / `hideLabel` for domain wording — "Expand log" / "Collapse log". The toggle state is uncontrolled by default; wire `expanded` + `onExpandedChange` to control it.

```tsx
import { Spoiler, CodeBlock } from "@protocore/pds";

const log = `[04:12:01] settle: batch #4821 opened
[04:12:01] rail: SEPA connected
[04:12:02] rail: SWIFT connected
[04:12:02] rail: ACH connected
[04:12:04] fx: applied rate table 04:00Z
[04:12:07] match: 12902/12904 entries reconciled
[04:12:07] flag: 2 entries -> manual review
[04:12:09] settle: batch sealed
[04:12:09] report: downstream job triggered`;

export default function SpoilerCustom() {
  return (
    <Spoiler maxHeight={64} showLabel="Expand log" hideLabel="Collapse log">
      <CodeBlock>{log}</CodeBlock>
    </Spoiler>
  );
}
```

## Usage

**Do**

- Use it to preview a single long block with a graceful cut.
- Give the toggle domain wording via `showLabel` / `hideLabel`.
- Control it with `expanded` when the state lives in your model.

**Don't**

- For several sections, use an Accordion.
- Set `maxHeight` high enough not to clip a single line.
- Place it over a surface matching the fade's canvas color.

## Accessibility

**Notes**

- The toggle is a real `button` with `aria-expanded` reflecting state and `aria-controls` pointing at the clamped region.
- The control only renders when content overflows, so keyboard users never tab to a no-op toggle.
- The height transition and fade are decorative and honour `prefers-reduced-motion`.

## Related

`accordion`, `text`, `code-block`, `definition-list`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
