ATOM

Chip

A compact pill for a filter or selection you can remove. Multi-select tags and active filters.

Preview

Sizes: xssmlxl (default: s)

Editorial

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

## Ejemplos

Selected filter chip:

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

<Chip type="filled" size="s" onClose={() => {}}> Design </Chip> ```

## Accesibilidad

- `onClose` must have an accessible name (`aria-label` on the close control). - The visual prop is named `type` (not `variant`) — keep labels in sync with registry.

### Correcto

- Close button tiene aria-label='Remove {text}' automaticamente - Close button usa `tabIndex={-1}` — no focusable por Tab (el chip completo recibe foco) - focus-visible y focused prop muestran focus ring para teclado - disabled desactiva tanto el chip como el close button

### Evitar

- No usar Chip para status display estatico — usar Tag en su lugar - No pasar JSX como children — solo string (necesario para aria-label del close) - No omitir onClose si el chip es removible — sin el, no hay boton X

## Cuándo no usar

- Tags that are not interactive status labels should use `Tag`, not `Chip`.

## Criterio de uso

- Usa Chip para una selección, filtro o valor que el usuario pueda retirar; el botón de cierre debe estar dentro del mismo contexto semántico. - Usa `outlined` en reposo y `filled` cuando el estado seleccionado deba destacar; evita que la superficie sea la única señal. - Si el chip está bloqueado por permisos, explica la razón cerca del grupo y no simules una acción disponible.

## Gotchas

- El control de cierre necesita nombre accesible propio y debe conservar foco de teclado. - `type` es el prop visual del componente; no lo renombres a `variant` al componerlo con otros controles.

Uso

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

<Chip type="filled" size="s" onClose={() => {}}>
  Design
</Chip>

Props

PropTipoDefaultRango / opcionesWhatHow
typeselectoutlined`outlined`, `filled`Surface style of the chip (outlined vs filled).outlined for filters at rest; filled when selected. Default: outlined.
sizeselects`xs`, `s`, `m`, `l`, `xl`Padding and type scale of the chip.s for filter bars; m for forms; xs only in dense tables. Default: s.
disabledbooleanfalse`true` / `false`Prevents remove/select interaction.true when the filter is locked by plan or permissions. Default: false.
errorbooleanfalse`true` / `false`Error emphasis on the chip surface.true only with an associated validation message. Default: false.
focusedbooleanfalse`true` / `false`Forced focus ring for composed controls.Prefer real :focus-visible; use focused only for composite widgets. Default: false.
animatedbooleanfalse`true` / `false`Motion on appear/dismiss.true for filter UIs; false in static meta lists. Default: false.

Gotchas

  • a11y

    onClose must have an accessible name (aria-label on the close control).

  • react

    variant prop is named type (not variant) — keep in sync with registry variantProp.

Anatomía CSS

<div class="chip">
  <span class="chip__close"></span>
  <span class="chip__icon"></span>
  <span class="chip__label"></span>
</div>
ClasePropósito
chiproot
chip--disabledmodifier
chip--errormodifier
chip--filledmodifier
chip--focusedmodifier
chip--lmodifier
chip--mmodifier
chip--outlinedmodifier
chip--smodifier
chip--xlmodifier
chip--xsmodifier
chip__closeelement
chip__iconelement
chip__labelelement

Tokens resueltos

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

VariantePropValorToken
allfginherit(literal)
allbordernone(unparsed)
outlinedbg#fafafabackground
outlinedborder#e5e5e5border
outlineddisabled-bg#f84131destructive
filledbg#f5f5f5muted
filleddisabled-bg#f84131destructive
alldisabled-fg#525252muted.foreground
outlineddisabled-border#f5f5f5muted
outlinederror-fg#f84131destructive
outlinederror-border#f84131destructive
fillederror-bg#f84131destructive
fillederror-fg#f84131destructive
allbgcolor-mix(in srgb, var(--foreground) 6%, transparent)(literal)
allerror-bgcolor-mix(in srgb, var(--destructive) 18%, transparent)(literal)
alldisabled-bgcolor-mix(in srgb, var(--foreground) 4%, transparent)(literal)
allhover-bgcolor-mix(in srgb, var(--foreground) 12%, transparent)(literal)

Animaciones

PropiedadDuraciónEasing
background-colorvar(--duration-150)var(--easing-out)
border-colorvar(--duration-150)var(--easing-out)
scalevar(--duration-150)var(--easing-in-out)
background-color0.15scubic-bezier(0.19
1
0.22
1)
border-color0.15scubic-bezier(0.19
1
0.22
1)
scalevar(--duration-500)0.05s cubic-bezier(0.32
0.72
0
1)
background-colorvar(--duration-150)var(--easing-out)
scalevar(--duration-500)cubic-bezier(0.32
0.72
0
1)

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

/* -------------------------------------------------------------------------
   Chip

   Interactive tag with optional leading icon and close button.
   Same sizing discipline as Button: height + padding from tokens,
   gap/radius/icon em-relative.

   Types:   outlined, filled
   Sizes:   xs, s, m, l, xl (same naming as Button)
   States:  enabled, hover, pressed, disabled, error
   ------------------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375em;
  border-radius: 0.5em;
  border: 1.5px solid transparent;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 10.24px;
  font-weight: 500;
  line-height: 1;
  color: #0a0a0a;
  cursor: default;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  white-space: nowrap;
  transition:
    background-color 150ms cubic-bezier(0.22, 1, 0.36, 1),
    border-color 150ms cubic-bezier(0.22, 1, 0.36, 1),
    scale 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

.chip[data-chip-animate]:active:not(.chip--disabled) {
  scale: 0.955 0.925;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .chip[data-chip-animate]:hover:not(.chip--disabled) {
    scale: 1.06 1.095;
    transition:
      background-color 0.15s cubic-bezier(0.19, 1, 0.22, 1),
      border-color 0.15s cubic-bezier(0.19, 1, 0.22, 1),
      scale 500ms 0.05s cubic-bezier(0.32, 0.72, 0, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .chip {
    transition-duration: 0ms;
  }
  .chip:active:not(.chip--disabled) {
    scale: 1;
  }
}

/* ---- Sizes (height + h-padding explicit, font-size uniform) ---- */

.chip--xs {
  height: 1.25rem;
  padding: 0 4px;
}

.chip--s {
  height: 1.5rem;
  padding: 0 8px;
}

.chip--m {
  height: 2rem;
  padding: 0 8px;
}

.chip--l {
  height: 2.5rem;
  padding: 0 12px;
}

.chip--xl {
  height: 3rem;
  padding: 0 16px;
}

/* ---- Types ---- */

.chip--outlined {
  background-color: #fafafa;
  border-color: #e5e5e5;
}

.chip--outlined:hover:not(.chip--disabled) {
  background-color: #f5f5f5;
}

.chip--filled {
  background-color: #f5f5f5;
}

.chip--filled:hover:not(.chip--disabled) {
  background-color: #f5f5f5;
}

/* ---- Disabled ---- */

.chip--disabled {
  color: #525252;
  cursor: not-allowed;
  pointer-events: none;
}

.chip--disabled.chip--outlined {
  border-color: #f5f5f5;
  background-color: #f5f5f5;
}

.chip--disabled.chip--filled {
  background-color: #f5f5f5;
}

/* ---- Error ---- */

.chip--error.chip--outlined {
  border-color: #f84131;
  color: #f84131;
}

.chip--error.chip--outlined:hover:not(.chip--disabled) {
  background-color: color-mix(in srgb, #f84131 6%, #fafafa);
}

.chip--error.chip--filled {
  background-color: color-mix(in srgb, #f84131 8%, #fafafa);
  color: #f84131;
}

.chip--error.chip--filled:hover:not(.chip--disabled) {
  background-color: color-mix(in srgb, #f84131 12%, #fafafa);
}

/* ---- Focus ---- */

.chip:focus-visible,
.chip--focused {
  box-shadow: 0 0 0 2px var(--focus-ring-color);
  outline: none;
}

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

.chip__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1em;
  height: 1em;
}

.chip__icon svg {
  width: 100%;
  height: 100%;
}

.chip__label {
  display: inline-flex;
  align-items: center;
}

.chip__close {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1.125em;
  height: 1.125em;
  padding: 0.125em;
  border: none;
  background-color: color-mix(in srgb, #0a0a0a 6%, transparent);
  color: inherit;
  cursor: pointer;
  border-radius: 9999px;
  transition:
    background-color 150ms cubic-bezier(0.22, 1, 0.36, 1),
    scale 500ms cubic-bezier(0.32, 0.72, 0, 1);
}

.chip__close svg {
  width: 100%;
  height: 100%;
}

.chip__close:active {
  scale: 0.9;
}

.chip--error .chip__close {
  background-color: color-mix(in srgb, #f84131 10%, transparent);
}

.chip--disabled .chip__close {
  cursor: not-allowed;
  pointer-events: none;
  background-color: color-mix(in srgb, #0a0a0a 4%, transparent);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .chip__close:hover {
    background-color: color-mix(in srgb, #0a0a0a 12%, transparent);
    scale: 1.06 1.095;
  }

  .chip--error .chip__close:hover {
    background-color: color-mix(in srgb, #f84131 18%, transparent);
  }
}

Codigo fuente

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

type ChipType = 'outlined' | 'filled';
type ChipSize = 'xs' | 's' | 'm' | 'l' | 'xl';

export type ChipProps = {
  type?: ChipType;
  size?: ChipSize;
  disabled?: boolean;
  error?: boolean;
  focused?: boolean;
  animated?: boolean;
  iconLeft?: ReactNode;
  onClose?: () => void;
  children: string;
  className?: string;
};

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

const CloseIcon = () => (
  <svg width="100%" height="100%" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <path d="M4.5 4.5l7 7M11.5 4.5l-7 7" />
  </svg>
);

export function Chip({
  type = 'outlined',
  size = 's',
  disabled = false,
  error = false,
  focused = false,
  animated = false,
  iconLeft,
  onClose,
  children,
  className,
}: ChipProps) {
  const classes = cn(
    'chip',
    `chip--${type}`,
    `chip--${size}`,
    disabled && 'chip--disabled',
    error && 'chip--error',
    focused && 'chip--focused',
    className,
  );

  return (
    <span className={classes} {...(animated ? { 'data-chip-animate': '' } : {})}>
      {iconLeft && <span className="chip__icon">{iconLeft}</span>}
      <span className="chip__label">{children}</span>
      {onClose && (
        <button
          type="button"
          className="chip__close"
          onClick={onClose}
          disabled={disabled}
          aria-label={`Remove ${children}`}
          tabIndex={-1}
        >
          <CloseIcon />
        </button>
      )}
    </span>
  );
}

Webflow

Webflowchip

Pega en el Designer como application/json, luego convierte a Component (Atom / Chip) 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 .chip[data-chip-animate]:active:not(.chip--disabled)compound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--outlined:hover:not(.chip--disabled)compound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--filled:hover:not(.chip--disabled)compound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--disabled.chip--outlinedcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--disabled.chip--filledcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--error.chip--outlinedcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--error.chip--outlined:hover:not(.chip--disabled)compound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .chip--error.chip--filledcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)

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

Componentes relacionados

On this page

Detalles

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

Componente

Chip

Source

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