Lineamientos de UI del iframe
Crowder es white-label: cada ticketera define su propia paleta y tipografía, y vos heredás ese tema visual dentro del iframe. Este documento no te prescribe colores ni fuentes — define solo el contrato del slot que se te garantiza y la geometría/forma recomendada para los componentes del iframe.
Tu iframe debe heredar el tema visual de la ticketera (paleta y tipografía) y aplicar las formas descritas acá. Para el protocolo de mensajes ver Implementación del iframe.
A. Contrato del slot
Sección titulada «A. Contrato del slot»Lo que el host de Crowder en la página de la ticketera te garantiza dentro del iframe. Podés asumir estas condiciones como dadas.
Layout y dimensiones
Sección titulada «Layout y dimensiones»El iframe se renderiza dentro de un contenedor .embedded-app-slot en la página de checkout/upsell. El layout padre tiene max-width: 1200px con margen lateral de 24px.
| Breakpoint | Ancho del slot | Layout |
|---|---|---|
Mobile (< 768px) | 100% del viewport (con padding lateral 16px) | Una columna: iframe arriba, Resumen de compra como barra sticky al pie |
Tablet (768–1024px) | Fluido, entre 480 y 700px | Dos columnas: iframe (flex 1) + Resumen (~340px) |
Desktop (≥ 1024px) | Fluido, entre 640 y 760px | Dos columnas: iframe (flex 1) + Resumen (380px fijo). Gap 24px |
Altura del iframe
Sección titulada «Altura del iframe»El iframe es autosize: vos emitís un mensaje type: "display" con sizes.iframeHeight (px) que informa la altura de tu contenido. El host ajusta el <iframe> a ese valor, haciendo clamp con el min-height del slot. Resultado: el iframe crece según su contenido, sin scrollbar interno.
| Breakpoint | min-height (piso del host) |
|---|---|
| Mobile | 480px |
| Desktop / Tablet | 560px |
Si el iframeHeight reportado es menor al piso, el host mantiene el min-height. Si es mayor, el iframe crece. Emití display al montar y cada vez que tu contenido cambia de altura (típicamente con un ResizeObserver sobre document.documentElement). Para conocer el viewport en sentido inverso (host → iframe) no necesitás postMessage: leé window.innerWidth/innerHeight directamente.
Forma del iframe
Sección titulada «Forma del iframe»El host aplica:
.embedded-app-slot__iframe { width: 100%; border: 0; border-radius: 12px; /* desktop / tablet */ background: transparent;}En mobile el border-radius baja a 0px (el iframe ocupa todo el ancho del viewport). Podés usar transparent en tu body para heredar el fondo del host, o pintar tu propio fondo con el token de superficie de la ticketera.
Background fijo (sin dark mode automático)
Sección titulada «Background fijo (sin dark mode automático)»El fondo del iframe debe ser un color fijo, por defecto blanco. No lo cambies en automático con prefers-color-scheme: dark ni con ningún otro media query del sistema. Si la ticketera expone un token de superficie, usalo — pero como valor fijo, no como expresión que conmute con el modo del SO.
/* ✅ Correcto: color fijo */body { background: #ffffff; }
/* ❌ Incorrecto: conmuta con el dark mode del SO */@media (prefers-color-scheme: dark) { body { background: #111; }}Por qué: el host de la ticketera define su propio tema visual y el iframe tiene que verse coherente con ese tema, no con la preferencia del SO del comprador. Un iframe que se pone oscuro mientras el resto del checkout sigue claro rompe la continuidad visual.
Padding y ancho del contenido
Sección titulada «Padding y ancho del contenido»Tu contenido debe respetar 16px de padding contra los bordes del iframe (horizontal y vertical) en todos los breakpoints. El host no inyecta padding interno: si lo omitís, el contenido queda pegado a los costados y a la parte superior/inferior (especialmente notorio en mobile, donde el iframe va sin border-radius y a ancho completo del viewport). Aplicalo al contenedor raíz del body — no a cada componente — para mantener una columna visual consistente.
body { padding: 16px;}El contenido del iframe ocupa el 100% del ancho disponible (slot menos los 16px de cada lado). El slot ya viene dimensionado por el host (entre 480 y 760px según breakpoint, ver tabla arriba), así que no agregues una max-width extra ni margin: auto para centrar la columna interna — eso deja franjas vacías a los costados y el formulario se ve angosto contra un slot ancho. El form, los inputs y el CTA deben extenderse de borde a borde del padding.
Lo que Crowder NO inyecta dentro del iframe
Sección titulada «Lo que Crowder NO inyecta dentro del iframe»- Sin header ni navbar de Crowder (el breadcrumb del evento vive en el host, arriba del slot).
- Sin loading propio durante
submit. Apenas Crowder disparasubmit, el host pone el botón Continuar en estado de loading y bloquea visualmente la transición. El iframe no debe renderizar su propio spinner global ni overlay con loader — alcanza con deshabilitar inputs/botones para evitar edición concurrente (ver Implementación del iframe). - Sin disclaimer legal dentro del iframe — el texto que aclara al comprador que el producto adicional lo ofrece y administra un tercero (“Estos productos son ofrecidos y administrados por…”) lo renderiza Crowder fuera del iframe. No lo repitas adentro.
- Sin paleta ni fuentes propias de Crowder. Tu iframe hereda del tema de la ticketera.
- Sin CSS reset compartido.
Lo que el iframe NO debe renderizar (lo pone el host)
Sección titulada «Lo que el iframe NO debe renderizar (lo pone el host)»Estos elementos viven fuera del iframe. Replicarlos adentro es un error: el usuario ve el mismo contenido dos veces y el flujo se rompe.
- Título y subtítulo de la sección. El host imprime el nombre del bloque (ej. “Add-ons”) y su descripción corta (“Productos opcionales para tu experiencia”) arriba del iframe. No rendericés tu propio H1/H2 con el mismo concepto adentro.
- Footer con sumatorio, total acumulado y botón “Continuar”. El total, el estado del carrito (“Ready to continue”) y el CTA que avanza al siguiente paso los renderiza el host (barra sticky en mobile, columna lateral en desktop). El iframe no debe incluir un footer con totales ni un botón propio para avanzar de paso — para avanzar usá el handshake
submit/submitteddocumentado en Implementación del iframe.
B. Lineamientos de forma
Sección titulada «B. Lineamientos de forma»Esta sección es recomendación, no requisito de protocolo. Define la geometría sin tocar color ni tipografía.
Principios
Sección titulada «Principios»- Color y tipografía los provee la ticketera. Consumí los tokens visuales que la ticketera expone. Si no expone un token específico, usá
currentColor,inherito variables CSS con fallbacks neutrales. - La forma es responsabilidad tuya y debe seguir esta guía para mantener consistencia con el resto del flujo de checkout.
- No reproduzcas branding de Crowder dentro del iframe. El iframe es tuyo y viste el tema de la ticketera.
Geometría de componentes
Sección titulada «Geometría de componentes»| Componente | border-radius | Notas |
|---|---|---|
| Cards de contexto y agrupadoras | 8px | Borde izquierdo destacado de 3px solid (color primario del tema) |
| Sub-cards (ej. ítem de ticket) | 8px | Borde 1px en color de borde del tema, padding 12px 16px |
| Cards de producto (grilla de shop) | 4px | overflow: hidden para que la imagen respete el radio |
| Chips de filtro de categoría | 8px | Padding 12px 20px, diferenciación por fondo |
| Rows de selección (lista) | 4px | Separadas por border-bottom: 1px solid |
| Badges de tipo / tarifa | 4px | Padding 2px 8px, tipografía pequeña |
| Botón primario (CTA) | 6px | Altura 48px |
| Stepper numérico (— 0 +) | 3px | Altura 50px |
| Modal / dialog | 6px | — |
| Inputs / fields | 0 (sin redondeo) | Underline-only: border-bottom: 1px solid, sin borde lateral ni superior, fondo transparente, height: 40px. Ver tab Inputs y formularios |
Tipografía de descripciones e identificadores
Sección titulada «Tipografía de descripciones e identificadores»Regla simple: sans para prosa, mono solo para identificadores opacos.
| Tipo de contenido | Clase / spec | Cuándo |
|---|---|---|
| Descripción / texto explicativo | text-sm text-muted-foreground (sans) | Prosa que el comprador lee — descripciones de items, ayudas, leyendas |
| Metadata legible del item | text-xs text-muted-foreground (sans) | Palabras de dominio: sector, tarifa, fila, asiento, categoría |
| Identificadores opacos | text-xs font-mono | Cadenas que el ojo necesita alinear o comparar carácter a carácter: UUID, short codes, número de orden, hash |
Por qué: el monoespaciado aporta legibilidad técnica solo cuando hay caracteres a alinear o comparar. Para palabras de dominio (sector A, fila 12, Platea VIP) el mono se ve “técnico” sin razón y rompe la coherencia con el resto del item. Reservalo para los IDs reales.
Patrones por componente
Sección titulada «Patrones por componente»Las cards que muestran información de contexto usan un borde izquierdo de 3px que indica el tipo de bloque:
- Contexto provisto por Crowder (evento, tickets recibidos): color primario del tema.
- Tu propia respuesta (ítems seleccionados): color de éxito del tema.
Resto de la card: border-radius: 8px, padding 20px, fondo en color de superficie del tema.
Tabs de texto plano con indicador de línea inferior. Sin fondo, sin border-radius.
- Altura de fila:
~44px - Tab activo:
border-bottom: 2px solid(color primario del tema) - Tab inactivo: sin borde
- Separador inferior del bloque:
border-bottom: 1px solid(color de borde del tema) - Padding por tab:
8px 0px,margin-right: 24px
border-radius: 6px, altura48px- Tipografía:
font-size: 0.875rem(14px relativo al body),font-weight: 600,line-height: 1,letter-spacing: 0— heredando lafont-familydel tema. No subir a1rem: con altura 48px el texto a 16px se ve sobredimensionado contra el resto del cuerpo. - Padding horizontal:
16pxmínimo. El botón eswidth: autopor defecto — no estires al 100% del ancho salvo en mobile angosto (menos de 480px). - Estado habilitado: fondo primario, texto sobre primario
- Estado deshabilitado:
opacity: 0.5, sin cambio de forma - Foco:
outline: 2px solid(color primario del tema),outline-offset: 2px - En el iframe el CTA suele alinearse a la derecha y ocupar ~50% del ancho
Para selección de cantidades (— · 0 · +).
- Altura
50px,border-radius: 3px - Botones laterales del mismo alto, separación con borde 1px
Grilla de 2 columnas. Cada card:
border-radius: 4px,overflow: hidden- Imagen en la mitad superior (proporción 16:9 o cuadrada)
- Label de categoría en metadata (uppercase pequeño)
- Nombre del producto
- Precio + stepper alineados al pie de la card
Crowder es la plataforma padre. Cualquier formulario embebido (iframe, partner forms, integraciones) adapta su forma visual a este sistema. Los colores quedan excluidos — son parte de la customización white-label de cada ticketera.
Tipografía del sistema de formularios
El iframe nunca fija font-size en px para campos, labels, helpers o botones — todos los tamaños se expresan en rem/em heredando del body que define la ticketera. Así un partner con body en 14px y otro en 16px mantienen las mismas proporciones internas.
| Elemento | font-size | line-height | Otras |
|---|---|---|---|
| Valor del input / select / textarea | 0.875rem (heredado del body) | 1.4 | font-weight: normal |
| Floating label (idle) | 0.875rem | 1.4 | mismo tamaño que el valor |
| Floating label (activo) | ~0.74rem (vía scale(0.85)) | — | ver patrón abajo |
| Helper text / error debajo del campo | 0.75rem | 1.3 | — |
| CTA del formulario | 0.875rem | 1 | font-weight: 600 (ver tab Botón primario) |
El input y el CTA quedan al mismo tamaño (0.875rem): el peso 600 y la altura 48px diferencian al botón sin agrandar el texto. Nunca subir el valor del input a 1rem — a 16px se ve sobredimensionado contra el resto del cuerpo del checkout.
En mobile el host garantiza body ≥ 16px para evitar el auto-zoom de iOS — el iframe no necesita aplicar un mínimo propio en los inputs.
Input de texto
border-bottom: 1px solid(único borde — sin borde superior, izquierdo ni derecho)border-radius: 0background-color: transparentheight: 40px,margin: 0font-size: 0.875rem,line-height: 1.4,font-weight: normal— relativo al body del host, sin override en px
Select / dropdown
- Mismas reglas que el input de texto (underline, sin borde lateral, sin redondeo)
- El chevron de expansión es el único elemento diferenciador permitido
Wrapper de campo
position: relative(requerido por el floating label)margin-bottom: 40px- Sin borde, sin fondo, sin redondeo
Floating label
Patrón floating label: la etiqueta vive dentro del campo y se eleva al recibir foco o cuando el campo tiene contenido. No se usan labels fijas por encima del campo.
- Estado por defecto:
position: absolute; top: 20px; font-size: 1rem; opacity: 0.5(mismo tamaño que el valor del input) - Estado activo (focus o con valor):
transform: translate(0, -30px) scale(0.75); opacity: 0.5(queda en ~0.75remefectivos) - Transición:
transform 0.25s, opacity 0.25s ease-in-out
Estados del campo
- Focus o con valor:
border-colordel underline cambia al color de marca del tema — ese cambio es el indicador de foco, no eloutlinedefault del browser - Inválido / error:
border-coloren color de error del tema - Reseteá el
outlinedefault (outline: 0; box-shadow: 0) — el cambio deborder-colorcubre el requisito de foco visible de la sección D, siempre que el contraste con el fondo cumpla WCAG AA
Radio buttons y checkboxes
- Radio o checkbox simple, alineado con el texto
- Sin contenedor con borde alrededor de cada opción (no se usa el patrón “card-option”)
- El patrón Rows de selección (ver tab correspondiente) es distinto: lista de opciones con ícono + texto + precio + chevron. No es un control de formulario, es una lista navegable, y mantiene sus separadores y borde de selección.
Botón primario (CTA) del formulario
Aplica todo lo del tab Botón primario (filled, border-radius: 6px, altura 48px). El refuerzo específico para formularios: el CTA outlined sin fill no está permitido como acción primaria. border-radius: 6px en el CTA es el único radio admitido dentro del sistema de formularios — todo el resto (input, select, wrapper) va sin redondeo.
Lista vertical de opciones (ej. método de entrega, planes de seguro).
border-radius: 4px, padding30px 20px- Ícono / radio a la izquierda (~50×50px), texto al centro, precio + chevron
›a la derecha - Separadas por
border-bottom: 1px solid(color de borde del tema) - Opción seleccionada:
border-left: 3px solid(color primario del tema)
C. Formato de moneda
Sección titulada «C. Formato de moneda»El context te llega con currency como código ISO 4217 (ARS, PEN, USD, BRL, etc.) y los price como number con hasta 2 decimales. No muestres el código crudo ni 15000.5 sin formatear: el iframe tiene que renderizar el monto con el símbolo, los separadores y los decimales que el usuario de esa plaza espera ver.
La forma simple y correcta de hacerlo es delegar en el browser:
const formatMoney = (amount, currency, locale) => new Intl.NumberFormat(locale, { style: 'currency', currency, // 'PEN', 'ARS', 'USD'... }).format(amount);
// formatMoney(15000.5, 'PEN', 'es-PE') → "S/ 15,000.50"// formatMoney(15000.5, 'ARS', 'es-AR') → "$ 15.000,50"// formatMoney(15000, 'CLP', 'es-CL') → "$ 15.000"Usá el locale del context para que separadores y posición del símbolo respeten la convención local. Si tenés que mostrar un monto antes de recibir el context, mostrá un skeleton — no inventes un símbolo.
Tabla de referencia
Sección titulada «Tabla de referencia»Símbolo y decimales esperados por moneda. La columna Decimales es el minimumFractionDigits / maximumFractionDigits que debe ver el usuario (no la precisión del number que llega por el wire).
currency | País | Símbolo | Decimales | Ejemplo (15000.5) |
|---|---|---|---|---|
ARS | Argentina | $ | 2 | $ 15.000,50 |
BOB | Bolivia | Bs | 2 | Bs 15.000,50 |
BRL | Brasil | R$ | 2 | R$ 15.000,50 |
CLP | Chile | $ | 0 | $ 15.001 |
COP | Colombia | $ | 0 | $ 15.001 |
USD | Ecuador | US$ / $ | 2 | US$ 15,000.50 |
EUR | Guayana Francesa | € | 2 | 15.000,50 € |
GYD | Guyana | G$ | 0 | G$ 15.001 |
PYG | Paraguay | ₲ | 0 | ₲ 15.001 |
PEN | Perú | S/ | 2 | S/ 15,000.50 |
SRD | Surinam | $ | 2 | $ 15.000,50 |
UYU | Uruguay | $U | 2 | $U 15.000,50 |
VES | Venezuela | Bs. | 2 | Bs. 15.000,50 |
D. Accesibilidad
Sección titulada «D. Accesibilidad»- Foco visible en todos los interactivos:
:focus-visiblecon outline de 2px youtline-offset: 2px. - Targets táctiles ≥ 44×44px en mobile.
prefers-reduced-motion: reducedebe deshabilitar animaciones no esenciales.- Idioma del contenido alineado al
localedel payload. - Contraste: sos responsable de respetar WCAG AA al aplicar la paleta de la ticketera (texto 4.5:1, componentes 3:1).