ATOM
Components (registry)Indicators

Tag

A small label for category or status — ghost, filled, or outlined. Metadata, not interactive filters.

Preview

Variants: ghostfilledoutlined (default: filled)

Sizes: xssm (default: s)

Editorial

<!-- F12c editorial — non-derivable only. Review: Karen. -->

## Ejemplos

Category metadata:

```tsx import { Tag } from '@/components/atoms/Tag';

<Tag variant="filled" intent="neutral" size="s">Design system</Tag> <Tag variant="outline" intent="success" size="s" dot>Stable</Tag> ```

## Accesibilidad

- Tags are labels, not buttons — do not attach click handlers without turning them into real controls with roles and keyboard support. - Color/`intent` must not be the only cue; keep the text label meaningful.

### Correcto

- Tag es un `<span>` no interactivo — correcto para metadata y status display - Avatar img tiene alt='' (decorativo) — el texto del label es lo accesible - Dot usa currentColor — cambia con el intent automaticamente - Ellipsis con max-width 200px previene overflow en layouts

### Evitar

- No usar Tag para acciones — usar Chip (con onClose) o Button - No depender solo del color del intent para comunicar estado — acompanar con texto descriptivo - No usar intent='ai' para contenido no generado por AI — es un indicador semantico - No pasar dot + icon + avatar juntos — elegir uno

## Cuándo no usar

- Dismissible / selectable filters → `Chip` (interactive). - Numeric counts on icons → `Badge`.

## Criterio de uso

- Usa Tag para clasificar o describir contenido; el texto debe seguir siendo útil si se quita el color y el dot. - Reserva `success`, `warning` y `danger` para estados con significado real; `brand` y `ai` deben funcionar como énfasis, no como semántica inventada. - Mantén las etiquetas cortas. Si el usuario necesita seleccionar, quitar o cambiar el valor, usa `Chip` o un control explícito.

## Gotchas

- Tag no es interactivo por defecto: no lo conviertas en botón sólo con un click handler. - No uses un Tag como banner de alerta; para mensajes que requieren atención usa un patrón de feedback. - **Nota**: CSS autocontenido. Tag no tiene hover, transiciones ni animaciones — es puramente visual. Los 24 combos de color (3 variantes x 8 intents) estan hardcoded.

Uso

import { Tag } from '@/components/atoms/Tag';

<Tag variant="filled" intent="success" size="s">
  Live
</Tag>

Props

PropTipoDefaultRango / opcionesWhatHow
variantselectfilled`ghost`, `filled`, `outlined`Surface treatment of the status tag.filled for primary status; outlined for quiet meta; ghost for inline. Default: filled.
intentselectneutral`success`, `warning`, `danger`, `info`, `neutral`, `brand`, `ai`, `disabled`Semantic color of the tag.neutral for meta; success/warning/danger for state; brand/ai sparingly. Default: neutral.
sizeselects`xs`, `s`, `m`Type and padding scale.s default; xs for dense tables; m for marketing. Default: s.
dotbooleanfalse`true` / `false`Shows a leading status dot.true for live/status lists; false for pure labels. Default: false.

Gotchas

  • a11y

    Tags are not interactive by default — do not use them as buttons; use Chip or Button.

Anatomía CSS

<div class="text-gradient">

</div>
ClasePropósito
text-gradientroot

Tokens resueltos

Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.

VariantePropValorToken
allbordervar(--stroke-thin) solid transparent(literal)
gradientfg#0a0a0aforeground
gradientbordertransparent(literal)
inversebg#0a0a0aforeground
inversefg#fafafabackground
ghostfg#8023ffai.text
ghostdisabled-fg#525252muted.foreground
filledbg#8023ffai
filledfg#8023ffai.text
filleddisabled-bg#f5f5f5muted
filleddisabled-fg#525252muted.foreground
outlinedfg#8023ffai.text
outlinedborder#8023ffai.text
outlineddisabled-fg#525252muted.foreground
outlineddisabled-border#f5f5f5muted
allbgcurrentColor(literal)

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).

/* -------------------------------------------------------------------------
   Tag

   Status pill for categories, labels, and section headings.
   Pill shape (border-radius: 1000px). Non-interactive.

   Variants: ghost (no bg), filled (bg), outlined (border)
   Intents:  success, warning, danger, info, neutral, brand, ai, disabled
   Sizes:    xs (16px), s (20px), m (24px)
   Features: hasDots, hasIcon, hasAvatar
   ------------------------------------------------------------------------- */

.tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  max-width: 12.5rem;
  border-radius: 9999px;
  border: 1.5px solid transparent;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Sizes ---- */

.tag--xs { height: 1rem; padding: 0 8px; font-size: 10.24px; }
.tag--s  { height: 1.25rem; padding: 0 8px; font-size: 10.24px; }
.tag--m  { height: 1.5rem; padding: 0 8px; font-size: 12.8px; }

/* ---- l: eyebrow de hero ----
   El unico tamano pensado para FRASE, no para etiqueta de estado: por eso
   levanta el max-width de 12.5rem y el nowrap del bloque base. Sin esto una
   frase como "Inteligencia de Conversion para WhatsApp" se corta con ellipsis. */

.tag--l {
  height: auto;
  max-width: none;
  padding: 8px 16px;
  font-size: 10.24px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  white-space: normal;
  overflow: visible;
}

/* ---- Mono (patron ATOM: etiqueta tecnica) ---- */
/* Tipografia mono en caja recta. Combina con --inverse o cualquier intent. */

.tag--mono {
  font-family: 'interval', ui-monospace, 'cascadia code', monospace;
  font-weight: 400;
  font-size: 10.24px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-radius: 4px;
}

