---
titulo: Botón
resumen: Lanza una acción o lleva al siguiente paso. En la landing es una
  píldora en mono y mayúsculas; en el portal, el botón de `button.css`.
estado: produccion
funcion: acciones
nivel: atomo
creado: 2026-10-11
actualizado: 2026-10-13
tokens:
  - cta
  - cta-ink
  - cta-hover
  - line-2
  - paper-fixed
  - ink-fixed
  - deep-ink
  - deep-cta-hover
  - ink
  - ink-hover
  - paper
  - paper-2
  - line-strong
  - danger
relacionados:
  - fundamentos/paleta-jerte
  - iniciativas/boton-unificado
plataformas:
  web:
    estado: produccion
    superficies:
      - id: landing
        fuente: public/site/report.css
        clases:
          - pill
          - pill-accent
          - pill-light
      - id: portal
        fuente: public/site/button.css
        clases:
          - button
          - button-outline
          - button-ghost
          - button-subtle
          - button-danger
          - button-light
          - button-small
          - button-block
      - id: portal
        fuente: src/components/ui/button.tsx
  ios:
    estado: sin-iniciar
  android:
    estado: sin-iniciar
id: componentes/boton
url: /designloop/componentes/boton
sync: ok
---

# Botón

Lanza una acción o lleva al siguiente paso. En la landing es una píldora en mono y mayúsculas; en el portal, el botón de `button.css`.

## Cuándo usarlo

- Para una acción: reservar una llamada, empezar la suscripción, enviar un formulario, guardar cambios.
- Para ir a un destino clave cuando ese destino es la acción del bloque, como «Área clientes».

## Cuándo no

- Dentro de un párrafo o una lista: usa un enlace normal.
- Para cambiar de vista dentro de la misma página: usa pestañas.

## Reglas

| Regla | Cómo se comprueba |
| --- | --- |
| Un solo botón relleno por grupo de acciones. | Landing: como mucho un `.pill-accent` por `.actions`. Portal: como mucho un `.button` sin variante. |
| En la landing, el botón principal es oliva (`.pill-accent`); el secundario, contorno (`.pill`). | Sin colores de acento como relleno salvo `--cta`. |
| Sobre azul cielo el botón es `.pill-light`; dentro de una hoja oliva, `.pill-accent` se invierte solo (blanco con texto oliva). | Ningún acento sobre otro acento (ver [Paleta Jerte](/designloop/fundamentos/paleta-jerte)). |
| La etiqueta es un verbo y su objeto, corta y aprobada. | Los textos salen del diccionario i18n; no se reescriben sin petición de Andrés. |
| Altura: 38 px en la landing (36 px por debajo de 960 px); 44 px en el portal y 36 px en la variante pequeña. | `height` de `.pill`; `min-height` de `.button` y `.button-small`. |

## Antipatrones

- **Doble principal:** dos botones rellenos uno al lado del otro.
- **Acento como relleno:** en la landing, tierra y cielo no rellenan botones; en el portal, `--accent` es para el foco y la selección.
- **Sinónimos de clase:** no existen `.btn`, `.button-primary` ni `.button-secondary`. Usa solo las clases de la API.
- **Botón como enlace de párrafo:** un botón en mitad de un texto rompe la lectura.

## Accesibilidad

- `<a>` cuando navega y `<button type="button">` o `type="submit"` cuando actúa.
- El foco visible es global: en la landing, contorno de 2 px en `--ink` separado 3 px; en el portal, 2 px en `--accent` separado 2 px.
- Los enlaces externos (la reserva de llamada) llevan `target="_blank"` y `rel="noopener noreferrer"`.
- En el portal, `disabled` baja la opacidad al 50 % y desactiva el puntero; `SubmitButton` añade `aria-busy` mientras envía.

## Dos fuentes

