# cock — complete reference
> An open-source Svelte 5 and Tailwind 4 component kit: 66 accessible components, dark mode, theming from three colours and a theme generator.
Install: `npm install @joshicodes/cock` (also bun, pnpm, yarn).
```css
/* src/app.css */
@import 'tailwindcss';
@import '@joshicodes/cock/theme.css';
```
```ts
// src/hooks.server.ts — applies the stored theme before the first paint
export { themeHandle as handle } from '@joshicodes/cock';
```
## Theming
Set three custom properties; the rest is derived with `color-mix`:
`--ck-bg`, `--ck-fg`, `--ck-accent` (plus `--ck-accent-hover` and `--ck-accent-fg`
when you use a custom accent). Derived tokens: `--ck-canvas`, `--ck-surface`,
`--ck-muted`, `--ck-border`, `--ck-border-strong`, `--ck-fg-muted`, `--ck-fg-subtle`,
`--ck-accent-soft`, `--ck-ring`. Each one reaches Tailwind as a utility, e.g. `bg-surface`.
Other knobs: `--ck-tint`, `--ck-radius-scale`, `--ck-font-sans`, `--ck-font-mono`.
Put `data-ck-theme="dark"` on any element to theme only that subtree.
Runtime: `theme.mode`, `theme.toggle()`, `theme.accent`, `theme.setToken(name, value, scheme?)`,
`theme.setSharedToken()`, `theme.clearTokens()`, `theme.resolved`, `theme.revision`.
Every component accepts `class`, merged with tailwind-merge so your utility wins.
## Button
The one component everything else borrows from. Pass `href` and it renders an `` with the same look, which saves wrapping buttons in links and getting the keyboard behaviour wrong.
Category: Primitives. Docs: https://cock.joshicodes.de/components/button
```svelte
import { Button } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `ButtonVariant` | `'solid'` | — |
| `size` | `ButtonSize` | `'md'` | — |
| `href` | `string` | — | Render as an ` ` instead of a ``. |
| `target` | `string` | — | — |
| `rel` | `string` | — | — |
| `loading` | `boolean` | `false` | — |
| `block` | `boolean` | `false` | Stretch to the full width of the parent. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
Remaining attributes are forwarded (`HTMLButtonAttributes`).
**Behaviour**
- `loading` disables the button and shows a spinner, so a double submit is not possible while a request is in flight.
- With `href` the component renders an ``; `disabled` then becomes `aria-disabled` plus `tabindex="-1"`, because a link cannot be disabled.
- Every other attribute is forwarded, so `type="submit"`, `form` and `onclick` work as usual.
## Badge
A small status label, with an optional leading dot.
Category: Primitives. Docs: https://cock.joshicodes.de/components/badge
```svelte
import { Badge } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `BadgeVariant` | `'neutral'` | — |
| `size` | `'sm' | 'md'` | `'md'` | — |
| `dot` | `boolean` | `false` | Show a small leading status dot. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
Remaining attributes are forwarded (`HTMLAttributes`).
## Avatar
Picture, initials or a fallback glyph, with a status dot.
Category: Primitives. Docs: https://cock.joshicodes.de/components/avatar
```svelte
import { Avatar } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `src` | `string` | — | — |
| `alt` | `string` | — | — |
| `name` | `string` | — | Falls back to initials, then to a neutral glyph. |
| `size` | `'xs' | 'sm' | 'md' | 'lg' | 'xl'` | `'md'` | — |
| `shape` | `'circle' | 'square'` | `'circle'` | — |
| `status` | `'online' | 'busy' | 'away' | 'offline'` | — | — |
| `class` | `string` | — | — |
**Behaviour**
- Falls back to initials from `name` when `src` is missing or fails to load, and to a neutral glyph when there is no name either.
- Overlapping a row of avatars is a `-space-x-2` on the wrapper and a ring on each one; there is no separate group component.
## CopyButton
Copies a string and confirms it in place.
Category: Primitives. Docs: https://cock.joshicodes.de/components/copy-button
```svelte
import { CopyButton } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | — | required. The text that ends up on the clipboard. |
| `label` | `string` | `'Copy'` | — |
| `copiedLabel` | `string` | `'Copied'` | — |
| `iconOnly` | `boolean` | `false` | Icon only; the label becomes the accessible name. |
| `variant` | `ButtonVariant` | `'outline'` | — |
| `size` | `ButtonSize` | — | — |
| `class` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `oncopy` | `(value: string) => void` | — | — |
**Behaviour**
- Uses the asynchronous clipboard API, which browsers only allow from a real click on a secure origin. If the write is refused the button simply does not change.
## ThemeToggle
Light and dark, or a segmented control that adds system.
Category: Primitives. Docs: https://cock.joshicodes.de/components/theme-toggle
```svelte
import { ThemeToggle } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `'icon' | 'segmented'` | `'icon'` | `icon` swaps a single glyph, `segmented` shows light / dark / system. |
| `class` | `string` | — | — |
**Behaviour**
- Writes through the `theme` state, so the choice is stored and every other instance updates with it.
- Pair it with the no-flash script, or the page renders in the wrong scheme for a frame.
## Spinner
A borrowed-border spinner that takes the current text colour.
Category: Primitives. Docs: https://cock.joshicodes.de/components/spinner
```svelte
import { Spinner } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | — |
| `class` | `string` | — | — |
| `label` | `string` | `'Loading'` | — |
## Skeleton
Grey placeholder blocks, with a shortcut for stacked text lines.
Category: Primitives. Docs: https://cock.joshicodes.de/components/skeleton
```svelte
import { Skeleton } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
| `lines` | `number` | — | Convenience for text placeholders: renders N stacked lines. |
## Separator
A hairline, horizontal or vertical, optionally with a label.
Category: Primitives. Docs: https://cock.joshicodes.de/components/separator
```svelte
import { Separator } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `orientation` | `'horizontal' | 'vertical'` | `'horizontal'` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | Optional label rendered in the middle of a horizontal rule. |
## Kbd
A key cap for documenting shortcuts.
Category: Primitives. Docs: https://cock.joshicodes.de/components/kbd
```svelte
import { Kbd } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
## Code
Inline code, bordered so it separates from the sentence.
Category: Primitives. Docs: https://cock.joshicodes.de/components/code
```svelte
import { Code } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
## Card
Flat by default: a border and nothing else. Ask for depth with `variant`, or skip the parts entirely and set `padding` when all you want is a box.
Category: Layout. Docs: https://cock.joshicodes.de/components/card
```svelte
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@joshicodes/cock';
```
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `'default' | 'flat' | 'elevated' | 'raised'` | `'default'` | `elevated` adds a shadow, `raised` lifts on hover, `flat` drops the border too. |
| `padding` | `'none' | 'sm' | 'md' | 'lg'` | `'none'` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
Remaining attributes are forwarded (`HTMLAttributes`).
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `divided` | `boolean` | `false` | Draw a hairline under the header. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
| `actions` | `Snippet` | — | Right-aligned slot for actions. |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `divided` | `boolean` | `true` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Behaviour**
- `CardHeader` takes an `actions` snippet for the top right corner, which is where a badge or a menu button usually goes.
- On a dark background a shadow is nearly invisible, so `elevated` also lightens the surface.
## EmptyState
The dashed box for lists that have nothing in them yet.
Category: Layout. Docs: https://cock.joshicodes.de/components/empty-state
```svelte
import { EmptyState } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `title` | `string` | — | required |
| `description` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `icon` | `Snippet` | — | — |
| `actions` | `Snippet` | — | — |
## Field
Generates an id, points the label at it and sets `aria-describedby`, then hands all three to you through a snippet. It is the piece that stops every form growing its own slightly different label markup.
Category: Forms. Docs: https://cock.joshicodes.de/components/field
```svelte
import { Field } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `label` | `string` | — | — |
| `description` | `string` | — | Helper text under the control. Replaced by `error` when that is set. |
| `error` | `string` | — | — |
| `required` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet<[FieldSlotArgs]>` | — | required |
**Behaviour**
- `error` replaces `description` while it is set, and switches the control to its invalid styling.
- The snippet receives `id`, `invalid` and `aria-describedby`. Spread the rest onto the control so the wiring survives.
## Input
Text input in three sizes, with slots inside either end.
Category: Forms. Docs: https://cock.joshicodes.de/components/input
```svelte
import { Input } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | — |
| `invalid` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `leading` | `Snippet` | — | Icon or text rendered inside, before the input. |
| `trailing` | `Snippet` | — | Icon, button or text rendered inside, after the input. |
Remaining attributes are forwarded (`HTMLInputAttributes`).
**Behaviour**
- `bind:value` keeps a string even for `type="number"`, because the component writes through the input event rather than the native binding.
- `leading` is not interactive; put a button in `trailing` if it needs to be clickable.
## NumberInput
A number field built on `type="text"`. A native number input empties itself the moment a stray letter lands in it, changes value when the scroll wheel passes over, and hands back an empty string for anything it considers invalid — so the component never sees what the person actually typed. This one parses the text itself.
Category: Forms. Docs: https://cock.joshicodes.de/components/number-input
```svelte
import { NumberInput } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number | null` | `null` | bindable. `null` while the field is empty, so a blank is not confused with zero. |
| `min` | `number` | — | — |
| `max` | `number` | — | — |
| `step` | `number` | `1` | — |
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | — |
| `invalid` | `boolean` | `false` | — |
| `unit` | `string` | — | Suffix shown inside the field, e.g. a unit. |
| `class` | `string` | — | — |
Remaining attributes are forwarded (`HTMLInputAttributes`).
**Keyboard**
- `↑ / ↓`: One step
- `Page Up / Page Down`: Ten steps
- `Home / End`: Jumps to min or max, when they are set
**Behaviour**
- `value` is `number | null`. An empty field is `null`, which is not the same as `0`, and a form that treats them alike is a bug waiting for a quantity of zero.
- Clamping to `min` and `max` happens on blur, not on every keystroke, or typing `100` into a field capped at 50 would stop at `5`.
- `step` decides the decimal places, so a step of `0.25` keeps two and never shows `0.30000000000000004`.
- The step buttons are out of the tab order: the field is already a spinbutton, and the arrow keys do the same job.
## Textarea
Multi-line input that can grow with its content.
Category: Forms. Docs: https://cock.joshicodes.de/components/textarea
```svelte
import { Textarea } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `invalid` | `boolean` | `false` | — |
| `autosize` | `boolean` | `false` | Grow with the content instead of scrolling. |
| `class` | `string` | — | — |
Remaining attributes are forwarded (`HTMLTextareaAttributes`).
## Select
The native select, styled, with an options array or your own markup.
Category: Forms. Docs: https://cock.joshicodes.de/components/select
```svelte
import { Select } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `options` | `SelectOption[]` | — | Pass options, or supply `` elements as children. |
| `placeholder` | `string` | — | — |
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | — |
| `invalid` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
Remaining attributes are forwarded (`HTMLSelectAttributes`).
**Behaviour**
- A real ``, so the phone opens its own picker and the keyboard behaviour is the platform one.
## Checkbox
Checkbox with a label, help text and an indeterminate state.
Category: Forms. Docs: https://cock.joshicodes.de/components/checkbox
```svelte
import { Checkbox } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `checked` | `boolean` | `false` | bindable |
| `indeterminate` | `boolean` | `false` | — |
| `description` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
Remaining attributes are forwarded (`HTMLInputAttributes`).
## RadioGroup
A set of radios that share a name and a bound value.
Category: Forms. Docs: https://cock.joshicodes.de/components/radio-group
```svelte
import { RadioGroup, Radio } from '@joshicodes/cock';
```
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `name` | `string` | `uid('radio')` | — |
| `disabled` | `boolean` | `false` | — |
| `orientation` | `'vertical' | 'horizontal'` | `'vertical'` | — |
| `label` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | — | required |
| `description` | `string` | — | — |
| `disabled` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Behaviour**
- `Radio` reads the group through context, so it has to sit inside a `RadioGroup`. On its own it throws with a readable message rather than silently doing nothing.
## SegmentedControl
For three or four options that fit on screen, where a select would hide the answer behind a click. It is a radio group, not a tab list: it picks a value rather than swapping a panel, and the arrow keys select as they move, which is how a radio group is meant to behave.
Category: Forms. Docs: https://cock.joshicodes.de/components/segmented-control
```svelte
import { SegmentedControl, SegmentedOption } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `options` | `SegmentedOption[]` | — | required |
| `size` | `'sm' | 'md'` | `'md'` | — |
| `disabled` | `boolean` | `false` | — |
| `label` | `string` | — | Names the group for screen readers. Skip it inside a ``. |
| `stretch` | `boolean` | `false` | Stretch the segments to fill the width instead of hugging their text. |
| `class` | `string` | — | — |
**Keyboard**
- `← / → or ↑ / ↓`: Moves to the next option and selects it
- `Home / End`: First or last option
**Behaviour**
- Reach for `Select` once the list gets long or the labels get wordy; this one stops being readable at about four.
- The whole strip is one stop in the tab order. Tab moves past it, the arrows move inside it.
- `stretch` fills the width, which is what you want in a narrow column or on a phone.
## Switch
An on/off toggle for settings that apply immediately.
Category: Forms. Docs: https://cock.joshicodes.de/components/switch
```svelte
import { Switch } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `checked` | `boolean` | `false` | bindable |
| `disabled` | `boolean` | `false` | — |
| `size` | `'sm' | 'md'` | `'md'` | — |
| `description` | `string` | — | — |
| `labelPosition` | `'start' | 'end'` | `'end'` | Put the label before the switch instead of after it. |
| `class` | `string` | — | — |
| `aria-label` | `string` | — | Needed when the switch has no visible label. |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onchange` | `(checked: boolean) => void` | — | — |
**Behaviour**
- A `role="switch"` button, not a checkbox. Use it for things that take effect at once; use a checkbox inside a form that gets submitted.
## Slider
A styled range input with an optional formatted value.
Category: Forms. Docs: https://cock.joshicodes.de/components/slider
```svelte
import { Slider } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number` | `50` | bindable |
| `min` | `number` | `0` | — |
| `max` | `number` | `100` | — |
| `step` | `number` | `1` | — |
| `disabled` | `boolean` | `false` | — |
| `showValue` | `boolean` | `false` | Show the current value above the track. |
| `format` | `(value: number) => string` | `(v: number) => String(v)` | — |
| `label` | `string` | — | — |
| `class` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onchange` | `(value: number) => void` | — | — |
## PinInput
One box per character, for verification codes.
Category: Forms. Docs: https://cock.joshicodes.de/components/pin-input
```svelte
import { PinInput } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `length` | `number` | `6` | — |
| `type` | `'numeric' | 'alphanumeric'` | `'numeric'` | — |
| `mask` | `boolean` | `false` | Hide the characters, like a password field. |
| `group` | `number` | — | Put a small gap after this many boxes, e.g. 3 for "123 456". |
| `disabled` | `boolean` | `false` | — |
| `invalid` | `boolean` | `false` | — |
| `label` | `string` | `'Verification code'` | Accessible name for the whole group. |
| `class` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `oncomplete` | `(value: string) => void` | — | — |
**Keyboard**
- `Backspace`: Clears the current box, or steps back and clears the previous one
- `← →`: Moves between boxes
- `Paste`: Fills from the box you pasted into, ignoring anything that does not fit
**Behaviour**
- The first box carries `autocomplete="one-time-code"`, so the phone offers the code from the SMS.
## TagInput
Free-text tags with keyboard editing and a limit.
Category: Forms. Docs: https://cock.joshicodes.de/components/tag-input
```svelte
import { TagInput } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string[]` | `[]` | bindable |
| `placeholder` | `string` | `'Add a tag…'` | — |
| `max` | `number` | — | Stop accepting tags at this many. |
| `allowDuplicates` | `boolean` | `false` | — |
| `validate` | `(tag: string) => boolean` | — | Return false to reject a tag. |
| `disabled` | `boolean` | `false` | — |
| `invalid` | `boolean` | `false` | — |
| `id` | `string` | — | — |
| `class` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onchange` | `(tags: string[]) => void` | — | — |
**Keyboard**
- `Enter or ,`: Adds the tag being typed
- `Backspace`: On an empty field, removes the last tag
- `Paste`: Splits on commas and newlines
**Behaviour**
- A rejected tag — duplicate, over the limit or turned down by `validate` — flashes instead of disappearing silently, so Enter never looks broken.
## FileDrop
A `` wrapped around a hidden file input, which is the whole trick: clicking, tabbing to it and pressing Enter all work because the browser already does them. The drag handling is the only part written by hand.
Category: Forms. Docs: https://cock.joshicodes.de/components/file-drop
```svelte
import { FileDrop } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `files` | `File[]` | `[]` | bindable. The files chosen so far, in the order they were picked. |
| `accept` | `string` | — | Same syntax as the native attribute: `.png,.jpg` or `image/*`. |
| `multiple` | `boolean` | `false` | — |
| `disabled` | `boolean` | `false` | — |
| `hint` | `string` | — | Line under the heading, usually what you accept and how big. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | Replaces the default wording inside the box. |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onselect` | `(files: File[]) => void` | — | Runs with the files that passed `accept`, dropped or picked. |
**Keyboard**
- `Tab, then Enter or Space`: Opens the file dialog
**Behaviour**
- `accept` is checked on dropped files too. The browser only enforces it in its own dialog, so a drop would otherwise hand you the PDF you said you did not want.
- Files that fail `accept` are dropped quietly. Compare the count you get against what you expected if you want to say something about it.
- The input is cleared after every pick, so choosing the same file again after removing it still fires.
- `multiple` appends to the list rather than replacing it; a single-file drop zone replaces.
## Label
A form label with an optional required marker.
Category: Forms. Docs: https://cock.joshicodes.de/components/label
```svelte
import { Label } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `for` | `string` | — | — |
| `required` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
## Alert
An inline message in six tones, optionally dismissible.
Category: Feedback. Docs: https://cock.joshicodes.de/components/alert
```svelte
import { Alert } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `AlertVariant` | `'neutral'` | — |
| `title` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `icon` | `Snippet` | — | Replace the built-in status glyph. |
| `children` | `Snippet` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `ondismiss` | `() => void` | — | Renders a dismiss button and calls back when pressed. |
**Behaviour**
- Alerts stay in the layout and stay put. For something that happened elsewhere, use a toast.
## Toaster
Mount `Toaster` once in your root layout, then call `toast()` from anywhere — including outside a component, since the queue is plain state rather than context.
Category: Feedback. Docs: https://cock.joshicodes.de/components/toaster
```svelte
import { Toaster, toast } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `position` | `'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'` | `'bottom-right'` | — |
| `class` | `string` | — | — |
**Behaviour**
- `duration: 0` keeps a toast until it is dismissed, which is what you want for an error with a retry action.
- `toast()` returns an id that `toast.dismiss(id)` takes.
## Progress
A determinate or indeterminate bar.
Category: Feedback. Docs: https://cock.joshicodes.de/components/progress
```svelte
import { Progress } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number` | — | `undefined` renders an indeterminate bar. |
| `max` | `number` | `100` | — |
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | — |
| `variant` | `'accent' | 'success' | 'warning' | 'danger'` | `'accent'` | — |
| `label` | `string` | — | — |
| `class` | `string` | — | — |
## ProgressRing
The round version, for tight spots and dashboards.
Category: Feedback. Docs: https://cock.joshicodes.de/components/progress-ring
```svelte
import { ProgressRing } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number` | — | 0 to `max`. Leave it out for a spinning, indeterminate ring. |
| `max` | `number` | `100` | — |
| `size` | `number` | `40` | Diameter in pixels. |
| `thickness` | `number` | `4` | Stroke width in pixels. |
| `variant` | `'accent' | 'success' | 'warning' | 'danger'` | `'accent'` | — |
| `showValue` | `boolean` | `false` | Print the percentage in the middle. |
| `label` | `string` | — | — |
| `class` | `string` | — | — |
## Tooltip
A short hint on hover and on keyboard focus.
Category: Feedback. Docs: https://cock.joshicodes.de/components/tooltip
```svelte
import { Tooltip } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `content` | `string` | — | required |
| `placement` | `Placement` | `'top'` | — |
| `delay` | `number` | `120` | Milliseconds to wait before showing. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Behaviour**
- Renders into `` and positions itself against its trigger, so an `overflow: hidden` ancestor or a scrolling table cannot clip it.
- Opens on focus as well as hover, so it is reachable without a mouse. Never put anything in it that is only available there.
## Modal
A centred dialog with a backdrop, focus trap and footer slot.
Category: Overlays. Docs: https://cock.joshicodes.de/components/modal
```svelte
import { Modal } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | bindable |
| `title` | `string` | — | — |
| `description` | `string` | — | — |
| `size` | `'sm' | 'md' | 'lg' | 'xl' | 'full'` | `'md'` | — |
| `dismissible` | `boolean` | `true` | Clicking the backdrop closes the modal. |
| `hideClose` | `boolean` | `false` | Hide the built-in close button in the header. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
| `footer` | `Snippet` | — | Pinned to the bottom of the panel, separated by a hairline. |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onclose` | `() => void` | — | — |
**Keyboard**
- `Esc`: Closes, unless `dismissible` is false
- `Tab`: Cycles inside the dialog
**Behaviour**
- Renders into `` and positions itself against its trigger, so an `overflow: hidden` ancestor or a scrolling table cannot clip it.
- Locks body scrolling while open, counted, so nested overlays do not unlock it early.
- Focus moves to the first focusable element and returns to the opener on close.
## Drawer
The same dialog, sliding in from any edge.
Category: Overlays. Docs: https://cock.joshicodes.de/components/drawer
```svelte
import { Drawer } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | bindable |
| `side` | `'left' | 'right' | 'top' | 'bottom'` | `'right'` | — |
| `title` | `string` | — | — |
| `description` | `string` | — | — |
| `size` | `string` | — | Width for left/right, height for top/bottom. |
| `dismissible` | `boolean` | `true` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
| `footer` | `Snippet` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onclose` | `() => void` | — | — |
**Keyboard**
- `Esc`: Closes, unless `dismissible` is false
**Behaviour**
- Renders into `` and positions itself against its trigger, so an `overflow: hidden` ancestor or a scrolling table cannot clip it.
- Takes the modal props plus `side`.
## Popover
A positioned panel anchored to a trigger you supply.
Category: Overlays. Docs: https://cock.joshicodes.de/components/popover
```svelte
import { Popover } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | bindable |
| `placement` | `Placement` | `'bottom-start'` | — |
| `offset` | `number` | `8` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `trigger` | `Snippet<[TriggerAttrs]>` | — | required. Spread the given attrs onto your own button. |
| `children` | `Snippet<[{ close: () => void }]>` | — | required |
**Keyboard**
- `Esc`: Closes and returns focus to the trigger
**Behaviour**
- Renders into `` and positions itself against its trigger, so an `overflow: hidden` ancestor or a scrolling table cannot clip it.
- Flips to the other side when the preferred one does not fit, and stays clear of the viewport edge.
- The trigger snippet receives the attributes to spread, which is what keeps `aria-expanded` correct.
## DropdownMenu
An anchored menu with full keyboard control.
Category: Overlays. Docs: https://cock.joshicodes.de/components/dropdown-menu
```svelte
import { DropdownMenu, MenuItem, MenuLabel, MenuSeparator } from '@joshicodes/cock';
```
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | bindable |
| `placement` | `Placement` | `'bottom-start'` | — |
| `offset` | `number` | `6` | — |
| `class` | `string` | — | — |
| `label` | `string` | `'Menu'` | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `trigger` | `Snippet<[TriggerAttrs]>` | — | required |
| `children` | `Snippet` | — | required |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `href` | `string` | — | Render as a link instead of a button. |
| `disabled` | `boolean` | `false` | — |
| `variant` | `'default' | 'danger'` | `'default'` | — |
| `hint` | `string` | — | Trailing hint, e.g. a keyboard shortcut. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `icon` | `Snippet` | — | Leading icon slot. |
| `children` | `Snippet` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onselect` | `() => void` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Keyboard**
- `↑ ↓`: Moves between items, wrapping at the ends
- `Home / End`: First and last item
- `Esc`: Closes and returns focus to the trigger
- `Tab`: Closes and moves on
**Behaviour**
- Renders into `` and positions itself against its trigger, so an `overflow: hidden` ancestor or a scrolling table cannot clip it.
- Opening with the mouse or the keyboard focuses the first item either way.
## CommandPalette
The ⌘K dialog. Hand it an array of commands with groups, keywords and callbacks; it handles the search, the grouping and the keyboard.
Category: Overlays. Docs: https://cock.joshicodes.de/components/command-palette
```svelte
import { CommandPalette } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | bindable |
| `commands` | `Command[]` | — | required |
| `placeholder` | `string` | `'Type a command or search…'` | — |
| `shortcut` | `string | false` | `'mod+k'` | Toggles the palette from anywhere. `false` turns it off. |
| `empty` | `string` | `'Nothing matches that.'` | — |
| `class` | `string` | — | — |
**Keyboard**
- `⌘K / Ctrl+K`: Opens and closes it, unless `shortcut` says otherwise
- `↑ ↓`: Moves through the results
- `Enter`: Runs the highlighted command
- `Esc`: Closes
**Behaviour**
- Search matches labels and keywords; a plain substring ranks above a scattered match, and a match at the start of a word ranks highest.
- Built as a combobox: focus stays in the input and the active option is announced through `aria-activedescendant`.
## Tabs
Underlined or pill tabs with arrow-key navigation.
Category: Navigation. Docs: https://cock.joshicodes.de/components/tabs
```svelte
import { Tabs, TabList, Tab, TabPanel } from '@joshicodes/cock';
```
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | `''` | bindable |
| `variant` | `TabsVariant` | `'line'` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | — | required |
| `disabled` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | — | required |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Keyboard**
- `← →`: Moves to the previous or next tab and selects it
- `Home / End`: First and last tab
**Behaviour**
- Panels only render while selected, so an expensive panel costs nothing until it is opened.
- The strip scrolls sideways when the tabs do not fit, rather than wrapping or overflowing.
## Accordion
Collapsible sections, one at a time or several at once.
Category: Navigation. Docs: https://cock.joshicodes.de/components/accordion
```svelte
import { Accordion, AccordionItem } from '@joshicodes/cock';
```
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `type` | `'single' | 'multiple'` | `'single'` | `single` closes the others when one opens. |
| `value` | `string | string[]` | `type === 'single' ? '' : []` | bindable. Open item value(s). Bindable. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
###
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `string` | — | required |
| `title` | `string` | — | required |
| `disabled` | `boolean` | `false` | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Behaviour**
- `type="multiple"` takes an array as its bound value; the single variant takes a string.
## Breadcrumb
The trail back up, from an array of items.
Category: Navigation. Docs: https://cock.joshicodes.de/components/breadcrumb
```svelte
import { Breadcrumb } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `items` | `Crumb[]` | — | required |
| `class` | `string` | — | — |
**Behaviour**
- The last item is rendered as the current page and is never a link.
## Pagination
Page numbers with ellipses, and a compact mode on phones.
Category: Navigation. Docs: https://cock.joshicodes.de/components/pagination
```svelte
import { Pagination } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `page` | `number` | `1` | bindable |
| `total` | `number` | — | required |
| `siblings` | `number` | `1` | How many numbered buttons to show around the current page. |
| `class` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `onchange` | `(page: number) => void` | — | — |
**Behaviour**
- Below the `sm` breakpoint the numbers are replaced by the position, because a dozen buttons do not fit on a phone.
## Table
It styles `thead` and `tbody`; the markup stays yours. Sorting and selection are deliberately left out, because every project wants them slightly differently.
Category: Data. Docs: https://cock.joshicodes.de/components/table
```svelte
import { Table } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `tone` | `'neutral' | 'accent'` | `'neutral'` | `accent` carries the theme colour into the header, stripes and hover. |
| `striped` | `boolean` | `false` | Tint every other row. |
| `hoverable` | `boolean` | `true` | Highlight rows on hover. |
| `dense` | `boolean` | `false` | — |
| `bordered` | `boolean` | `true` | Wrap in a bordered, scrollable container. |
| `caption` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | — |
**Behaviour**
- `tone="accent"` carries the theme colour into the header, the stripes and the hover state.
- `` marks a selected row in either tone.
- The wrapper scrolls sideways, so a wide table does not break the page on a phone.
## Stat
A single number with a label and a trend.
Category: Data. Docs: https://cock.joshicodes.de/components/stat
```svelte
import { Stat } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `label` | `string` | — | required |
| `value` | `string | number` | — | required |
| `change` | `string` | — | e.g. `+12.5%` — coloured by `trend`. |
| `trend` | `'up' | 'down' | 'flat'` | `'flat'` | — |
| `hint` | `string` | — | — |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `icon` | `Snippet` | — | — |
## Sparkline
A trend line that fills its container, without a chart library.
Category: Data. Docs: https://cock.joshicodes.de/components/sparkline
```svelte
import { Sparkline } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `data` | `number[]` | — | required |
| `height` | `number` | `32` | Drawn height in pixels; the width follows the container. |
| `fill` | `boolean` | `true` | Fill the area under the line with a fade. |
| `dot` | `boolean` | `true` | Mark the last value with a dot. |
| `strokeWidth` | `number` | `1.75` | — |
| `label` | `string` | — | Describe the trend for screen readers, e.g. "Up 12% this week". |
| `class` | `string` | — | Colour comes from `currentColor`; the default is the accent. |
**Behaviour**
- The line keeps its stroke width while the SVG stretches, and the end dot stays round.
- Give it a `label`: to a screen reader a sparkline is a picture and needs describing.
## Heatmap
One cell per value, shaded with the accent.
Category: Data. Docs: https://cock.joshicodes.de/components/heatmap
```svelte
import { Heatmap } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `data` | `number[]` | — | required. One number per cell, filled column by column. |
| `rows` | `number` | `7` | Cells per column; 7 gives one week per column. |
| `cellSize` | `number` | `11` | Cell edge in pixels. Ignored with `fit`. |
| `fit` | `boolean` | `false` | Stretch the cells so the grid fills its container's width. |
| `minCell` | `number` | `7` | Smallest a cell may get while fitting. Below that the grid scrolls. |
| `gap` | `number` | `3` | — |
| `levels` | `number` | `4` | Number of colour steps above zero. |
| `max` | `number` | — | Value that counts as full colour. Defaults to the largest value. |
| `describe` | `(value: number, index: number) => string` | `(value) => String(value)` | Tooltip text for a cell. |
| `legend` | `boolean` | `true` | — |
| `label` | `string` | — | Summary for screen readers. |
| `class` | `string` | — | — |
**Behaviour**
- `fit` fills the width, down to `minCell`; past that the grid scrolls instead of becoming unreadable.
## Typewriter
Types a line, pauses, deletes it and moves to the next.
Category: Effects. Docs: https://cock.joshicodes.de/components/typewriter
```svelte
import { Typewriter } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `text` | `string | string[]` | — | required. One string, or several to cycle through. |
| `speed` | `number` | `55` | Milliseconds per typed character. |
| `deleteSpeed` | `number` | `28` | Milliseconds per deleted character. |
| `pause` | `number` | `1800` | How long a finished string stays before it is deleted. |
| `delay` | `number` | `0` | Wait before the first character. |
| `loop` | `boolean` | — | Keep cycling. Defaults to true when there is more than one string. |
| `humanize` | `boolean` | `true` | Vary the rhythm a little instead of typing like a metronome. |
| `reserve` | `boolean` | `false` | Reserve the width of the longest string so surrounding text does not move. |
| `caret` | `boolean` | `true` | — |
| `caretShape` | `'bar' | 'block' | 'underline'` | `'bar'` | — |
| `hideCaretWhenDone` | `boolean` | `false` | Remove the caret once the last string is typed and nothing else will happen. |
| `class` | `string` | — | — |
| `caretClass` | `string` | — | — |
**Events**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `oncomplete` | `() => void` | — | — |
**Behaviour**
- `reserve` holds the width of the longest string, so the text around it does not jump.
- Skips straight to the final state when the visitor has reduced motion turned on.
- Screen readers get the finished text, not the animation.
## Terminal
A fake shell session that types itself.
Category: Effects. Docs: https://cock.joshicodes.de/components/terminal
```svelte
import { Terminal } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `lines` | `TerminalLine[]` | — | required |
| `title` | `string` | — | Shown in the bar above the output, e.g. a working directory. |
| `prompt` | `string` | `'$'` | — |
| `speed` | `number` | `45` | Milliseconds per typed character. |
| `lineDelay` | `number` | `300` | Default pause between lines. |
| `loop` | `boolean` | `false` | Start over a few seconds after the last line. |
| `startOnView` | `boolean` | `true` | Wait until the terminal scrolls into view. |
| `class` | `string` | — | — |
**Behaviour**
- Reserves the height of every line up front, so the box does not grow while it types.
- Waits until it scrolls into view unless `startOnView` is false.
- Screen readers get the finished text, not the animation.
## Caret
The blinking bar on its own, in three shapes.
Category: Effects. Docs: https://cock.joshicodes.de/components/caret
```svelte
import { Caret } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `shape` | `'bar' | 'block' | 'underline'` | `'bar'` | — |
| `blink` | `boolean` | `true` | Turn blinking off, e.g. while text is being typed. |
| `class` | `string` | — | — |
## Scramble
Settles from random glyphs into the real text.
Category: Effects. Docs: https://cock.joshicodes.de/components/scramble
```svelte
import { Scramble } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `text` | `string` | — | required |
| `duration` | `number` | `900` | Total time until every character has settled, in milliseconds. |
| `glyphs` | `string` | `'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#%&*+=/<>'` | Characters to cycle through before settling. |
| `trigger` | `'mount' | 'visible' | 'hover'` | `'visible'` | When to play. Changing `text` always plays it again. |
| `class` | `string` | — | — |
**Behaviour**
- Plays on scroll, on hover or whenever the text changes.
- Skips straight to the final state when the visitor has reduced motion turned on.
- Screen readers get the finished text, not the animation.
## CountUp
Counts to a number when it scrolls into view.
Category: Effects. Docs: https://cock.joshicodes.de/components/count-up
```svelte
import { CountUp } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number` | — | required |
| `duration` | `number` | `1200` | Milliseconds per run. |
| `decimals` | `number` | `0` | — |
| `locale` | `string` | — | — |
| `format` | `(value: number) => string` | — | Custom formatting; overrides `decimals` and `locale`. |
| `prefix` | `string` | `''` | — |
| `suffix` | `string` | `''` | — |
| `startOnView` | `boolean` | `true` | Wait until the number scrolls into view before counting. |
| `class` | `string` | — | — |
**Behaviour**
- After the first run it animates from the old value to the new one.
- Skips straight to the final state when the visitor has reduced motion turned on.
- Screen readers get the finished text, not the animation.
## Odometer
Digits that roll like a mechanical counter.
Category: Effects. Docs: https://cock.joshicodes.de/components/odometer
```svelte
import { Odometer } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `value` | `number` | — | required |
| `locale` | `string` | — | — |
| `options` | `Intl.NumberFormatOptions` | — | Passed to Intl.NumberFormat, e.g. `{ style: 'percent' }`. |
| `duration` | `number` | `700` | Milliseconds per roll. |
| `class` | `string` | — | — |
**Behaviour**
- Digits are keyed from the right, so the ones column stays put as the number grows.
## Marquee
A looping strip that pauses on hover.
Category: Effects. Docs: https://cock.joshicodes.de/components/marquee
```svelte
import { Marquee } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `speed` | `number` | `40` | Pixels per second. |
| `direction` | `'left' | 'right'` | `'left'` | — |
| `pauseOnHover` | `boolean` | `true` | — |
| `gap` | `string` | `'2.5rem'` | Space between items, any CSS length. |
| `fade` | `boolean` | `true` | Fade the content out towards both edges. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | required |
**Behaviour**
- Repeats the content enough times to cover the container, so the loop never shows a gap.
## Reveal
Fades content in as it scrolls into view.
Category: Effects. Docs: https://cock.joshicodes.de/components/reveal
```svelte
import { Reveal } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `delay` | `number` | `0` | Milliseconds before the transition starts; use it to stagger siblings. |
| `duration` | `number` | `500` | — |
| `distance` | `number` | `14` | How far the content travels up, in pixels. |
| `once` | `boolean` | `true` | Reveal once, or hide again whenever it leaves the viewport. |
| `threshold` | `number` | `0.15` | Share of the element that must be visible to trigger. |
| `appear` | `boolean` | `false` | Play the entrance even when the element is already on screen at mount. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | required |
**Behaviour**
- Content already on screen at load is left alone, unless you pass `appear`.
- Stagger a row by giving each one a larger `delay`.
- Skips straight to the final state when the visitor has reduced motion turned on.
## Highlight
A marker stroke that draws itself across the text.
Category: Effects. Docs: https://cock.joshicodes.de/components/highlight
```svelte
import { Highlight } from '@joshicodes/cock';
```
**Props**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `variant` | `'marker' | 'underline'` | `'marker'` | A broad marker stroke behind the text, or a line under it. |
| `color` | `string` | — | Any CSS colour. Defaults to a translucent accent. |
| `duration` | `number` | `800` | — |
| `delay` | `number` | `0` | — |
| `trigger` | `'visible' | 'mount'` | `'visible'` | Draw once the text is on screen, or straight away. |
| `class` | `string` | — | — |
**Snippets**
| Name | Type | Default | Notes |
| --- | --- | --- | --- |
| `children` | `Snippet` | — | required |
**Behaviour**
- Survives a line break, because the stroke is a background rather than a box.
- Skips straight to the final state when the visitor has reduced motion turned on.