ATOM
Components (registry)Actions

PricingCard

A plan card with price, tags, benefits, and CTA on light, brand, or dark surface. Pricing tables.

Preview

Anatomía CSS

<div class="pricing-card">
  <span class="pricing-card__channels"></span>
  <span class="pricing-card__crm"></span>
  <span class="pricing-card__cta"></span>
  <span class="pricing-card__eyebrow"></span>
  <span class="pricing-card__inner"></span>
  <span class="pricing-card__logos"></span>
  <span class="pricing-card__price"></span>
  <span class="pricing-card__price-note"></span>
  <span class="pricing-card__price-row"></span>
  <span class="pricing-card__price-unit"></span>
  <span class="pricing-card__scribble"></span>
  <span class="pricing-card__tag-row"></span>
  <span class="pricing-card__title"></span>
</div>
ClasePropósito
pricing-cardroot
pricing-card--brandmodifier
pricing-card__channelselement
pricing-card__crmelement
pricing-card__ctaelement
pricing-card__eyebrowelement
pricing-card__innerelement
pricing-card__logoselement
pricing-card__priceelement
pricing-card__price-noteelement
pricing-card__price-rowelement
pricing-card__price-unitelement
pricing-card__scribbleelement
pricing-card__tag-rowelement
pricing-card__titleelement

Tokens resueltos

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

VariantePropValorToken
allbg#ffffffcard
allfg#25d366success
brandbg#0a0a0abrand.foreground
brandfg#0a0a0abrand.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).

/* -------------------------------------------------------------------------
   Pricing Card

   Anatomía espejo de las cards de ATOM /plans (docs/osmo-reference: padding
   60px, radio contenedor, título enorme en peso regular, divider con tags,
   benefits con hairlines translúcidas, scribble decorativo), con los valores
   del sistema Atom.

   Superficies:
     (default)                    — #ffffff, tarjeta clara
     .pricing-card--brand         — superficie naranja de marca, texto oscuro
                                    (WCAG: brand-foreground es neutral-950)
     data-theme="dark" en la card — tarjeta oscura via theming del sistema:
                                    todos los semantics flipan solo dentro

   Parts: __inner, __tag-row, __title, __price-row, __price, __price-unit,
          __price-note, __channels (fila de iconos de canal), __cta, __divider,
          __benefits, __benefit, __benefit-value, __logos (franja de partners),
          __scribble
   ------------------------------------------------------------------------- */

.pricing-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 48px 40px;
  color: #0a0a0a;
  background-color: #ffffff;
  border-radius: 16px;
  /* Motion: pendiente de diseno de Karen (W3 de evolution). No inventar. */
}

.pricing-card--brand {
  color: #0a0a0a;
  background-color: #ff6600;
}

/* En dark la card usa --card (carbon, neutral-900), NO --background
   (neutral-950, casi negro puro): una tarjeta es superficie ELEVADA sobre el
   fondo, y el negro absoluto aplana y encandila en pantallas OLED. */

.pricing-card__inner {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  gap: 32px;
}

/* ---- Eyebrow / tags ---- */

.pricing-card__tag-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

/* El look del eyebrow lo da el atomo (.tag--mono.tag--inverse); la card solo
   corrige el relleno inverso sobre su superficie brand (no es un theme). */
:where(.pricing-card--brand) :where(.pricing-card__eyebrow.tag--inverse) {
  color: #ff6600;
  background-color: #0a0a0a;
}

/* ---- Título y precio ---- */

.pricing-card__title {
  font-family: 'grift', 'inter tight', georgia, serif;
  font-size: 48.83px;
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.03em;
}

.pricing-card__price-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pricing-card__price {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 25px;
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.08;
}

.pricing-card__price-unit {
  font-weight: 400;
  color: #525252;
}

.pricing-card__price-note {
  color: #525252;
}

