Components (registry)Actions
PricingCard
A plan card with price, tags, benefits, and CTA on light, brand, or dark surface. Pricing tables.
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>
| Clase | Propósito |
|---|---|
pricing-card | root |
pricing-card--brand | modifier |
pricing-card__channels | element |
pricing-card__crm | element |
pricing-card__cta | element |
pricing-card__eyebrow | element |
pricing-card__inner | element |
pricing-card__logos | element |
pricing-card__price | element |
pricing-card__price-note | element |
pricing-card__price-row | element |
pricing-card__price-unit | element |
pricing-card__scribble | element |
pricing-card__tag-row | element |
pricing-card__title | 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 |
|---|---|---|---|
| all | bg | #ffffff | card |
| all | fg | #25d366 | success |
| brand | bg | #0a0a0a | brand.foreground |
| brand | fg | #0a0a0a | brand.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; } }