Badge
A small status chip that sits on an edge or next to a label. Counts, inbox state, and quiet flags.
Variants: neutralinbox (default: neutral)
Editorial
<!-- F12c editorial — non-derivable only. Review: Karen. -->
## Ejemplos
Inbox count / quiet status:
```tsx import { Badge } from '@/components/atoms/Badge';
<Badge variant="neutral" state="enabled">3</Badge> <Badge variant="success">Live</Badge> ```
## Accesibilidad
- If the badge is the only indicator of state, include text (not color alone): “3 unread”, not a bare red dot without a name. - Decorative badges next to already-named headings can be `aria-hidden` when they add no new information.
### Correcto
- Badge es un `<span>` — no interactivo, no focusable. Correcto para indicadores visuales - Combinar con aria-label en el padre si el numero no es autoexplicativo (ej: 'Inbox, 3 mensajes sin leer') - Truncar a '99+' en lugar de mostrar numeros largos que rompen el layout
### Evitar
- No usar Badge para texto largo — maximo 3-4 caracteres (numeros) - No depender solo del color para comunicar urgencia — el contexto (posicion, texto adyacente) importa - No usar Badge como boton — es no interactivo. Para acciones, usar Chip con onClose
## Cuándo no usar
- Interactive filters users click to toggle → `Chip` / `Tag` patterns meant for actions. - Category metadata chips that are purely labels → `Tag` may fit better than a count-oriented badge.
## Criterio de uso
- Usa Badge para conteos cortos o estados secundarios próximos a un control o label, no para explicar una situación completa. - Si el número representa elementos no leídos, comunica también su significado: “3 mensajes sin leer”, no sólo “3”. - Mantén el badge inline y proporcional al contenido; una alerta que necesita lectura, acción o detalle debe usar feedback dedicado.
## Gotchas
- El color y la posición no deben ser la única forma de entender el estado. - Si el badge es decorativo junto a un título ya nombrado, puedes ocultarlo a tecnologías asistivas para evitar repetición. - **Nota**: Badge trunca con ellipsis si el texto excede max-width (36px). Para conteos grandes, formatea como '99+' desde el componente padre. - **Nota**: CSS autocontenido. Badge no tiene hover, transiciones ni animaciones — es un indicador estatico puro.
Uso
import { Badge } from '@/components/atoms/Badge';
<Badge variant="inbox" state="enabled">
3
</Badge>Props
| Prop | Tipo | Default | Rango / opciones | What | How |
|---|---|---|---|---|---|
| variant | select | neutral | `neutral`, `inbox` | Surface style of the status chip. | neutral for quiet metadata; inbox for count/attention chips. Default: neutral. |
| state | select | enabled | `enabled`, `focused`, `subtle` | Emphasis of the badge relative to surrounding UI. | enabled default; focused when the related control is active; subtle for de-emphasized counts. |
Gotchas
- a11y
If the badge is a live count, expose the meaning in accessible text (e.g. aria-label on the parent control), not only the digit.
- layout
Badges are inline; do not stretch them as block-level status banners — use toast or alert patterns for that.
Anatomía CSS
<div class="badge"> <span class="badge__label"></span> </div>
| Clase | Propósito |
|---|---|
badge | root |
badge--enabled | modifier |
badge--focused | modifier |
badge--inbox | modifier |
badge--neutral | modifier |
badge--subtle | modifier |
badge__label | element |
Tokens resueltos
Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.
| Variante | Prop | Valor | Token |
|---|---|---|---|
| neutral | bg | #f5f5f5 | muted |
| neutral | fg | #0a0a0a | foreground |
| neutral | focused-bg | #0a0a0a | primary |
| neutral | focused-fg | #fafafa | primary.foreground |
| neutral | subtle-bg | #262626 | color.neutral800 |
| neutral | subtle-fg | #fafafa | color.neutral50 |
| inbox | bg | #fb7a6e | color.coral300 |
| inbox | fg | #0a0a0a | color.neutral950 |
| inbox | focused-bg | #c21e12 | color.coral700 |
| inbox | focused-fg | #fafafa | color.neutral50 |
| inbox | subtle-bg | #f84131 | destructive |
| inbox | subtle-fg | #0a0a0a | destructive.foreground |
CSS mínimo funcional
Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).
/* -------------------------------------------------------------------------
Badge
Numeric counter pill. Compact indicator for counts and notifications.
Non-interactive. Fixed height (16px), pill shape, truncates with ellipsis.
Variants: neutral, inbox
States: enabled, focused, subtle
------------------------------------------------------------------------- */
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1rem;
max-width: 2.25rem;
height: 1rem;
padding: 0 4px;
border-radius: 9999px;
font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif;
font-size: 10.24px;
font-weight: 500;
line-height: 1;
white-space: nowrap;
overflow: hidden;
}
.badge__label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
min-width: 1px;
}
/* ---- Neutral ---- */
.badge--neutral.badge--enabled {
background-color: #f5f5f5;
color: #0a0a0a;
}
.badge--neutral.badge--focused {
background-color: #0a0a0a;
color: #fafafa;
}
.badge--neutral.badge--subtle {
background-color: #262626;
color: #fafafa;
}
/* ---- Inbox ---- */
.badge--inbox.badge--enabled {
background-color: #fb7a6e;
color: #0a0a0a;
}
.badge--inbox.badge--focused {
background-color: #c21e12;
color: #fafafa;
}
.badge--inbox.badge--subtle {
background-color: #f84131;
color: #0a0a0a;
}
Codigo fuente
export type BadgeVariant = 'neutral' | 'inbox'; export type BadgeState = 'enabled' | 'focused' | 'subtle'; export type BadgeProps = { variant?: BadgeVariant; state?: BadgeState; children: string; className?: string; }; function cn(...classes: (string | false | undefined | null)[]) { return classes.filter(Boolean).join(' '); } export function Badge({ variant = 'neutral', state = 'enabled', children, className, }: BadgeProps) { return ( <span className={cn( 'badge', `badge--${variant}`, `badge--${state}`, className, )} > <span className="badge__label">{children}</span> </span> ); }
Webflow
Pega en el Designer como application/json, luego convierte a Component (Atom / Badge) y publica. Formato interno no documentado de Webflow — regenerable, no dependencia de runtime.
Setup del sitio (una vez)
Custom Code → Head:
<link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/tokens.css"> <link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/components.css">
Unsupported (no silencioso)
selectoron.badge--neutral.badge--enabled— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.badge--neutral.badge--focused— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.badge--neutral.badge--subtle— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.badge--inbox.badge--enabled— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.badge--inbox.badge--focused— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)selectoron.badge--inbox.badge--subtle— compound/descendant selector — moved to head Custom Code (Designer styles are single-class)
Tras pegar: Create component → nombre Atom / Badge → Publish.