Passer au contenu principal

Button

Implemented

Primary interactive button — variants: primary (teal CTA), secondary (outlined), ghost (text-only), danger (destructive). Sizes: sm/md/lg all enforce WCAG 2.5.5 44px tap-min (var(--ep-tap-min)); size changes padding and font-size, not height.

Categoryatoms
Sourcepatterns/atoms/button.html
AI HintThe universal action control — primary (theme-stable btn-primary surface, white text), secondary, ghost and danger variants in sm/md/lg, all pinned to the 44px tap floor. Use loading to swap the label for an inline spinner during async actions.

Native action control with four visual variants (primary, secondary, ghost, danger), three size scales, and a loading state. D-HOME-P3e 2026-08-11: primary uses white text on teal for stronger subjective contrast. FR-CA: Bouton d'action — principal, secondaire, discret, destructif.

Button

Interactive atom (Family 1 — Tier 1)

A native <button> with consistent variants, sizes, and loading state. Inherits all standard React.ButtonHTMLAttributes (onClick, disabled, type, aria-*, data-*, etc.).

This component supports light and dark mode.

Usage

import { Button } from '@epasslive/design-system'

<Button variant="primary" onClick={save}>Enregistrer</Button>
<Button variant="secondary" onClick={cancel}>Annuler</Button>
<Button variant="ghost" onClick={goBack}>Retour</Button>
<Button variant="danger" onClick={handleDelete}>Supprimer</Button>

// Sizes
<Button size="sm">Compact</Button>
<Button size="md">Default</Button>
<Button size="lg">Large</Button>

// States
<Button loading>En cours...</Button>
<Button disabled>Indisponible</Button>
<Button fullWidth>Passer à l'étape suivante</Button>

// Form integration
<Button type="submit" variant="primary">Soumettre</Button>

When to use

  • primary — submit, save, CTA. One per view max.
  • secondary — cancel, back, alternate flow.
  • ghost — tertiary actions, link-styled helpers.
  • danger — delete, refund, revoke. Always pair with a confirm dialog.

Variants

  • primary — bg-ep-btn-primary text-ep-text-inverse hover:bg-ep-btn-primary-hover. White text on teal (D-HOME-P3e 2026-08-11).
  • secondary — outlined teal border + teal text + teal/10 hover bg.
  • ghost — teal text only + teal/10 hover bg. No border.
  • danger — bg-ep-semantic-error-bg text-ep-text-inverse. Red surface, white text.

Sizes

All sizes are pinned to min-h-[var(--ep-tap-min)] (44px) for WCAG 2.5.5 (44×44px minimum touch target). The visual difference is padding and font-size only:

  • sm — px-3 text-ep-sm (12px horizontal padding, 14px text)
  • md — px-4 text-ep-sm (16px horizontal padding, 14px text) — default
  • lg — px-5 text-ep-base (20px horizontal padding, 16px text)

States

  • default — interactive, full opacity.
  • hover — primary darkens via bg-ep-btn-primary-hover; secondary/ghost gain bg-ep-text-teal/10 overlay; danger reduces opacity 10%.
  • focus — 2px teal focus ring + 2px ring offset (focus-visible:ring-2 focus-visible:ring-ep-teal focus-visible:ring-offset-2).
  • disabled — disabled:opacity-70 disabled:cursor-not-allowed. Interaction is blocked via the native disabled attribute.
  • loading — sets aria-busy="true" + disables interaction. Visual spinner placeholder (DS-INT backlog).

Accessibility

The root is a native <button> with a visible focus ring (focus-visible:ring-2 focus-visible:ring-ep-teal focus-visible:ring-offset-2). When loading is true the component sets aria-busy="true" and disables interaction. Contrast meets WCAG 2.2 AA in both light and dark modes (D-HOME-P3e 2026-08-11: primary now uses white text on teal for stronger subjective contrast).

Dark mode

Primary uses bg-ep-btn-primary (teal, theme-stable) with text-ep-text-inverse (white in both themes since D-HOME-P3e). Danger uses bg-ep-semantic-error-bg with text-ep-text-inverse. Both tuned for WCAG 2.2 AA.

Inherited props

As a native <button> wrapping React.ButtonHTMLAttributes, the component accepts all standard HTML button attributes: onClick, type (button/submit/reset),disabled, name, value, form, formAction, autoFocus,aria-*, data-*, etc. children is the only prop excluded from the inheritance (typed explicitly to React.ReactNode).

Props

Prop
Type
Default
Description
variant
'primary' | 'secondary' | 'ghost' | 'danger'
primary

Visual intent of the action.

size
'sm' | 'md' | 'lg'
md

Padding scale (px-3/4/5) and font-size (text-ep-sm/base). All sizes are pinned to min-h-[var(--ep-tap-min)] (44px) for WCAG 2.5.5 — the visual difference is density, not height.

children*
React.ReactNode
—

Visible label content (text or node). The component is a native <button> so the label is announced by screen readers.

loading
boolean
false

When true: sets aria-busy="true" on the button, disables interaction, and the rendered content shows a spinner placeholder. (Note: spinner rendering is currently a label-only stub — visual spinner is tracked in DS-INT backlog.)

fullWidth
boolean
false

Stretches the button to fill its container width (w-full).

Props

Preview

Button

Prop
Type
Required
Default
Description
variant
'primary' | 'secondary' | 'ghost' | 'danger'
Optional
'primary'
No description.
size
'sm' | 'md' | 'lg'
Optional
'md'
No description.
children
React.ReactNode
Required
—
No description.
onClick
React.MouseEventHandler<HTMLButtonElement>
Optional
—
No description.
disabled
boolean
Optional
False
No description.
loading
boolean
Optional
False
No description.
type
'button' | 'submit' | 'reset'
Optional
'button'
No description.
fullWidth
boolean
Optional
False
No description.
trailing
React.ReactNode
Optional
undefined

Content after a decorative divider at the end of the button, e.g. the cart total on Buy now (`Buy now | $39.99`). Part of the accessible name.

Quick use:<Button />

Accessibility

ARIA rolebutton

Type

1 rule

NASA Rule 5

children must be provided and non-empty

ARIA

1 rule

WCAG 4.1.3

Loading state must set aria-busy='true' on the element

Usage

Import

tsxSnippet
import { Button } from "@epasslive/design-system"

Example 1

tsxSnippet
<Button variant="primary">Submit</Button>

Example 2

tsxSnippet
<Button variant="secondary" size="sm">Cancel</Button>

Anti-patterns

— Do not compose atoms directly — use molecules instead

— Avoid inline styles on atoms

Design System v4.1.0 | ePassLive