.pricing-card--brand .pricing-card__price-unit,
.pricing-card--brand .pricing-card__price-note {
  color: color-mix(in srgb, #0a0a0a 72%, transparent);
}

/* ---- Canales (fila de iconos bajo el precio) ---- */

/* La fila la resuelve el atomo icon-row (icon-row--s + --icon-row-size);
   aqui solo queda el color, que es decision de la card. */
.pricing-card__channels {
  color: #0a0a0a;
  --icon-row-size: 1.5em;
}

/* ---- Integraciones CRM: label estilo feature + logos debajo ---- */

.pricing-card__crm {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-block-start: 20px;
}

/* icon-row--wrap --indented resuelve la fila y la sangria; la card solo
   declara color y la normalizacion de altura de los logos de terceros. */
.pricing-card__logos {
  color: #0a0a0a;
  --icon-row-size: 1.25em;
}

.pricing-card__logos > svg {
  max-width: 8em;
}

.pricing-card__logos svg[aria-label='salesforce'] {
  height: 1.75em;
}

/* ---- CTA ---- */

.pricing-card__cta {
  /* Los botones conservan su ancho intrinseco: el paquete atom-wa-btn posiciona
     iconos en absoluto asumiendo esa geometria — estirarlos los desmaqueta. */
  display: flex;
}

/* La card de marca invierte el CTA por DEFECTO para contrastar sobre el naranja.
   Va envuelto en :where() (especificidad 0) y re-tematiza custom properties en
   vez de pisar background-color: asi cualquier intent del atomo
   (.button--whatsapp, .button--brand) lo gana sin pelear especificidad. Un
   organismo propone, no impone. */
:where(.pricing-card--brand) :where(.pricing-card__cta .button--primary) {
  --button-bg-primary: #0a0a0a;
  --button-hover-bg-primary: #262626;
  --button-fg-primary: #fafafa;
}

/* ---- Divider con etiquetas (patrón tag-row de ATOM) ---- */

/* ---- Benefits ---- */

.pricing-card--brand .icon { color: #0a0a0a; }

:where(.pricing-card--brand) :where(.feature__value.tag--inverse) {
  color: #ff6600;
  background-color: #0a0a0a;
}

/* ---- Scribble decorativo ---- */

.pricing-card__scribble {
  position: absolute;
  font-weight: 400;
  top: 40px;
  right: 40px;
  max-width: 10em;
  font-family: 'gantol', 'brush script mt', cursive;
  font-size: 20px;
  line-height: 0.9;
  text-align: center;
  color: #25d366;
  rotate: -4deg;
  pointer-events: none;
}

.pricing-card--brand .pricing-card__scribble {
  color: #0a0a0a;
}

/* ---- Responsive ---- */

@media (max-width: 767px) {
  .pricing-card {
    padding: 32px 20px;
  }

  .pricing-card__inner {
    gap: 24px;
  }

    .pricing-card__title {
    font-size: 39.06px;
  }

  .pricing-card__scribble {
    position: static;
    align-self: flex-end;
    rotate: 0deg;
  }
}

Codigo fuente

styles/components/pricing-card.css
/* -------------------------------------------------------------------------
   Pricing Card

   Anatomía espejo de las cards de ATOM /plans (docs/osmo-reference: padding
   60px, radio contenedor, título enorme en peso regular, divider con tags,
   benefits con hairlines translúcidas, scribble decorativo), con los valores
   del sistema Atom.

   Superficies:
     (default)                    — var(--card), tarjeta clara
     .pricing-card--brand         — superficie naranja de marca, texto oscuro
                                    (WCAG: brand-foreground es neutral-950)
     data-theme="dark" en la card — tarjeta oscura via theming del sistema:
                                    todos los semantics flipan solo dentro

   Parts: __inner, __tag-row, __title, __price-row, __price, __price-unit,
          __price-note, __channels (fila de iconos de canal), __cta, __divider,
          __benefits, __benefit, __benefit-value, __logos (franja de partners),
          __scribble
   ------------------------------------------------------------------------- */

.pricing-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--spacing-12) var(--spacing-10);
  color: var(--foreground);
  background-color: var(--card);
  border-radius: var(--radius-xl);
  /* Motion: pendiente de diseno de Karen (W3 de evolution). No inventar. */
}

.pricing-card--brand {
  color: var(--brand-foreground);
  background-color: var(--brand);
}

/* En dark la card usa --card (carbon, neutral-900), NO --background
   (neutral-950, casi negro puro): una tarjeta es superficie ELEVADA sobre el
   fondo, y el negro absoluto aplana y encandila en pantallas OLED. */

.pricing-card__inner {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  gap: var(--spacing-8);
}

/* ---- Eyebrow / tags ---- */

.pricing-card__tag-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--spacing-4);
}

