Buttons & actions
PressButton
Compresses slightly on press, then settles
detent-press-button
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 2
- Wave
- phase-2-controls
- Group
- Buttons & actions
4.3 KB gzip for the class module · inlines the shared spring
Variants
Recorded states
focus-visiblerecorded state; no setterpressedrecorded state; no setterdisabledrecorded state; no setterdark themerecorded state; no setterreduced motionrecorded state; no setter
Usage
Import
import '/components/press-button/define.js';
// or register it yourself
import { DetentPressButton } from '/components/press-button/press-button.js';
Attributes
disabledfull-widthpress-strengthsizevariant
Properties
variantsizepressStrengthfullWidthdisabled
Slots
default
Events
No events are recorded for this component.
Example
Runnable example: site/examples/press-button.ts. The snippet below is generated from the catalogued attributes.
<detent-press-button
disabled
full-width
press-strength="0.04"
size="md"
variant="primary"
></detent-press-button>
Source
The pinned upstream source for this row:
src/components/ui/PressButton.tsxsha256 6f985aaeb748…
Native files
The native module and its artifacts:
src/components/press-button/press-button.tssrc/components/press-button/styles.csssrc/components/press-button/define.tsdocs/components/press-button.mdsite/examples/press-button.tstests/browser/press-button.spec.tsevidence/components/press-button.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
disabled | disabled | boolean | false | Presence semantics: present means true, absent means false. |
full-width | fullWidth | boolean | false | Presence semantics: present means true, absent means false. |
press-strength | pressStrength | number | 0.04 | Kebab-case attribute; camelCase property. |
size | size | 'sm' | 'md' | 'lg' | 'icon' | 'md' | Kebab-case attribute; camelCase property. |
variant | variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'primary' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'primary' | Property-only: assign from script; not accepted as an attribute. |
size | 'sm' | 'md' | 'lg' | 'icon' | 'md' | Property-only: assign from script; not accepted as an attribute. |
pressStrength | number | 0.04 | Property-only: assign from script; not accepted as an attribute. |
fullWidth | boolean | false | Property-only: assign from script; not accepted as an attribute. |
disabled | boolean | false | Property-only: assign from script; not accepted as an attribute. |
Events
No custom events are recorded for this component.
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The layered scaleX/scaleY and whileTap compose into one press amount on the shared spring with the upstream springSnappy constants.
- `press-strength` clamps to 0.01-0.06 and reflects its normalised form; the size scale applies to ghost too, because upstream skipped size classes for ghost and left no minimum height (WCAG 2.5.8).
- Disabled mutes surface and text without fading text (upstream opacity-30 measured 1.9:1); the secondary/outline boundary is a color-mix of the text token because upstream --border measures 1.4:1 on white.
- Hover mixes the surface instead of hover:opacity-90; reduced motion removes the squash and keeps a brightness cue.
- aria-label and aria-labelledby are forwarded to the internal control so `size="icon"` has a name.
What it does
A button that compresses slightly on press — a layered scaleX/scaleY squash composed with the tap scale — and settles back with the snappy spring, so the interaction feels physical without moving the layout.
Upstream behaviour
- Layered scaleX + scaleY squash for an organic compression on press
- Tunable pressStrength (0-1) to control how much the button compresses
- Snappy spring with a tiny overshoot so the button "settles" instead of snapping
- 4 visual variants: Primary, Secondary, Outline, Ghost — light/dark theme aware
- 4 size dimensions: Small (sm), Medium (md), Large (lg), and square Icon
- Full keyboard accessibility with focus-ring and disabled state
Differences from upstream
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The layered scaleX/scaleY and whileTap compose into one press amount on the shared spring with the upstream springSnappy constants.
- `press-strength` clamps to 0.01-0.06 and reflects its normalised form; the size scale applies to ghost too, because upstream skipped size classes for ghost and left no minimum height (WCAG 2.5.8).
- Disabled mutes surface and text without fading text (upstream opacity-30 measured 1.9:1); the secondary/outline boundary is a color-mix of the text token because upstream --border measures 1.4:1 on white.
- Hover mixes the surface instead of hover:opacity-90; reduced motion removes the squash and keeps a brightness cue.
- aria-label and aria-labelledby are forwarded to the internal control so `size="icon"` has a name.