# Button

Use for actions. For navigation use `href`, which renders a link that looks like a button.
Built on React Aria's Button, so keyboard, focus and press behavior come from React Aria.

## Props

```txt
variant: 'text' | 'outlined' | 'contained' (default 'text')
color: 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info' | 'inherit' or any
  other key of theme.palette (default 'primary'). A custom key needs main, dark and
  contrastText in the theme. Declare it in `ButtonColorOverrides` for autocomplete.
size: 'small' | 'medium' | 'large' (default 'medium')
startIcon, endIcon: ReactNode placed before and after the label. The library ships no icons. Use any
  icon set or an inline svg, and mark the svg aria-hidden (the label names the button).
fullWidth: boolean (default false)
disabled: boolean (default false)
disableElevation: boolean. Removes the contained variant's shadow.
disableRipple: boolean. Turns off the Material look's press ripple.
href: string. Renders an <a>. Also takes target, rel and download.
loading: boolean. Shows an indicator and ignores presses. The button stays focusable.
loadingPosition: 'start' | 'center' | 'end' (default 'center')
loadingIndicator: ReactNode. Replaces the default spinner.
onClick: (event) => void. Receives the real click event.
type, name, value, form, autoFocus, aria-*, data-*, and every other HTML prop of a button.
sx, className, style, ref, children.
```

Accepted and ignored, so they never reach the DOM: `centerRipple`, `focusRipple`, `disableFocusRipple`, `disableTouchRipple`, `TouchRippleProps`, `touchRippleRef`, `focusVisibleClassName`, `LinkComponent` and `action`. Not supported: the `component` prop. Button drops `component` and logs a one-time development warning. For an upload button, render a hidden file input and call its `click()` from the Button's `onClick`.
Theme `components.Button.defaultProps` works. The label follows `typography.button`, including `textTransform` (capitals in the Material look, none in Minimal).

## Do

```tsx
<Button variant="contained" onClick={save}>Save</Button>
<Button variant="outlined" startIcon={<SaveIcon aria-hidden />} loading={saving}>Save</Button>
<Button href="/docs" target="_blank" rel="noopener">Docs</Button>
<Button type="submit" variant="contained" color="success">Send</Button>
```

## Don't

```tsx
<Button onClick={() => router.push('/x')}>Go</Button>  ← use href so it is a real link
<Button disabled onClick={save}>Save</Button>  ← no need to guard; onClick never fires when disabled
<Button><SaveIcon /></Button>  ← icon-only buttons belong in IconButton
```

## Client-side routers

`href` renders React Aria's `Link`, so by default it does a full page load. To route on the
client (Next.js App Router, React Router), wrap the app once in React Aria's `RouterProvider`.
`LinkComponent` stays ignored. Ctrl- and Cmd-click still open a new tab.

```tsx
'use client';
import { useRouter } from 'next/navigation'; // React Router: useNavigate()
import type { ReactNode } from 'react';
import { RouterProvider } from 'react-aria-components';

export function Routing({ children }: { children: ReactNode }) {
  const router = useRouter();
  return <RouterProvider navigate={router.push}>{children}</RouterProvider>;
}
```

## Accessibility

`onClick` runs before the browser's default action for the mouse, Enter and Space, so
`event.preventDefault()` stops a form submit or a link navigation. A loading button keeps focus and sets `aria-disabled`. React Aria announces the change. A
disabled link gets `aria-disabled` and keeps its `href`. Text on `contained` and `outlined`
buttons meets 4.5:1 in both looks and modes, except the default warning and info colors in
light mode and error in dark mode, which the Material look keeps.