Entre el 11 y el 12 de octubre de 2026 la landing y el portal compartieron `public/site/button.css` ([imandresochoa/designloop#11](https://github.com/imandresochoa/designloop/pull/11)). El rediseño de la landing la pasó a la píldora de `report.css`; `button.css` queda solo para el portal. Volver a unificarlos sería una [iniciativa](/designloop/iniciativas/boton-unificado).

# Web

## Landing

Píldoras en `public/site/report.css`: mono, mayúsculas, 38 px de alto y totalmente redondas.

### Principal y secundario

```html preview superficie=landing
<div class="actions">
  <a class="pill pill-accent" href="#">Empezar</a>
  <a class="pill" href="#">Escríbenos</a>
</div>
```

### Claro, sobre azul cielo

La hoja de la llamada: el botón no puede ser oliva (un acento sobre otro).

```html preview superficie=landing
<div class="sheet call-sheet is-signal">
  <h3>¿Prefieres hablarlo antes?</h3>
  <p>Reserva una llamada de 15 minutos y vemos si encajamos.</p>
  <a class="pill pill-light" href="#">Reservar llamada</a>
</div>
```

### Dentro de una hoja oliva

`.is-deep` invierte `.pill-accent`: fondo blanco y texto oliva.

```html preview superficie=landing
<div class="sheet is-deep">
  <button type="button" class="pill pill-accent">Preparar email</button>
</div>
```

### API de clases (landing)

| Clase | Qué hace |
| --- | --- |
| `pill` | Base y secundario: contorno `--line-2`, fondo transparente, texto `--ink` en Roboto Mono de 15 px y mayúsculas. Hover: relleno `--ink`. |
| `pill-accent` | Principal: relleno `--cta` (oliva), texto `--cta-ink`; hover en `--cta-hover`. Dentro de `.is-deep`, relleno `--deep-ink` y texto `--deep`. |
| `pill-light` | Sobre azul cielo: relleno `--paper-fixed`, texto `--ink-fixed`; hover en blanco puro. |

## Portal

Clases de `public/site/button.css`, que solo usa el portal. Las pone el componente `Button`.

```html preview superficie=portal
<button class="button">Guardar cambios</button>
<button class="button button-ghost">Cancelar</button>
<button class="button button-danger">Eliminar</button>
```

### API de clases (portal)

| Clase | Qué hace |
| --- | --- |
| `button` | Base: relleno `--ink`, texto `--paper`, 44 px de alto, texto de 15 px, radio de 12 px. Hover en `--ink-hover`. |
| `button-outline` | Secundario: fondo transparente, borde `--line-strong`; hover en `--paper-2`. |
| `button-ghost` | Sin borde ni fondo, texto `--ink-2`; hover en `--paper-2`. Acciones terciarias del portal. |
| `button-subtle` | Fondo `--card` con borde `--line`. |
| `button-danger` | Relleno `--danger`, texto blanco. Solo para acciones destructivas. |
| `button-light` | Para fondos oscuros: relleno `--paper`, texto `--ink`. |
| `button-small` | 36 px de alto, texto de 14 px, radio de 10 px. |
| `button-block` | Ocupa todo el ancho de su contenedor. |

### Componente React

`Button` y `ButtonLink`, en `src/components/ui/button.tsx`. Traducen sus props a las clases de `button.css`: `variant="outline"` → `button-outline`, `size="sm"` → `button-small`. `className` se añade al final y las utilidades de Tailwind (`w-full`, márgenes) pueden ajustarlo, porque el botón está en la capa `components`.

```tsx
import { Button, ButtonLink } from '@/components/ui';

<Button>Guardar cambios</Button>
<Button variant="ghost">Cancelar</Button>
<Button variant="danger">Eliminar</Button>
<ButtonLink href="/admin/clients/new" size="sm">Nuevo cliente</ButtonLink>
```

```html preview superficie=portal
<button class="button">Guardar cambios</button>
<button class="button button-ghost">Cancelar</button>
<a href="#" class="button button-small">Nuevo cliente</a>
```

### Props

| Prop | Valores | Por defecto |
| --- | --- | --- |
| `variant` | `primary`, `outline`, `ghost`, `subtle`, `danger` | `primary` |
| `size` | `sm` (36 px, 14 px, radio de 10 px), `md` (44 px, 15 px, radio de 12 px) | `md` |
| `type` | `button`, `submit`, `reset` | `button` |

# Enlace con producción

| Fuente | Estado | Huella |
| --- | --- | --- |
| `public/site/report.css` | Sincronizado | 58522cb7c625dcf7 |
| `public/site/button.css` | Sincronizado | 2ba69fbd2d963caa |
| `src/components/ui/button.tsx` | Sincronizado | 9c0b89d35ff98960 |
| `tokens.produccion.json` | Sincronizado | — |
