ATOM
Components (registry)Indicators

Skeleton

Grey shapes shimmer where content will land. First paint placeholders for cards, text, and avatars.

Preview

Variants: defaultcircletext (default: default)

Editorial

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

## Ejemplos

Card placeholder while loading:

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

<div aria-busy="true" aria-live="polite"> <Skeleton variant="default" /> <Skeleton variant="default" /> </div> ```

## Accesibilidad

- Mark the loading region with `aria-busy="true"` (and optionally `aria-live`) on the container — skeleton shapes alone are not announced as “loading”. - Replace skeletons with real content; do not leave them permanently on screen.

### Correcto

- aria-hidden='true' automatico — screen readers ignoran los skeletons - prefers-reduced-motion desactiva el pulso (queda bloque estatico) - Replicar la estructura del contenido real — mismos gaps, heights, widths - Reemplazar skeleton por contenido real sin cambio de layout (CLS = 0)

### Evitar

- No usar Skeleton como indicador de error — es solo para loading - No dejar skeletons permanentemente — siempre debe haber timeout o fallback - No animar con duraciones menores a 500ms — el pulso se vuelve molesto

## Cuándo no usar

- Indeterminate wait without layout reservation → `Spinner`. - Avatar-specific loading face → `Avatar skeleton` prop when that control owns the slot.

## Criterio de uso

- Usa Skeleton cuando conoces la forma del contenido que llegará y quieres reservar su espacio desde el primer paint. - Haz que la geometría del placeholder se parezca al contenido real; una barra genérica no explica una card compleja y aumenta el cambio visual. - Retíralo cuando el contenido esté listo y conserva `aria-busy` en el contenedor durante la transición.

## Gotchas

- Skeleton es decorativo: el estado de carga debe comunicarse en el contenedor, no con una colección de formas sin nombre. - No lo uses como animación permanente ni para bloquear acciones que ya podrían estar disponibles. - **Nota**: variant='text' tiene height: 1em por defecto — escala con el font-size del contenedor. Solo necesitas width. - **Nota**: 10 lineas de CSS. El componente mas ligero del DS. El consumidor controla todo el dimensionamiento via inline style. - **Nota**: No requiere GSAP. Es puro CSS @keyframes. La duracion (1s) es mas rapida que el Avatar skeleton (1.5s) — el pulso se siente mas energico.

Uso

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

<div aria-busy="true">
  <Skeleton variant="circle" className="w-10 h-10" />
  <Skeleton variant="text" className="w-40" />
</div>

Props

PropTipoDefaultRango / opcionesWhatHow
variantselectdefault`default`, `circle`, `text`Placeholder geometry.default for blocks/cards; circle for avatars; text for line stacks. Default: default.

Gotchas

  • a11y

    Mark the loading region with aria-busy on the container; skeleton itself is decorative.

  • layout

    Size via className/width styles to match the real component footprint — avoid layout shift when content swaps in.

Anatomía CSS

<div class="skeleton">

</div>
ClasePropósito
skeletonroot
skeleton--circlemodifier
skeleton--textmodifier

Tokens resueltos

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

VariantePropValorToken
allbg#e5e5e5border

Animaciones

PropiedadDuraciónEasing
@keyframes skeleton-pulse

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

/* -------------------------------------------------------------------------
   Skeleton

   Loading placeholder. Pulse animation on muted background.
   Consumer sets width/height/border-radius via style or className.

   Parts: .skeleton
   ------------------------------------------------------------------------- */

.skeleton {
  display: block;
  background-color: #e5e5e5;
  border-radius: 8px;
  animation: skeleton-pulse 1000ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* ---- Shape helpers ---- */

.skeleton--circle {
  border-radius: 9999px;
}

.skeleton--text {
  height: 1em;
  border-radius: 4px;
}

/* ---- Reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
  }
}

Codigo fuente

components-react
components/atoms/Skeleton.tsx
import { type HTMLAttributes } from 'react';

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

export type SkeletonProps = {
  variant?: 'default' | 'circle' | 'text';
  className?: string;
} & HTMLAttributes<HTMLDivElement>;

export function Skeleton({ variant = 'default', className, ...props }: SkeletonProps) {
  return (
    <div
      aria-hidden="true"
      className={cn(
        'skeleton',
        variant !== 'default' && `skeleton--${variant}`,
        className,
      )}
      {...props}
    />
  );
}

Webflow

Webflowskeleton

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

  • @media on (prefers-reduced-motion: reduce)not a Designer breakpoint — moved to head Custom Code block

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

Componentes relacionados

On this page

Detalles

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

Componente

Skeleton

Source

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