Skip to content

Spoiler

Content that stays covered until somebody asks for it. The cover is a blur rather than a hidden box, so a reader can see that there is something there and how much of it, without reading it by accident.

tsx
import { Spoiler } from 'neba';

<Spoiler locale="ko">
  <p>로즈버드는 썰매의 이름이었습니다.</p>
</Spoiler>;

Props

PropTypeDefaultDescription
revealedbooleanWhether the content is uncovered. Pass it to drive the Spoiler yourself
defaultRevealedbooleanfalseWhere an uncontrolled Spoiler starts
onRevealedChange(revealed: boolean) => voidCalled when the reveal or hide button is pressed
localestring'en'Which language the default label and notice are written in: a BCP 47 tag. Unsupported tags fall back to English
labelReactNodeThe reveal button’s label. Defaults to the locale’s word for it
hideLabelReactNodeThe hide button’s label, when reversible is on
descriptionReactNode | falseThe line above the button. Defaults to the locale’s wording; false writes nothing at all
actionReactNodeReplaces the default reveal button entirely. The replacement is yours to wire up through revealed and onRevealedChange
reversiblebooleanfalseKeeps the content coverable: a hide button under the content, whose row is held open while it is still covered so the box does not change height
maxHeightnumber | stringClamps the covered box to this height: a CSS length, or a number in pixels. Revealing releases it
blurnumber10How hard the content is blurred, in pixels
paddedbooleantrueInner padding around the content. Turn it off for something that should reach the edges
variantshared'solid' | 'outline' | 'text''outline'Weight of the box’s surface. text draws no box at all
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The sheet’s radius, and the size of the button on it
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
densityshared'default' | 'compact''default'Padding around the cover’s own text and button, and nothing else
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
childrenReactNodeWhat is being covered

Every other <div> attribute passes through to the root, except onChange: the change worth listening for is onRevealedChange.

The shared axes (variant size color density elevation) are defined in prop conventions.

Examples

maxHeight

Left out, the box is exactly as tall as what it holds: right for a paragraph or a picture. The cover keeps its place once the content is out, so a notice and a button taller than the line they were covering do not shrink the box on the press, and nothing on the page below moves.

maxHeight is the one thing that does change the height. It clamps the covered box, and revealing lets go of the clamp so the content takes whatever height it needs; a clamp that stayed on would leave the reader a scrollbar instead. It takes a CSS length or a number in pixels.

reversible puts the cover back on afterwards, with a hide button under the content. Its row is held open while the content is still covered, so the way back costs the box no height either.

locale

The button and the line above it are the only words the component invents, and locale is which language they are in: a BCP 47 tag such as ko, pt-BR or zh-Hant. Tags with no translation fall back to English, and a regional tag resolves to its language: ko-KR is ko, zh-TW is Traditional.

label, description and action

label changes the button's words and description changes the line above it; description={false} leaves the cover with nothing written on it. blur decides how hard the content is blurred, in pixels.

action replaces the button entirely, and that replacement is yours to wire up, through revealed and onRevealedChange.

padded

The box pads its content on Box's own scale. Turn it off for something that should reach the edges: the corners then crop the picture the way they do on any other sheet.

variant

text draws no box at all, which is what a spoiler in the middle of running prose usually wants. solid is the filled sheet, for one that is meant to stop the reader.

Controlled

Pass revealed and the Spoiler stops keeping state of its own. Use it to reveal several at once, to remember what a reader has already uncovered, or to put the control somewhere else on the page.

tsx
const [revealed, setRevealed] = useState(false);

<Spoiler revealed={revealed} onRevealedChange={setRevealed}>
  <p>The butler did it.</p>
</Spoiler>;

Accessibility

  • While it is covered the content is inert: out of the tab order, off the accessibility tree, and out of a select-all. A spoiler that could be defeated by a select-all is not a spoiler.
  • The reveal button carries aria-expanded and aria-controls, pointing at the content it uncovers.
  • Set locale so the button and the notice are read out in the page's own language, or write them out in label and description.

Released under the MIT License