Buttons & actions
Button
Primary, secondary, outline, ghost and destructive actions
detent-button
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 1
- Wave
- phase-1-pilot
- Group
- Buttons & actions
4.5 KB gzip for the class module
Variants
Recorded states
focus-visiblerecorded state; no setterpressedrecorded state; no setterreduced motionrecorded state; no setter
Usage
Import
import '/components/button/define.js';
// or register it yourself
import { DetentButton } from '/components/button/button.js';
Attributes
disabledfull-widthis-loadingloading-textsizevariant
Properties
No properties are recorded for this component.
Slots
defaultleft-iconright-icon
Events
No events are recorded for this component.
Example
Runnable example: site/examples/button.ts. The snippet below is generated from the catalogued attributes.
<detent-button
disabled
full-width
is-loading
loading-text
size="md"
variant="primary"
></detent-button>
Source
The pinned upstream source for this row:
src/components/ui/Button.tsxsha256 9ea5b0730067…
Native files
The native module and its artifacts:
src/components/button/button.tssrc/components/button/styles.csssrc/components/button/define.tsdocs/components/button.mdsite/examples/button.tstests/browser/button.spec.tsevidence/components/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. |
is-loading | isLoading | boolean | false | Presence semantics: present means true, absent means false. |
loading-text | loadingText | string | undefined | Kebab-case attribute; camelCase property. |
size | size | 'sm' | 'md' | 'lg' | 'icon' | 'md' | Kebab-case attribute; camelCase property. |
variant | variant | 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'success' | 'link' | 'gradient' | 'primary' | Kebab-case attribute; camelCase property. |
Properties
No property-only values are recorded for this component.
Events
No custom events are recorded for this component.
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
left-icon | Consumer content; rendered through a slot, never re-created. |
right-icon | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Native form surface added by the Phase 1 plan: type (button|submit|reset, default button preserving upstream), name, value, static formAssociated, forwarded submit/reset to the owning form or form="<id>". Not recorded upstream.
- type=submit forwards through HTMLFormElement.prototype.requestSubmit with a transient hidden native submit button carrying the submitter name/value, because requestSubmit(host) throws TypeError for a form-associated custom element on Chromium, Firefox and WebKit (F-3); event.submitter in a submit listener is that proxy, and an detent-button is never the form's implicit submit button.
- variant="default" from the upstream source type union is not in the catalogued variant list; it resolves to primary (visually identical) with one development console.warn per distinct value.
- disabled="false" disables (HTML presence semantics); variant/size/type reflect their resolved value, are rewritten on invalid input, and warn once per distinct invalid value; name/value reflect because the entry name is read from the content attribute.
- No custom events: the native composed click is used (one per activation), host.click() activates the action like button.click(), and programmatic property assignment emits nothing.
- Framer Motion whileTap scale 0.97 is a CSS :active transform with no transition under prefers-reduced-motion: reduce.
- Tinted destructive and success variants mix the hue with the theme surface and text tokens; the pinned upstream hex pair measured 3.73:1 on a light surface (WCAG AA needs 4.5:1). Hover fills are solid 700-level hues so white stays at least 6.29:1.
What it does
A form-aware button with eight visual variants, four sizes plus an icon size, a loading state and optional leading or trailing icons. It submits, resets and validates like a native button.
Upstream behaviour
- 8 visual variants: Primary, Secondary, Outline, Ghost, Destructive, Success, Link, Gradient
- 4 size dimensions: Small (sm), Medium (md), Large (lg), and square Icon
- Spring tap micro-interaction (whileTap 0.97) via Framer Motion
- Accessible loading state with integrated monochrome spinner & aria-busy
- Left and right icon slots with automatic sizing and gap alignment
- Full width layout support (fullWidth)
- Strictly adheres to EasyUI monochrome dark palette and sky focus ring
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Native form surface added by the Phase 1 plan: type (button|submit|reset, default button preserving upstream), name, value, static formAssociated, forwarded submit/reset to the owning form or form="<id>". Not recorded upstream.
- type=submit forwards through HTMLFormElement.prototype.requestSubmit with a transient hidden native submit button carrying the submitter name/value, because requestSubmit(host) throws TypeError for a form-associated custom element on Chromium, Firefox and WebKit (F-3); event.submitter in a submit listener is that proxy, and an detent-button is never the form's implicit submit button.
- variant="default" from the upstream source type union is not in the catalogued variant list; it resolves to primary (visually identical) with one development console.warn per distinct value.
- disabled="false" disables (HTML presence semantics); variant/size/type reflect their resolved value, are rewritten on invalid input, and warn once per distinct invalid value; name/value reflect because the entry name is read from the content attribute.
- No custom events: the native composed click is used (one per activation), host.click() activates the action like button.click(), and programmatic property assignment emits nothing.
- Framer Motion whileTap scale 0.97 is a CSS :active transform with no transition under prefers-reduced-motion: reduce.
- Tinted destructive and success variants mix the hue with the theme surface and text tokens; the pinned upstream hex pair measured 3.73:1 on a light surface (WCAG AA needs 4.5:1). Hover fills are solid 700-level hues so white stays at least 6.29:1.