Marca
El azul de la plataforma. Acción principal, enlaces, foco del teclado.
--marca rellena · --marca-tinta escribe · --marca-lavado es el chip
#E8EFFF
Guía de estilo
Todo lo que se ve en las quince páginas sale de un solo archivo, css/base.css, sin framework ni compilación. Esta página es su documentación, escrita con las mismas piezas: si algo se rompiera, se vería aquí antes que en ningún otro sitio. Empezando por este fondo.
Ninguno de los nueve chips es jade, y no es casualidad: sobre un lavado jade, un chip jade sería un rectángulo del mismo color que el fondo con letras dentro. La línea de 3 px de aquí arriba es .filo-arriba, y lo de encima .bloque-color. Los dos están explicados más abajo, sin salir de la página.
Paleta
No es una paleta inventada para el sitio: es la de la plataforma. Quien llega desde un anuncio y luego entra en la aplicación reconoce los mismos colores en el mismo sitio.
En la edición de noche la paleta entera se sustituye por una versión más clara y menos saturada, --x-tinta pasa a valer lo mismo que --x, y --sobre-x se vuelve tinta oscura: en oscuro ningún color de la paleta admite texto blanco encima. El mejor de los seis, el azul claro, se queda en 2,69:1 con blanco, y el ámbar baja hasta 1,61:1. Por eso nunca se escribe un hexadecimal en el HTML: se escribe la variable, y el tema decide.
El azul de la plataforma. Acción principal, enlaces, foco del teclado.
--marca rellena · --marca-tinta escribe · --marca-lavado es el chip
#E8EFFF
La constancia. También los avisos de «esto está pendiente», porque es el único cálido que no se confunde con un error.
--racha rellena · --racha-tinta escribe · --racha-lavado es el chip
#FFEEEB
Los puntos que se ganan, y la nota del error del hispanohablante en cada unidad.
--xp rellena · --xp-tinta escribe · --xp-lavado es el chip
#FFF7E8
Lo que sale bien: respuesta correcta, lección terminada, lo que sí incluye un plan.
--acierto rellena · --acierto-tinta escribe · --acierto-lavado es el chip
#E6F8F2
Lo de pago y lo delicado: planes, privacidad, datos personales.
--premium rellena · --premium-tinta escribe · --premium-lavado es el chip
#F1EAFD
Vídeo, vocabulario y todo lo que se consume mirando en vez de respondiendo.
--mirar rellena · --mirar-tinta escribe · --mirar-lavado es el chip
#E5F7FA
El jade vivo #10B981 sobre el fondo claro da 2,36:1 de contraste: escrito en texto pequeño se lee como un gris verdoso y la gente cree que está desactivado. La variante tinta #0B7F58 es el mismo tono llevado a 4,67:1, que ya pasa el mínimo de la norma para texto. De ahí la regla corta: «--x» rellena, «--x-tinta» escribe, «--x-lavado» es el chip». Un color vivo en un titular grande está bien; ese mismo color en un folio de doce píxeles, no.
Cada color tiene un cuarto valor que no se escribe a mano: el mismo tono llevado a su fondo, con color-mix(in oklab, var(--x) 90%, #000). Es el extremo al que llega el degradado de un bloque a sangre, y también el color en el que aterriza un botón al pulsarlo. Las seis tiras de arriba son ese degradado de verdad, no una imitación: llevan la clase bloque-color puesta.
En OKLAB y no en sRGB porque mezclar #FF5A3C con negro en sRGB lo apaga a pardo —el verde y el azul caen a distinta velocidad—, mientras que en OKLAB baja la luz y deja el coral donde estaba.
Y el 90 % no es un número redondo elegido a ojo: es el tope que aguanta el contraste. El extremo hondo lleva encima la misma tinta que el color vivo, y en seis de los doce colores esa tinta es OSCURA, así que oscurecer el fondo les RESTA contraste en vez de sumarlo. Al 82 %, que fue lo primero que se probó, el coral hondo se quedaba en 3,77:1 y el naranja C2 en 4,13:1, los dos por debajo del mínimo. Si algún día se quiere más profundidad no se sube el porcentaje: se cambia la tinta del bloque, que es lo que pone el techo.
Los doce --x-hondo no se repiten en los dos bloques del tema oscuro. Son var() de los colores de arriba, que sí se redeclaran allí, y un var() dentro de una ficha se resuelve con el valor que gana en el elemento donde está escrita: el hondo de noche sale solo. Repetir la fórmula solo abriría la puerta a que un día las tres copias dijeran cosas distintas.
La escalera de niveles
Recorren toda la plataforma: la barra de progreso, la insignia del perfil, el borde de la lección, el certificado. Subir de nivel se ve, no solo se lee. Funcionan igual que los seis de arriba: la clase se pone y lo de dentro la hereda.
n-a1
#10B981
#0B7F58
n-a2
#06B6D4
#047A8F
n-b1
#2563FF
#1C5CFF
n-b2
#7C3AED
#7C3AED
n-c1
#DB2777
#CA226C
n-c2
#F97316
#B94E05
Levanta la vista. Bajo la barra superior de esta misma página hay una línea de 2 px con los seis colores en orden, de A1 a C2 y de izquierda a derecha. Va ahí porque ahí significa algo —es el programa entero, el mismo recorrido que la barra de progreso y que la insignia del perfil—; en cualquier otro sitio, seis colores seguidos serían un arcoíris de adorno. Está al 55 % para que se lea como el canto de una pila de papeles de colores y no como una cinta, y se dibuja en .barra::after y no en el border-bottom, que ya lo ocupa la línea de 1 px que separa la barra del contenido: la línea fina encima del filete es justo lo que le da canto. Con colores forzados por el sistema se retira entero, para no pintar seis colores que el sistema no ha elegido.
La regla que ahorra más trabajo
Es la regla de la plataforma y aquí funciona igual. Dar color a una pantalla nueva no es escribir reglas para ella: es ponerle su clase.
<section class="s-racha bloque-lavado"> <div class="pliego"> <p class="folio">…</p> ← ya es coral <span class="chip">…</span> ← ya es coral lavado <article class="tarjeta tarjeta-seccion"> ← ya tiene el borde coral <div class="aviso"> ← ya es coral </div> </section>
Las seis clases de sección —s-marca, s-acierto, s-racha, s-xp, s-premium, s-mirar— y las seis de nivel no hacen otra cosa que redefinir cinco variables: --seccion, --seccion-tinta, --seccion-lavado, --seccion-hondo y --sobre-seccion. El folio, el chip, el aviso, el nodo del sendero, la barra de progreso, el borde de la tarjeta y la clave de la tabla las leen de ahí. Encima, dos modificadores: bloque-color viste la sección entera del color a sangre, y bloque-lavado la viste de su versión clara.
La quinta, --seccion-hondo, viaja con las otras cuatro en cada clase y NO se deriva de --seccion en un solo sitio. Si estuviera escrita una vez en :root como una mezcla de var(--seccion), se resolvería allí —donde --seccion todavía es el azul de marca— y las doce secciones tendrían el mismo fondo hondo azul. Un var() dentro de una ficha se resuelve en el elemento donde está escrita la ficha, no donde se usa.
El bloque a sangre
Esto que hay detrás de este párrafo es .bloque-color. No es un valor de color repetido: es un degradado dentro de la familia coral, con grano encima. Las dos cosas están puestas por el mismo motivo —que no parezca generado— y las dos se pueden comprobar aquí mismo.
En una pantalla de 27 pulgadas se ve que es UN valor repetido, y eso es exactamente lo que delata una página generada.
La misma clase que viste la sección entera, en miniatura. Mismo degradado, mismo grano, mismo coral.
Un degradado de azul a violeta es lo que delata una página generada: son DOS familias y el ojo lo lee como un efecto pegado encima. El mismo azul llevado a su tono hondo se lee como material —tinta sobre papel, plástico teñido—, porque es lo que hace la luz de verdad al caer sobre una superficie de un solo color. Y 160° es casi vertical, ligeramente escorado: es por donde cae la luz en una habitación, así que el ojo lo acepta sin preguntarse de dónde viene.
Un degradado liso a pantalla completa hace BANDAS: el panel solo tiene 256 escalones por canal y a lo largo de 800 px de alto se ven las fronteras entre uno y el siguiente. El grano las rompe porque mete ruido por debajo del escalón; es tramado, la misma razón por la que una foto impresa lleva trama. Son dos capas de puntos de medio píxel, una clara y otra oscura, en rejillas de 3 y 5 px: primos entre sí, así que el patrón no se repite hasta los 15 px y no se lee como rejilla. Las dos a la misma opacidad, para que la media de luz no se mueva ni una décima y el contraste medido siga valiendo. No se ve. Se nota.
Sacan un filete de 3 px del color de la sección hasta el borde mismo de la pantalla, para que el color toque el filo del papel como en una portada impresa. Van en la <section>, que es lo único que llega de lado a lado: .pliego no, porque tiene tope de ancho. Hay uno arriba del índice de esta página y otro debajo de «El móvil de 320 px».
El filete es DECORACIÓN, no información: repite un color que la sección ya anuncia en el folio y en el chip, los dos escritos con --seccion-tinta, que sí va a contraste de texto completo. Sobre el fondo va de 1,70:1 —el ámbar— a 5,31:1 —el violeta—, la misma horquilla que la barrita de .folio::before, que mide también 3 px y lleva ahí desde el principio.
Quien pide más contraste quiere fondos planos: el degradado le da dos valores distintos bajo la misma línea de texto —y el peor de los dos es el que manda— y el grano le mete ruido justo debajo de las letras. Se devuelve el color vivo, que en los seis colores de tinta oscura es el extremo bueno del degradado: el coral vuelve de 4,70:1 a 6,11:1 y el naranja C2 de 5,18:1 a 6,75:1.
Con colores forzados el degradado sí se pinta —no es una sombra— y se convierte en una mancha de un color que el sistema no ha elegido, encima de un texto que sí. Se retira, y mandan Canvas y CanvasText.
Lo que cambia dentro
Y lo primero, el propio bloque: este es el segundo seguido, así que pierde su degradado, se queda en el coral hondo donde acababa el de arriba y marca la juntura con un filete de 1 px. Es la línea fina que tienes justo encima. Sin esa regla habría dos corales casi iguales con una costura horizontal a media pantalla, que se lee como un fallo de maquetación y no como una decisión.
Estos tres van dentro de una tarjeta blanca, así que salen como fuera de todo bloque: azul, jade y contorno gris. Los tres de debajo son exactamente el mismo marcado, sin una clase de más, puestos directamente sobre el coral.
El botón principal dentro de un bloque de color era jade sobre azul: dos saturados peleándose a dos centímetros. Ahora se invierte a papel, con el texto en --seccion-tinta. Es el contraste máximo posible y además invierte la jerarquía sin meter un tercer color, que es lo que ensucia. El de contorno se queda en contorno, pero del color del papel: un borde gris sobre coral desaparece. Y el texto secundario hereda y se apaga con opacidad —el pie de figura, al 82 %— en vez de irse a un gris fijo, que sobre un fondo de color se ve sucio y deja de funcionar en cuanto la sección cambia de tono.
Era una limitación de verdad, no un detalle. .unidad, .faq details, .aviso, .tarjeta y .plan declaraban background pero no color: dentro de un bloque heredaban --sobre-seccion —la tinta pensada para ir SOBRE el color de la sección— y acababan escribiéndola sobre su propio fondo de papel. En tema claro pasaba desapercibido; en oscuro quedaba tinta casi negra sobre tarjeta casi negra. Consecuencia práctica: ninguna de esas piezas podía meterse en una sección a sangre, que son justo las que más se lucen ahí. Las tres de aquí abajo son la prueba de que ya se puede.
Papel blanco y tinta propia, la de leer sobre papel, no la del bloque. La flecha sí toma la tinta de la sección. La página de preguntas frecuentes monta así su última sección entera.
Fuente: sección 26 de base.css.
Su fondo es el lavado de la sección y su tinta la de siempre, no la del bloque.
Papel, borde transparente y tinta propia. Dentro del bloque el borde sobra: el contraste con el fondo ya la recorta.
Tipografía
Source Serif 4
Una serif con la que un titular parece escrito, no maquetado. Solo en h1–h4, cifras grandes e importes.
Archivo
Párrafos, botones, menús, tablas y formularios. Todo lo que se lee de corrido o se pulsa.
/ˈθɪŋk/ /ɔː/
Charis SIL, la de SIL International para transcripción. Se aplica con .fon, nunca a ojo.
El corte de Charis SIL que sirve Google Fonts no trae θ (U+03B8), ˈ (U+02C8) ni ː (U+02D0) — justo los tres signos que llevan /ˈθɪŋk/ y /ɔː/, que son de las primeras unidades del curso. Con Charis sola esos tres salen en la fuente por defecto del sistema, con otra altura de x, y la transcripción parece una errata. Noto Serif va detrás y el navegador rellena esos glifos uno a uno, sin que nadie tenga que escribir nada.
Source Serif 4, Archivo, Charis SIL y Noto Serif se sirven desde este mismo dominio, desde la carpeta fuentes/ y declaradas en css/fuentes.css. No es una preferencia de estilo: la CSP del servidor es style-src 'self' y font-src 'self' data:, así que el enlace a Google Fonts lo rechazaba el navegador sin decir nada en pantalla y la tipografía del sitio no llegaba a verse. Lo que se leía eran los respaldos del sistema: los titulares en Georgia y el texto en Arial. De paso se ahorran dos viajes de red encadenados —la hoja en fonts.googleapis.com y después el WOFF2 en fonts.gstatic.com—, y desde que las cachés de navegador se particionan por sitio la caché compartida de Google ya no existe: no se pierde nada por traérselas.
Cada familia lleva además una cara de respaldo calibrada: la fuente del sistema con las métricas recalculadas para que ocupe exactamente lo mismo que la definitiva. Ahí viven size-adjust —108,59 % para Source Serif 4, 98,6 y 96,4 % para Archivo y 111,59 % para Charis SIL—, ascent-override y descent-override. Con eso, el intercambio de font-display: swap no mueve ni un píxel de la línea cuando llega la fuente real. Los números salen de medir los binarios, no escritos a ojo, y van en la cara de respaldo y nunca en la real: puestos en la real deformarían la tipografía buena en vez de arreglar el salto.
Y sin un solo error visible: la CSP lo bloquea en silencio, la página carga entera y lo único que cambia es la letra. Si algún día hace falta una familia nueva, se descarga, se mide y se declara aquí; no se enlaza.
Ocho tamaños y ni uno más. Los cuatro grandes son clamp() que interpolan entre 360 px y 1280 px de ventana, así que no hay ningún salto brusco al girar un móvil. El cuerpo base va en rem: quien ha subido el tamaño de letra de su navegador tiene que notarlo aquí también.
Botones
box-shadow: 0 4px 0 var(--relieve-marca) — una sombra sólida, sin difuminar, del propio color llevado más oscuro: #0036BE para marca, #0A7854 para acierto, #CD2000 para racha. Al pulsar, el botón se desplaza exactamente los 4 px que mide ese borde y la sombra desaparece: el botón ocupa el hueco que dejaba.
Una sombra difusa dice «tarjeta»: algo que flota y que se mira. Esta dice «esto se hunde»: algo que se pulsa. Con blur el botón se convierte en la caja genérica de cualquier interfaz y deja de pedir que lo toquen. Quien pide menos movimiento en el sistema conserva el color y pierde el desplazamiento, no al revés.
El hover de la marca era #3C74FF escrito a mano: un azul MÁS CLARO que el de reposo, que con el blanco de su texto encima daba 4,09:1 —por debajo del mínimo— y que de noche seguía siendo ese mismo azul de día, ajeno a la paleta oscura. Aclarar al pasar por encima es además lo que hace una web hecha a ojo: una superficie real se oscurece donde se apoya la mano. Ahora el hover baja un 8 % en OKLAB y la pulsación cae hasta --x-hondo, así que el botón aterriza exactamente en el color del fondo de su familia. Medido: el azul va de 4,88 a 5,87 y a 6,15 de día, y de 7,30 a 5,87 y a 5,57 de noche; el peor de los seis pares es el coral pulsado, 4,70:1.
.btn-borde solo se teñía por el filo: el interior seguía siendo papel y la mano no recibía nada. Ahora el hover trae también el lavado y la pulsación lo llena del color entero, que es la respuesta que da un interruptor de verdad. Medido: tinta de marca sobre lavado, 4,53:1 de día y 4,95:1 de noche; blanco sobre marca llena, 4,88:1 de día, y tinta oscura sobre marca clara, 7,30:1 de noche. Se descartó hundir el lavado un 12 % más al pulsar porque dejaba el texto en 3,88:1.
Componentes
Uno de cada, funcionando. Copiar el HTML de aquí es la forma prevista de montar una página nueva: no hay ningún componente escondido en otro archivo.
.tarjeta · .tarjeta-alta · .tarjeta-seccion
Borde de 2 px, sin sombra.
Con sombra, sin borde. Solo para lo que de verdad flota.
El borde toma el color de la sección.
El borde es la elevación. Si todo lleva sombra, nada destaca: por eso tarjeta-alta se reserva para una o dos cosas por página.
.chip
chip s-acierto s-racha n-b2
Fondo lavado y texto en tinta: la única combinación de la paleta que aguanta texto pequeño. Etiqueta, no botón — si se puede pulsar, es un botón.
.aviso
Dos columnas: el icono SVG y el texto. El icono toma el color de la sección, así que el mismo aviso sirve para informar, avisar o corregir según la clase que lleve.
Es el componente del producto honesto: lo que está pendiente, apagado o sin cerrar se dice en un aviso visible, no en una nota al pie.
INFO · ALTO · RELOJ · BIEN
Aclara algo y no tiene consecuencias. Es el caso corriente.
Lo roto, lo que se contradice o lo que nadie ha revisado: el titular legal sin rellenar, dos documentos legales que no dicen la misma edad mínima.
Lo apagado o pendiente de trámite, con intención de encenderlo: los cobros, las fichas de App Store y Google Play.
Lo que ya funciona y alguien podría dar por pendiente, como borrar la cuenta entera desde el perfil.
Cuatro glifos y no uno. El color de la sección ya daba el tono, pero un aviso de advertencia y uno de buena noticia se leían idénticos, y quien no separa el rojo del verde no tenía forma de saber cuál era cuál. El dibujo lo dice sin depender del color. Se eligen por lo que dice el aviso, no por la sección donde cae.
section.aire-corto · section.aire-largo
Una sección vale por defecto clamp(2.75rem, 6vw, 5.5rem) arriba y abajo. aire-corto es para lo que no desarrolla una idea —un aviso suelto, una fila de cifras, la llamada final— y aire-largo para la primera sección de la página, que es la que fija el ritmo de lectura. Las dos cajas de aquí arriba llevan exactamente esos dos valores, no una imitación.
dl.cifras > div.cifra
Cifra en la serif, con tabular-nums para que no baile al cambiar, y el pie explicando de qué es. El tono se cambia con --tono.
.unidad
ship
/ʃɪp/
SHIP — i corta, casi «e»
barco
The ship is late. El barco llega tarde.
El error del hispanohablanteAlargada suena a «sheep», oveja. La i corta inglesa está entre la i y la e españolas.
No es una tarjeta de vocabulario: es el producto enseñado en vez de descrito. Ocho campos, el borde superior del color de la sección y la nota del error siempre en ámbar.
.telefono
El halo de debajo es turquesa porque esta sección lo es. Una sombra GRIS debajo de una captura llena de color es lo que hace que la imagen parezca pegada encima en vez de estar puesta en la página: la luz que rebota en algo turquesa llega turquesa al papel de al lado. Va en tres piezas —--sh-seccion-gris, --sh-seccion-tiro y --sh-seccion-carga— y no en una sola ficha a propósito: una ficha entera con la mezcla dentro se resolvería en :root, donde --seccion todavía es la marca, y todas las capturas del sitio tendrían halo azul. Las tres se juntan en el elemento, que es donde el color de la sección ya vale lo que tiene que valer. De noche el halo se alarga y se aclara —del 55 % al 42 % de carga— porque sobre un fondo casi negro un 55 % de color es una mancha y no hay blanco debajo contra el que recortar. Quien pide más contraste se queda sin halo: ahí el marco tiene que sostenerse solo.
ol.sendero > li.paso
Un solo camino, sin ramas: el alumno nunca tiene que decidir qué hace hoy. La línea que une los nodos la dibuja cada paso, así el último no la lleva y el camino termina limpio.
.progreso > i
El ancho va en el atributo style del relleno, que es un <i>. La franja clara de arriba va dentro del relleno, no encima de la pista: es lo que hace que parezca llena y no pintada.
table.datos
| Nivel | Qué se aprende | Unidades |
|---|---|---|
| A1 | Presentarse y sobrevivir a una primera conversación. | 393 |
| B1 | Relatar experiencias y dar opiniones. | 240 |
Tabla de verdad, no seis tarjetas: un lector de pantalla que encuentra «393» dentro de un div no sabe que son unidades. Por debajo de 45 rem cada fila se apila en ficha, y el thead se oculta con clip-path para que siga estando ahí.
.faq > details
Se abre y se cierra sin una línea de JavaScript: lo hace el navegador, que además ya lo anuncia como desplegable y se encarga del foco.
Fuente: de dónde sale esta respuesta.
La flecha es un cuadrado con dos bordes girado 45 grados, no un icono: gira sola al abrir. El .fuente del final no es decorativo — es la regla de «ninguna afirmación sin fuente» hecha componente.
.opcion · .opcion.es-correcta · .opcion.es-fallo
Acierto y fallo no se distinguen solo por el verde y el coral: la correcta lleva una marca de verificación y la fallada un aspa, dibujadas con bordes girados en ::after y ::before, sin un icono que cargar. Quien no separa esos dos colores lee el resultado igual. Las que no se eligieron y tampoco eran la buena se apagan, porque a partir de ahí sobran.
El móvil que nadie prueba
No es el móvil corriente, pero existe: es el ancho de un iPhone SE de primera generación y el de cualquiera que tenga el zoom del sistema subido. La barra superior no cabía, y una barra que desborda arrastra la página entera de lado.
El rótulo ocupa 135 px con flex: none, el respiro lateral 48 y las acciones 201: son 352 px metidos en una caja de 272. Se midió apretar el respiro, el hueco y el relleno de los botones sin esconder nada, y recupera 25 de los 56. No llega. Algo tiene que ceder, y lo que menos duele es la palabra «Talkademy» al lado del emblema: el emblema solo ya identifica la marca, y a 390 px la palabra vuelve. De ahí que el corte esté en 22 rem y no en un número redondo — 352 px es justo donde deja de caber.
El cuadrado de aquí arriba mide exactamente lo que miden el rótulo, el rótulo del panel, los enlaces de su columna, el consentimiento y las casillas de verificación: 2,75 rem de alto, que con el cuerpo base son 44 px. Menos que eso obliga a apuntar, y apuntar con el pulgar en un móvil es lo que hace que la gente pulse dos veces.
Por debajo de 48 rem la página además se aligera sin perder contenido: se recorta el aire de las secciones, el sendero estrecha sus nodos, el teléfono baja a 13 rem y lo que queda fuera de pantalla no se pinta hasta que hace falta. Esconder secciones enteras no es una opción — el contenido es el argumento—; lo que se quita es decoración.
Lo que no se rompe
Están escritas en la cabecera de base.css porque el producto las prohíbe por escrito. Quien herede esta plantilla las hereda también.
Un emoji cambia de dibujo en cada sistema, no hereda el color del texto y el lector de pantalla lo lee en voz alta con su nombre completo. Los iconos son SVG con stroke="currentColor" y aria-hidden, y están escritos a mano: los seis de contenido en herramientas/construir.py y los catorce de la columna del panel en herramientas/panel.py.
Ni vidas, ni corazones que se agoten, ni cuenta atrás que bloquee, ni «vuelve mañana o pierdes». No se dibuja aquí porque no existe en el producto, y hay una prueba automática que impide que vuelva a entrar.
Toda cifra de la plantilla está contada sobre el curso real con el mismo código que usa la plataforma, y su procedencia está anotada. Ninguna redondeada hacia arriba. En las preguntas frecuentes cada respuesta lleva su .fuente al pie.
No hay componente para eso, a propósito. Hoy no hay alumnos que contar, y fabricarlos además de mentir es ilegal: Directiva Ómnibus en la UE, 16 CFR 465 en Estados Unidos, con multa por cada reseña falsa. Lo que se enseña en su lugar es el producto.
Los estilos, en css/base.css; los textos, en los archivos de herramientas/, y luego python herramientas/construir.py, que rehace las quince páginas con la misma cabecera y el mismo pie. Un retoque directo en un .html funciona, pero la siguiente ejecución lo borra.