Files
whatsapp-api-mockup/css/iphone.css
T
urieljarethandClaude Opus 5.5 50a64f21e3 Render: chat pegado abajo, hojas sin desfase, separadores y fidelidad iOS
- scrollAbajo instantáneo + ResizeObserver 'pegado abajo' (imágenes tardías, pestaña oculta)
- Hoja de opciones busca la fila viva; re-render de su mensaje cierra la hoja
- Separadores de filas de la hoja, proporciones de lista, un solo separador entre grupos
- Home indicator sobre la hoja, composer oscuro #232626 con hairline, padding 7/12/6
- URLs sin puntuación final, www. -> https://, negrita/cursiva/tachado con reglas de WhatsApp
- Documento: 'tamaño • ext' o 'PDF'; transición de color al cambiar tema
- Presentación sin toolbar en escritorio (Esc para salir), hooks de zoom, móvil sin marco

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 21:13:13 -06:00

315 lines
9.6 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
WhatsApp Cloud API Mockup — css/iphone.css (Módulo A · shell)
Columna de vista previa (#panel-movil, #barra-movil) y marco iPhone
393×852 (radio 55, negro) con Dynamic Island, status bar de 54 pt e
indicador home. Escalado 100 % CSS (transform + var --wam-escala)
para caber siempre en la pantalla. Geometría según docs/research-ui.md §7.
===================================================================== */
/* --- Escala del teléfono ----------------------------------------------
Fallback por pasos (media queries) y ajuste exacto con aritmética
tipada de CSS (@supports), que calcula escala = espacio disponible /
tamaño natural. El escenario reserva el tamaño YA escalado para que
el layout no deje huecos ni recortes. */
:root { --wam-escala: 1; }
@media (max-height: 1030px) { :root { --wam-escala: .9; } }
@media (max-height: 940px) { :root { --wam-escala: .82; } }
@media (max-height: 860px) { :root { --wam-escala: .74; } }
@media (max-height: 780px) { :root { --wam-escala: .66; } }
@media (max-height: 700px) { :root { --wam-escala: .58; } }
@media (max-height: 620px) { :root { --wam-escala: .5; } }
@supports (scale: calc(1vh / 1px)) {
:root {
/* 150 px = toolbar + barra-movil + aire; 352 px = editor mínimo + aire.
px/px → número (requisito de scale()); min/max solo comparan números. */
--wam-escala: max(.3, min(
1,
(100vh - 150px) / 852px,
(100vw - 352px) / 393px
));
}
body.presentacion {
/* sin editor ni barra de vista previa: solo resta la toolbar (~57 px) + aire */
--wam-escala: max(.3, min(
1,
(100vh - 96px) / 852px,
(100vw - 64px) / 393px
));
}
}
/* Presentación: se oculta la barra "Vista previa / Reproducir demo" para
que el teléfono crezca (en 1280×720 pasa de .67 a ~.73). */
body.presentacion #barra-movil { display: none; }
/* Presentación en escritorio (ratón + teclado): captura limpia, sin toolbar.
Se sale con Esc o P (js/ux.js); una pista flotante lo recuerda. */
@media (min-width: 901px) and (hover: hover) and (pointer: fine) {
body.presentacion #toolbar { display: none; }
body.presentacion #panel-movil { position: relative; }
body.presentacion #panel-movil::before {
content: "Esc para salir";
position: absolute;
top: 12px;
right: 16px;
padding: 5px 10px;
border-radius: 999px;
background: rgba(11, 20, 26, .55);
color: #fff;
font-size: 12px;
font-weight: 600;
pointer-events: none;
opacity: .75;
z-index: 5;
}
@supports (scale: calc(1vh / 1px)) {
body.presentacion {
--wam-escala: max(.3, min(1, (100vh - 32px) / 852px, (100vw - 64px) / 393px));
}
}
}
/* Zoom manual (lo fija js/ux.js con body[data-wam-zoom]; sin atributo =
"Ajustar", el cálculo automático). Con zoom fijo el panel hace scroll. */
html body[data-wam-zoom="100"] { --wam-escala: 1; }
html body[data-wam-zoom="125"] { --wam-escala: 1.25; }
body[data-wam-zoom="100"] #panel-movil,
body[data-wam-zoom="125"] #panel-movil { overflow: auto; }
body[data-wam-zoom="100"] .iphone-escena,
body[data-wam-zoom="125"] .iphone-escena { margin: 0 auto; }
/* --- Columna de vista previa (#panel-movil) ---------------------------- */
#panel-movil {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
align-items: center;
padding: 14px 16px 18px;
overflow: auto;
background:
radial-gradient(1200px 500px at 50% -10%, rgba(0, 168, 132, .08), transparent 60%),
var(--wam-fondo);
}
/* Barra sobre el teléfono */
#barra-movil {
flex: none;
width: 100%;
max-width: 560px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
#barra-movil .barra-titulo {
font-size: 12px;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--wam-texto-suave);
}
#btn-reproducir {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 16px;
border: 0;
border-radius: 999px;
background: var(--wam-acento);
color: #fff;
font-size: 13px;
font-weight: 700;
cursor: pointer;
box-shadow: 0 4px 14px rgba(0, 168, 132, .35);
transition: background-color .15s ease, transform .05s ease;
}
#btn-reproducir:hover { background: var(--wam-acento-hover); }
#btn-reproducir:active { transform: translateY(1px); }
/* Escenario: reserva el tamaño ya escalado */
.iphone-escena {
flex: none;
margin: auto; /* centra con espacio sobrante; sin clipping si no cabe */
width: calc(393px * var(--wam-escala));
height: calc(852px * var(--wam-escala));
}
/* --- Marco iPhone: 393×852 pt, radio 55, negro (research-ui §7) -------- */
.iphone {
position: relative;
width: 393px;
height: 852px;
border-radius: 55px;
background: #000;
overflow: hidden;
transform: scale(var(--wam-escala));
transform-origin: top left;
box-shadow:
0 0 0 2px #3d4348, /* aro de titanio */
0 0 0 3.5px #1b1f22,
0 30px 60px rgba(4, 12, 10, .45);
}
/* Pantalla: llena el marco; el look del chat lo gobierna .wa (módulo B) */
.pantalla {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 47px;
background: #efeae2; /* valor de arranque (claro); .wa / .wa.oscuro mandan */
padding-top: 54px; /* status bar de 54 pt: el header arranca debajo */
}
/* Dynamic Island: 126×37, top 11 */
.island {
position: absolute;
top: 11px;
left: 50%;
transform: translateX(-50%);
width: 126px;
height: 37px;
border-radius: 20px;
background: #000;
z-index: 40;
}
.island::after { /* lente de la cámara frontal */
content: "";
position: absolute;
top: 50%;
right: 11px;
transform: translateY(-50%);
width: 9px;
height: 9px;
border-radius: 50%;
background: radial-gradient(circle at 35% 32%, #223244 0 22%, #05080c 62%);
}
/* Status bar: 54 pt; hora "9:41" + señal / wifi / batería */
.statusbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 54px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 18px 30px 0 34px;
color: #000; /* en claro, status bar negra */
font-size: 15px;
font-weight: 600;
letter-spacing: .02em;
z-index: 30;
pointer-events: none;
}
.sb-hora { min-width: 44px; font-variant-numeric: tabular-nums; }
.statusbar .sb-iconos { display: flex; align-items: center; gap: 7px; }
#pantalla.oscuro .statusbar { color: #fff; }
/* Indicador home: 139×5, bottom 8 */
.home-indicator {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
width: 134px;
height: 5px;
border-radius: 3px;
background: #fff; /* en oscuro */
z-index: 70; /* encima de la hoja inferior (.sheet z 60), como en iOS */
pointer-events: none;
}
#pantalla:not(.oscuro) .home-indicator { background: #000; } /* en claro, negro */
/* --- Colocación del chat (estructura A; la estética es css/whatsapp.css B) */
#chat-header { flex: none; }
.header-linea { display: flex; align-items: center; gap: 4px; min-width: 0; }
#chat-verificado { display: inline-flex; flex: none; }
#chat-cuerpo {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
list-style: none;
padding: 8px 0 14px;
scroll-behavior: auto; /* el suave solo en enfocarEnChat (behavior explícito) */
overscroll-behavior: contain;
}
#chat-escribiendo { flex: none; }
#composer { flex: none; }
/* Composer: solo valores de arranque con var(--wa-*) de B (con fallback);
whatsapp.css (cargado después) prevalece en caso de empate. */
#composer .attach,
#composer .enviar {
display: inline-flex;
padding: 2px;
border: 0;
background: none;
cursor: pointer;
}
#composer .attach { color: var(--wa-chrome-text, #000); }
#composer .field svg { flex: none; }
#composer-input {
flex: 1 1 auto;
min-width: 0;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: inherit;
font-size: 16px;
}
#composer-input::placeholder { color: var(--wa-text-meta, #667781); }
/* Color de arranque de los iconos del header (B puede reestilarlos) */
#chat-header .back-chevron { color: var(--wa-accent, #00a884); }
#chat-header .icon-video,
#chat-header .icon-call { color: var(--wa-chrome-text, #111b21); }
/* --- Modo presentación: el iPhone se centra y respira más --------------- */
body.presentacion #panel-movil { padding: 14px 20px 22px; }
/* --- Móvil real (≤ 520 px): sin marco ----------------------------------
En el propio teléfono del consultor sobra el iPhone dibujado: la pantalla
ocupa el ancho y el alto disponibles a escala 1 (sin isla, status bar ni
indicador home duplicados). body.con-marco lo desactiva. */
@media (max-width: 520px) {
html body:not(.con-marco) { --wam-escala: 1; }
body:not(.con-marco) #panel-movil { padding-left: 0; padding-right: 0; }
body:not(.con-marco) #barra-movil { padding: 0 16px; margin-bottom: 8px; }
body:not(.con-marco) .iphone-escena {
flex: 1 1 auto;
width: 100%;
height: auto;
min-height: 420px;
margin: 0;
}
body:not(.con-marco) .iphone {
width: 100%;
height: 100%;
border-radius: 0;
transform: none;
box-shadow: none;
background: transparent;
}
body:not(.con-marco) .pantalla { border-radius: 0; padding-top: 0; }
body:not(.con-marco) .island,
body:not(.con-marco) .statusbar,
body:not(.con-marco) .home-indicator { display: none; }
body:not(.con-marco) .wa #composer { padding-bottom: 8px; }
body:not(.con-marco) .wa .sheet-panel { padding-bottom: 12px; }
}
/* --- Accesibilidad: menos movimiento ------------------------------------- */
@media (prefers-reduced-motion: reduce) {
#btn-reproducir { transition: none; }
}