/* ---- Gradient (borde degradado de marca) ----
   Highlight tipografico del sistema: el mismo --gradient-highlight del
   .text-gradient, aqui como CONTORNO. El borde degradado se hace con dos capas
   de background y distintos clip boxes — un border-image no permite radio, y
   este pill es radius-full. Por eso el border va transparent: es la ventana por
   la que se ve la capa de gradiente. */

.tag--gradient {
  border-color: transparent;
  background-image:
    linear-gradient(#fafafa, #fafafa),
    var(--gradient-highlight);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  color: #0a0a0a;
}

/* ---- Inverse (relleno con los semantics del contexto: flipa en dark) ---- */

.tag--inverse {
  color: #fafafa;
  background-color: #0a0a0a;
}

/* ---- Ghost (text only, no bg) ---- */

.tag--ghost.tag--success   { color: #0a290f; }
.tag--ghost.tag--warning   { color: #973c00; }
.tag--ghost.tag--danger    { color: #c21e12; }
.tag--ghost.tag--info      { color: #0e7490; }
.tag--ghost.tag--neutral   { color: #0a0a0a; }
.tag--ghost.tag--brand     { color: #a44200; }
.tag--ghost.tag--ai        { color: #8023ff; }
.tag--ghost.tag--disabled  { color: #525252; }

/* ---- Filled (bg + text) ---- */

.tag--filled.tag--success   {
  background-color: color-mix(in srgb, #25d366 18%, #fafafa);
  color: #0a290f;
}
.tag--filled.tag--warning   {
  background-color: color-mix(in srgb, #fe9900 18%, #fafafa);
  color: #973c00;
}
.tag--filled.tag--danger    {
  background-color: color-mix(in srgb, #f84131 16%, #fafafa);
  color: #c21e12;
}
.tag--filled.tag--info      {
  background-color: color-mix(in srgb, #22d3ee 18%, #fafafa);
  color: #0e7490;
}
.tag--filled.tag--neutral   { background-color: #f5f5f5; color: #0a0a0a; }
.tag--filled.tag--brand     {
  background-color: color-mix(in srgb, #ff6600 18%, #fafafa);
  color: #a44200;
}
/* El stop de arranque era --success: mientras ai no tuvo superficie propia, el
   intent se pintaba de verde y no se distinguia del de exito. Ahora arranca en
   su propio violeta. */
.tag--filled.tag--ai        {
  background: linear-gradient(
    136deg,
    color-mix(in srgb, #8023ff 20%, #fafafa),
    color-mix(in srgb, #8023ff 12%, #fafafa)
  );
  color: #8023ff;
}
.tag--filled.tag--disabled  { background-color: #f5f5f5; color: #525252; }

/* ---- Outlined (border + text) ---- */

.tag--outlined.tag--success   { border-color: #0a290f; color: #0a290f; }
.tag--outlined.tag--warning   { border-color: #973c00; color: #973c00; }
.tag--outlined.tag--danger    { border-color: #c21e12; color: #c21e12; }
.tag--outlined.tag--info      { border-color: #0e7490; color: #0e7490; }
.tag--outlined.tag--neutral   { border-color: #e5e5e5; color: #0a0a0a; }
.tag--outlined.tag--brand     { border-color: #a44200; color: #a44200; }
.tag--outlined.tag--ai        { border-color: #8023ff; color: #8023ff; }
.tag--outlined.tag--disabled  { border-color: #f5f5f5; color: #525252; }

/* ---- Parts ---- */

.tag__dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 9999px;
  background-color: currentColor;
  flex-shrink: 0;
}

.tag__icon {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.tag__avatar {
  flex-shrink: 0;
}

.tag__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 1px;
}

Codigo fuente

components-react
components/atoms/Tag.tsx
import { type ReactNode } from 'react';

type TagVariant = 'ghost' | 'filled' | 'outlined';
type TagIntent = 'success' | 'warning' | 'danger' | 'info' | 'neutral' | 'brand' | 'ai' | 'disabled';
type TagSize = 'xs' | 's' | 'm';

export type TagProps = {
  variant?: TagVariant;
  intent?: TagIntent;
  size?: TagSize;
  dot?: boolean;
  icon?: ReactNode;
  avatar?: string;
  children: string;
  className?: string;
};

function cn(...classes: (string | false | undefined | null)[]) {
  return classes.filter(Boolean).join(' ');
}

export function Tag({
  variant = 'filled',
  intent = 'neutral',
  size = 's',
  dot = false,
  icon,
  avatar,
  children,
  className,
}: TagProps) {
  const classes = cn(
    'tag',
    `tag--${variant}`,
    `tag--${intent}`,
    `tag--${size}`,
    className,
  );

  return (
    <span className={classes}>
      {dot && <span className="tag__dot" />}
      {avatar && (
        <span className="tag__avatar">
          <img src={avatar} alt="" />
        </span>
      )}
      {icon && <span className="tag__icon">{icon}</span>}
      <span className="tag__label">{children}</span>
    </span>
  );
}

Webflow

Webflowtag

Pega en el Designer como application/json, luego convierte a Component (Atom / Tag) 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)

  • selector on .tag--ghost.tag--successcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--warningcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--dangercompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--infocompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--neutralcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--brandcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--aicompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .tag--ghost.tag--disabledcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)

Tras pegar: Create component → nombre Atom / Tag → Publish.

Componentes relacionados

On this page

Detalles

Publicado14 de mayo de 2026
Categoriaindicators
Lectura...
Visitas...
Ayuda?Slack

Componente

Tag

Source

components-react / cssDisponible via MCP: atom_uikit_source("tag")
Abrir en Storybook