/* El look del eyebrow lo da el atomo (.tag--mono.tag--inverse); la card solo
   corrige el relleno inverso sobre su superficie brand (no es un theme). */
:where(.pricing-card--brand) :where(.pricing-card__eyebrow.tag--inverse) {
  color: var(--brand);
  background-color: var(--brand-foreground);
}

/* ---- Título y precio ---- */

.pricing-card__title {
  font-family: var(--font-family-display);
  font-size: var(--font-size-4xl);
  font-weight: var(--font-weight-regular);
  line-height: 1.02;
  letter-spacing: var(--letter-spacing-tighter);
}

.pricing-card__price-row {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-2);
}

.pricing-card__price {
  display: flex;
  align-items: baseline;
  gap: var(--spacing-2);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--letter-spacing-tight);
  line-height: 1.08;
}

.pricing-card__price-unit {
  font-weight: var(--font-weight-regular);
  color: var(--muted-foreground);
}

.pricing-card__price-note {
  color: var(--muted-foreground);
}

.pricing-card--brand .pricing-card__price-unit,
.pricing-card--brand .pricing-card__price-note {
  color: color-mix(in srgb, var(--brand-foreground) 72%, transparent);
}

/* ---- Canales (fila de iconos bajo el precio) ---- */

/* La fila la resuelve el atomo icon-row (icon-row--s + --icon-row-size);
   aqui solo queda el color, que es decision de la card. */
.pricing-card__channels {
  color: var(--foreground);
  --icon-row-size: 1.5em;
}

/* ---- Integraciones CRM: label estilo feature + logos debajo ---- */

.pricing-card__crm {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-4);
  padding-block-start: var(--spacing-5);
}

/* icon-row--wrap --indented resuelve la fila y la sangria; la card solo
   declara color y la normalizacion de altura de los logos de terceros. */
.pricing-card__logos {
  color: var(--foreground);
  --icon-row-size: 1.25em;
}

.pricing-card__logos > svg {
  max-width: 8em;
}

.pricing-card__logos svg[aria-label='salesforce'] {
  height: 1.75em;
}

/* ---- CTA ---- */

.pricing-card__cta {
  /* Los botones conservan su ancho intrinseco: el paquete atom-wa-btn posiciona
     iconos en absoluto asumiendo esa geometria — estirarlos los desmaqueta. */
  display: flex;
}

/* La card de marca invierte el CTA por DEFECTO para contrastar sobre el naranja.
   Va envuelto en :where() (especificidad 0) y re-tematiza custom properties en
   vez de pisar background-color: asi cualquier intent del atomo
   (.button--whatsapp, .button--brand) lo gana sin pelear especificidad. Un
   organismo propone, no impone. */
:where(.pricing-card--brand) :where(.pricing-card__cta .button--primary) {
  --button-bg-primary: var(--foreground);
  --button-hover-bg-primary: var(--primary-hover);
  --button-fg-primary: var(--background);
}

/* ---- Divider con etiquetas (patrón tag-row de ATOM) ---- */

/* ---- Benefits ---- */

.pricing-card--brand .icon { color: var(--brand-foreground); }

:where(.pricing-card--brand) :where(.feature__value.tag--inverse) {
  color: var(--brand);
  background-color: var(--brand-foreground);
}

/* ---- Scribble decorativo ---- */

.pricing-card__scribble {
  position: absolute;
  font-weight: var(--font-weight-regular);
  top: var(--spacing-10);
  right: var(--spacing-10);
  max-width: 10em;
  font-family: var(--font-family-scribble);
  font-size: var(--font-size-lg);
  line-height: 0.9;
  text-align: center;
  color: var(--success);
  rotate: -4deg;
  pointer-events: none;
}

.pricing-card--brand .pricing-card__scribble {
  color: var(--brand-foreground);
}

/* ---- Responsive ---- */

@media (max-width: 767px) {
  .pricing-card {
    padding: var(--spacing-8) var(--spacing-5);
  }

  .pricing-card__inner {
    gap: var(--spacing-6);
  }

    .pricing-card__title {
    font-size: var(--font-size-3xl);
  }

  .pricing-card__scribble {
    position: static;
    align-self: flex-end;
    rotate: 0deg;
  }
}

On this page

Detalles

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

Componente

PricingCard

Source

components-react / cssDisponible via MCP: atom_uikit_source("pricing-card")