# Progress

> Cuánto falta para que termine algo. Determinada o indeterminada, en dos altos.

```tsx
import { Progress } from "sebs7n-ui/progress"
```

## Ejemplos

### Una subida real

El `label` es el nombre accesible: no hace falta `aria-label`. `showValue` deja el porcentaje a la vista.

```tsx
import { Button } from "sebs7n-ui/button"
import { Progress } from "sebs7n-ui/progress"
import { useEffect, useState } from "react"

function Subida() {
  // Arranca a mitad de camino: una barra vacía como primer cuadro de la página no muestra nada.
  const [valor, setValor] = useState(41)
  const [corriendo, setCorriendo] = useState(false)

  useEffect(() => {
    if (!corriendo) return
    const id = setInterval(() => {
      setValor((actual) => {
        const siguiente = Math.min(100, actual + 7)
        if (siguiente === 100) setCorriendo(false)
        return siguiente
      })
    }, 300)
    return () => clearInterval(id)
  }, [corriendo])

  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <Progress label="facturas-2026.zip" showValue value={valor} />
      <div className="flex gap-2">
        <Button
          onClick={() => {
            setValor(0)
            setCorriendo(true)
          }}
          size="sm"
          variant="secondary"
        >
          Subir de nuevo
        </Button>
        <Button disabled={!corriendo} onClick={() => setCorriendo(false)} size="sm" variant="ghost">
          Pausar
        </Button>
      </div>
    </div>
  )
}
```

### Determinada e indeterminada

Con `value={null}` no hay porcentaje: Base UI saca `aria-valuenow` y la barra pasa a ser una franja que recorre la pista. Es para cuando no se puede saber cuánto falta.

```tsx
import { Progress } from "sebs7n-ui/progress"

function Indeterminada() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6">
      <Progress label="Importando clientes" showValue value={68} />
      <Progress label="Consultando a AFIP" value={null} />
    </div>
  )
}
```

### Tamaños y uso en una fila

`sm` (4px) dentro de una fila o una card chica; `md` (6px) suelto. Sin etiqueta visible va `aria-label`.

```tsx
import { Progress } from "sebs7n-ui/progress"

function Tamanos() {
  const cuotas = [
    { plan: "Plan Pro", pagadas: 9 },
    { plan: "Plan Equipo", pagadas: 4 },
  ]
  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      {cuotas.map((cuota) => (
        <div className="flex flex-col gap-1.5" key={cuota.plan}>
          <div className="flex items-baseline justify-between">
            <span className="text-callout text-label">{cuota.plan}</span>
            <span className="text-callout text-label-secondary">{cuota.pagadas} de 12 cuotas</span>
          </div>
          <Progress
            aria-label={`Cuotas pagadas de ${cuota.plan}`}
            max={12}
            size="sm"
            value={cuota.pagadas}
          />
        </div>
      ))}
      <Progress aria-label="Espacio usado" size="md" value={100} />
    </div>
  )
}
```

## Props

### Progress

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `value` * | `number` | — | **Heredada de Base UI.** El valor actual. `null` la deja indeterminada. |
| `label` | `NonNullable<ReactNode>` | — | El nombre visible de la barra. Es la forma preferida de nombrarla; sin él, el tipo exige `aria-label` o `aria-labelledby`. |
| `showValue` | `boolean` | `false` | Muestra el porcentaje a la derecha. Indeterminada no muestra número. |
| `size` | `"sm" \| "md"` | `"md"` | `sm` 4px · `md` 6px de alto de la pista. |
| `trackClassName` | `string` | — | Clases de la pista (el riel gris), por si hay que cambiarle el ancho o el radio. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

`*` obligatoria.

## Teclado

| Tecla | Qué hace |
|---|---|
| — | No es interactivo. |

## Accesibilidad

- Emite `role="progressbar"` con `aria-valuenow`, `aria-valuemin` y `aria-valuemax`.
- Con `value={null}` es indeterminada: desaparece `aria-valuenow` y el lector anuncia que está en curso, sin porcentaje.
- El nombre es **obligatorio y lo exige el tipo**: `label` (visible, la preferida), `aria-label` o `aria-labelledby`. Una barra sin nombre se anuncia «60 %» y nada más, y el 60 % de qué es justamente lo que hace falta saber.
- Con movimiento reducido la franja indeterminada no queda congelada a mitad de camino: la pista se llena de un gris más apagado.

## Reglas de uso

- **Si sabés cuánto falta, pasá el número.** La indeterminada es para cuando no se puede saber.
- La forma de algo que todavía no llegó es un `Skeleton`; el spinner de una acción es `Button loading`.
- Poné `label` o `aria-label`: una barra sin nombre no dice qué está progresando.
- `size="sm"` dentro de una fila o una card chica; `md` suelto.

## Relacionados

[meter](/docs/components/meter.md) · [skeleton](/docs/components/skeleton.md) · [slider](/docs/components/slider.md)
