|
| 1 | +# Spoiler |
| 2 | + |
| 3 | +An interactive component that allow hide long sections of content under a spoiler. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- Full keyboard navigation. |
| 8 | + |
| 9 | +- Can be uncontrolled or controlled. |
| 10 | + |
| 11 | +- Support for responsive design. |
| 12 | + |
| 13 | +## Import |
| 14 | + |
| 15 | +```tsx |
| 16 | +import { Spoiler } from 'qwik-primitives'; |
| 17 | +``` |
| 18 | + |
| 19 | +## Anatomy |
| 20 | + |
| 21 | +```tsx |
| 22 | +import { component$ } from '@builder.io/qwik'; |
| 23 | +import { Spoiler } from 'qwik-primitives'; |
| 24 | + |
| 25 | +const SpoilerDemo = component$(() => { |
| 26 | + return ( |
| 27 | + <Spoiler.Root> |
| 28 | + <Spoiler.Trigger /> |
| 29 | + <Spoiler.Panel> |
| 30 | + <Spoiler.Content /> |
| 31 | + </Spoiler.Panel> |
| 32 | + </Spoiler.Root> |
| 33 | + ); |
| 34 | +}); |
| 35 | +``` |
| 36 | + |
| 37 | +## Usage |
| 38 | + |
| 39 | +Spoiler component can be uncontrolled or controlled. |
| 40 | + |
| 41 | +### Uncontrolled |
| 42 | + |
| 43 | +```tsx |
| 44 | +import { component$ } from '@builder.io/qwik'; |
| 45 | +import { Spoiler } from '@/components'; |
| 46 | + |
| 47 | +const SpoilerDemo = component$(() => { |
| 48 | + return ( |
| 49 | + <Spoiler.Root> |
| 50 | + <Spoiler.Trigger>Toggle content</Spoiler.Trigger> |
| 51 | + <Spoiler.Panel minHeight="60px"> |
| 52 | + <Spoiler.Content> |
| 53 | + <p> |
| 54 | + Qwik Primitives is a UI toolkit for building accessible web apps and design systems with Qwik. It provides a |
| 55 | + set of low-level UI components and primitives which can be the foundation for your design system |
| 56 | + implementation. |
| 57 | + </p> |
| 58 | + <div style={{ height: '300px', backgroundColor: 'purple' }} /> |
| 59 | + </Spoiler.Content> |
| 60 | + </Spoiler.Panel> |
| 61 | + </Spoiler.Root> |
| 62 | + ); |
| 63 | +}); |
| 64 | +``` |
| 65 | + |
| 66 | +### Controlled |
| 67 | + |
| 68 | +```tsx |
| 69 | +import { component$, useSignal } from '@builder.io/qwik'; |
| 70 | +import { Spoiler } from '@/components'; |
| 71 | + |
| 72 | +const SpoilerDemo = component$(() => { |
| 73 | + const isOpen = useSignal(false); |
| 74 | + |
| 75 | + return ( |
| 76 | + <Spoiler.Root open={isOpen} onOpenChange$={(open) => (isOpen.value = open)}> |
| 77 | + <Spoiler.Trigger>Toggle content</Spoiler.Trigger> |
| 78 | + <Spoiler.Panel minHeight="60px"> |
| 79 | + <Spoiler.Content> |
| 80 | + <p> |
| 81 | + Qwik Primitives is a UI toolkit for building accessible web apps and design systems with Qwik. It provides a |
| 82 | + set of low-level UI components and primitives which can be the foundation for your design system |
| 83 | + implementation. |
| 84 | + </p> |
| 85 | + <div style={{ height: '300px', backgroundColor: 'purple' }} /> |
| 86 | + </Spoiler.Content> |
| 87 | + </Spoiler.Panel> |
| 88 | + </Spoiler.Root> |
| 89 | + ); |
| 90 | +}); |
| 91 | +``` |
| 92 | + |
| 93 | +## API Reference |
| 94 | + |
| 95 | +### Root |
| 96 | + |
| 97 | +Contains all the parts of a spoiler. This component is based on the `div` element. |
| 98 | + |
| 99 | +| Prop | Type | Default | Description | |
| 100 | +| --------------- | ------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 101 | +| `as` | `FunctionComponent` | `-` | Change the default rendered element for the one passed as, merging their props and behavior. Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details. | |
| 102 | +| `defaultOpen` | `boolean` | `-` | The open state of the spoiler when it is initially rendered. Use when you do not need to control its open state. | |
| 103 | +| `open` | `Signal` | `-` | The controlled open state of the spoiler. | |
| 104 | +| `onOpenChange$` | `QRL<(open: boolean) => void>` | `-` | Event handler called when the open state of the spoiler changes. | |
| 105 | +| `disabled` | `boolean` | `-` | When `true`, prevents the user from interacting with the spoiler. | |
| 106 | +| `style` | `CSSProperties` | `-` | The inline style for the element. | |
| 107 | + |
| 108 | +| Data attribute | Values | |
| 109 | +| ----------------- | --------------------- | |
| 110 | +| `[data-scope]` | `"spoiler"` | |
| 111 | +| `[data-part]` | `"root"` | |
| 112 | +| `[data-state]` | `"open" \| "closed"` | |
| 113 | +| `[data-disabled]` | Present when disabled | |
| 114 | + |
| 115 | +### Trigger |
| 116 | + |
| 117 | +The button that toggles the spoiler. This component is based on the `button` element. |
| 118 | + |
| 119 | +| Prop | Type | Default | Description | |
| 120 | +| ------- | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 121 | +| `as` | `FunctionComponent` | `-` | Change the default rendered element for the one passed as, merging their props and behavior. Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details. | |
| 122 | +| `style` | `CSSProperties` | `-` | The inline style for the element. | |
| 123 | + |
| 124 | +| Data attribute | Values | |
| 125 | +| ----------------- | --------------------- | |
| 126 | +| `[data-scope]` | `"spoiler"` | |
| 127 | +| `[data-part]` | `"trigger"` | |
| 128 | +| `[data-state]` | `"open" \| "closed"` | |
| 129 | +| `[data-disabled]` | Present when disabled | |
| 130 | + |
| 131 | +### Panel |
| 132 | + |
| 133 | +The panel that expands/collapses. This component is based on the `div` element. |
| 134 | + |
| 135 | +| Prop | Type | Default | Description | |
| 136 | +| ----------- | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 137 | +| `as` | `FunctionComponent` | `-` | Change the default rendered element for the one passed as, merging their props and behavior. Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details. | |
| 138 | +| `minHeight` | `string` | `0px` | The minimum height of the panel when spolier is closed. | |
| 139 | +| `onOpen$` | `QRL<() => void>` | `-` | Event handler called when the panel is fully open. If you animate the size of the panel when it opens this event handler was call after animation end. | |
| 140 | +| `onClose$` | `QRL<() => void>` | `-` | Event handler called when the panel is fully close. If you animate the size of the panel when it closes this event handler was call after animation end. | |
| 141 | +| `style` | `CSSProperties` | `-` | The inline style for the element. | |
| 142 | + |
| 143 | +| Data attribute | Values | |
| 144 | +| ----------------- | --------------------- | |
| 145 | +| `[data-scope]` | `"spoiler"` | |
| 146 | +| `[data-part]` | `"panel"` | |
| 147 | +| `[data-state]` | `"open" \| "closed"` | |
| 148 | +| `[data-disabled]` | Present when disabled | |
| 149 | + |
| 150 | +| CSS Variable | Description | |
| 151 | +| -------------------------------------------- | ------------------------------------------------------ | |
| 152 | +| `--qwik-primitives-spoiler-panel-min-height` | The minimum height of the panel when spolier is close. | |
| 153 | +| `--qwik-primitives-spoiler-panel-max-height` | The maximum height of the panel when spolier is open. | |
| 154 | + |
| 155 | +### Content |
| 156 | + |
| 157 | +The component that contains the spoiler content. Must be rendered inside `Spoiler.Panel`. This component is based on the `div` element. |
| 158 | + |
| 159 | +| Prop | Type | Default | Description | |
| 160 | +| ------- | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 161 | +| `as` | `FunctionComponent` | `-` | Change the default rendered element for the one passed as, merging their props and behavior. Read our [Composition](https://github.com/ZAHON/qwik-primitives/blob/main/packages/primitives/docs/composition.md) guide for more details. | |
| 162 | +| `style` | `CSSProperties` | `-` | The inline style for the element. | |
| 163 | + |
| 164 | +| Data attribute | Values | |
| 165 | +| ----------------- | --------------------- | |
| 166 | +| `[data-scope]` | `"spoiler"` | |
| 167 | +| `[data-part]` | `"content"` | |
| 168 | +| `[data-state]` | `"open" \| "closed"` | |
| 169 | +| `[data-disabled]` | Present when disabled | |
| 170 | + |
| 171 | +## Examples |
| 172 | + |
| 173 | +### Animating panel height |
| 174 | + |
| 175 | +Use the `--qwik-primitives-spoiler-panel-min-height` and `--qwik-primitives-spoiler-panel-max-height` CSS variables to animate the height of the panel when it opens/closes. |
| 176 | + |
| 177 | +```tsx |
| 178 | +// index.tsx |
| 179 | +import { component$, useStyles$ } from '@builder.io/qwik'; |
| 180 | +import { Spoiler } from '@/components'; |
| 181 | +import styles from './styles.css?inline'; |
| 182 | + |
| 183 | +const SpoilerDemo = component$(() => { |
| 184 | + useStyles$(styles); |
| 185 | + |
| 186 | + return ( |
| 187 | + <Spoiler.Root> |
| 188 | + <Spoiler.Trigger>Toggle content</Spoiler.Trigger> |
| 189 | + <Spoiler.Panel minHeight="60px" class="spoiler-panel"> |
| 190 | + <Spoiler.Content> |
| 191 | + <p> |
| 192 | + Qwik Primitives is a UI toolkit for building accessible web apps and design systems with Qwik. It provides a |
| 193 | + set of low-level UI components and primitives which can be the foundation for your design system |
| 194 | + implementation. |
| 195 | + </p> |
| 196 | + <div style={{ height: '300px', backgroundColor: 'purple' }} /> |
| 197 | + </Spoiler.Content> |
| 198 | + </Spoiler.Panel> |
| 199 | + </Spoiler.Root> |
| 200 | + ); |
| 201 | +}); |
| 202 | +``` |
| 203 | + |
| 204 | +```css |
| 205 | +/* styles.css */ |
| 206 | +.spoiler-panel[data-state='open'] { |
| 207 | + animation: spoiler-panel-down 300ms ease-out; |
| 208 | +} |
| 209 | + |
| 210 | +.spoiler-panel[data-state='closed'] { |
| 211 | + animation: spoiler-panel-up 300ms ease-out; |
| 212 | +} |
| 213 | + |
| 214 | +@keyframes spoiler-panel-down { |
| 215 | + 0% { |
| 216 | + height: var(--qwik-primitives-spoiler-panel-min-height); |
| 217 | + } |
| 218 | + 100% { |
| 219 | + height: var(--qwik-primitives-spoiler-panel-max-height); |
| 220 | + } |
| 221 | +} |
| 222 | + |
| 223 | +@keyframes spoiler-panel-up { |
| 224 | + 0% { |
| 225 | + height: var(--qwik-primitives-spoiler-panel-max-height); |
| 226 | + } |
| 227 | + 100% { |
| 228 | + height: var(--qwik-primitives-spoiler-panel-min-height); |
| 229 | + } |
| 230 | +} |
| 231 | +``` |
| 232 | + |
| 233 | +### Responsive min height of panel |
| 234 | + |
| 235 | +You can also pass a CSS variable to the `minHeight` property. This will allow you to vary the minimum panel height when the spoiler is closed, depending on the screen width. This is especially useful when the content you want to be visible when the spoiler is closed has different heights depending on the screen width. |
| 236 | + |
| 237 | +```tsx |
| 238 | +// index.tsx |
| 239 | +import { component$, useStyles$ } from '@builder.io/qwik'; |
| 240 | +import { Spoiler } from '@/components'; |
| 241 | +import styles from './styles.css?inline'; |
| 242 | + |
| 243 | +const SpoilerDemo = component$(() => { |
| 244 | + useStyles$(styles); |
| 245 | + |
| 246 | + return ( |
| 247 | + <Spoiler.Root> |
| 248 | + <Spoiler.Trigger>Toggle content</Spoiler.Trigger> |
| 249 | + <Spoiler.Panel minHeight="var(--spoiler-panel-min-height)" class="spoiler-panel"> |
| 250 | + <Spoiler.Content> |
| 251 | + <p> |
| 252 | + Qwik Primitives is a UI toolkit for building accessible web apps and design systems with Qwik. It provides a |
| 253 | + set of low-level UI components and primitives which can be the foundation for your design system |
| 254 | + implementation. |
| 255 | + </p> |
| 256 | + <div style={{ height: '300px', backgroundColor: 'purple' }} /> |
| 257 | + </Spoiler.Content> |
| 258 | + </Spoiler.Panel> |
| 259 | + </Spoiler.Root> |
| 260 | + ); |
| 261 | +}); |
| 262 | +``` |
| 263 | + |
| 264 | +```css |
| 265 | +/* styles.css */ |
| 266 | +.spoiler-panel { |
| 267 | + --spoiler-panel-min-height: 60px; |
| 268 | +} |
| 269 | + |
| 270 | +@media screen and (min-width: 640px) { |
| 271 | + .spoiler-panel { |
| 272 | + --spoiler-panel-min-height: 120px; |
| 273 | + } |
| 274 | +} |
| 275 | +``` |
| 276 | + |
| 277 | +## Accessibility |
| 278 | + |
| 279 | +### Differences to Collapsible component |
| 280 | + |
| 281 | +At first glance, the `Spoiler` component does not differ much from the `Collapsible` component. The main difference you may notice is that the `Spoiler` component allows you to set a minimum panel height. However, in a situation where you want to hide the content and then show it your first choice should be the `Collapsible` component. This is related to accessibility in case the `Collapsible` component is closed its content is removed from the accessibility tree while when the `Spoiler` component is closed its content is still visible in the accessibility tree. |
| 282 | + |
| 283 | +### Keyboard Interactions |
| 284 | + |
| 285 | +| Key | Description | |
| 286 | +| ------- | ------------------------- | |
| 287 | +| `Space` | Opens/closes the spoiler. | |
| 288 | +| `Enter` | Opens/closes the spoiler. | |
0 commit comments