Button
ui actionform
Polymorpher Button mit Varianten (default, primary, ghost, danger) und Größen (default, square für Icon-Buttons). forwardRef, reicht alle nativen <button>-Attribute durch. aria-pressed=true rendert den aktiven Toggle-Zustand.
Abhängigkeiten
Barrierefreiheit
- Echtes <button>-Element — Tastatur/Fokus/Disabled kommen nativ.
- Sichtbarer :focus-visible-Ring (--focus-ring).
- Toggle-Zustand über aria-pressed="true" stylebar.
Beispiele
Varianten
<Button>Default</Button>
<Button variant="primary">Speichern</Button>
<Button variant="ghost">Abbrechen</Button>
<Button variant="danger">Löschen</Button> Icon-Button (square)
<Button size="square" aria-label="Einstellungen">
<SettingsIcon />
</Button> Genutzte Tokens · 17
--accent--accent-active--accent-hover--accent-on--bg--border--control-height--danger--ease-standard--fg--field-pad-x--focus-ring--motion-fast--radius-sm--space-2--surface--text-sm
Quelldateien
components/ui/Button.tsx tsx
import { forwardRef, type ButtonHTMLAttributes } from 'react';
import styles from './Button.module.css';
type Variant = 'default' | 'primary' | 'ghost' | 'danger';
type Size = 'default' | 'square';
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: Variant;
size?: Size;
}
export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
{ variant = 'default', size = 'default', className, children, ...rest },
ref,
) {
const cls = [styles.btn, styles[variant], size === 'square' ? styles.square : '', className]
.filter(Boolean)
.join(' ');
return (
<button ref={ref} className={cls} {...rest}>
{children}
</button>
);
});
components/ui/Button.module.css css
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
height: var(--control-height);
padding: 0 var(--field-pad-x);
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--surface);
color: var(--fg);
font-size: var(--text-sm);
font-weight: 500;
line-height: 1;
box-sizing: border-box;
transition:
background-color var(--motion-fast) var(--ease-standard),
border-color var(--motion-fast) var(--ease-standard),
transform var(--motion-fast) var(--ease-standard);
}
.btn:hover {
border-color: color-mix(in oklab, var(--border), var(--fg) 25%);
background: var(--bg);
}
.btn:active {
transform: translateY(1px);
}
.btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.btn[aria-pressed='true'] {
background: var(--fg);
color: var(--bg);
border-color: var(--fg);
}
.btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn:disabled:hover {
background: var(--surface);
border-color: var(--border);
}
.primary:disabled:hover {
background: var(--accent);
border-color: var(--accent);
}
.btn :global(svg) {
flex-shrink: 0;
}
.primary {
background: var(--accent);
color: var(--accent-on);
border-color: var(--accent);
}
.primary:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
}
.primary:active {
background: var(--accent-active);
}
.danger {
background: var(--danger);
color: #fff;
border-color: var(--danger);
}
.danger:hover {
background: color-mix(in oklab, var(--danger), black 12%);
border-color: color-mix(in oklab, var(--danger), black 12%);
}
.danger:active {
background: color-mix(in oklab, var(--danger), black 20%);
}
.danger:disabled:hover {
background: var(--danger);
border-color: var(--danger);
}
.ghost {
border-color: transparent;
background: transparent;
}
.ghost:hover {
background: var(--bg);
border-color: var(--border);
}
.square {
width: var(--control-height);
padding: 0;
}
Holen
# MCP (Claude Code / Cursor)
get_component({ name: "button" })
# Registry direkt
https://www.ekklesi.tools/design/registry/components/button.json