Había dos listados: una tabla ordenable para el catálogo y una lista
aparte para los discos. Cada arreglo había que hacerlo dos veces y el
resultado era que la portada estaba en una y el año en la otra. Ahora es
un solo componente, y lo que cambia son las columnas de contexto, que se
encienden por props: dentro de un disco, la columna «Álbum» diría diez
veces lo mismo.
Solo recibe canciones. El estilo, el disco y la portada los resuelve
contra el catálogo que ya baja en `page.data`, así que quien la usa no
tiene que armar filas a mano, que era lo que obligaba a repetir código.
Cada fila lleva su portada —con la ampliada al pasar el ratón—, y debajo
del título van los géneros y las etiquetas en vez del primer verso: la
letra está entera en la ficha, y aquí lo que sirve para elegir es de qué
está hecho el tema. Las etiquetas se cortan por número, con un «+2» al
final: recortarlas con un degradado dejaba «Autoengaño» en «Autoeng», que
no parece una elección sino algo roto.
La cola ya se puede llenar. Hasta ahora solo se podía sustituir —cada
play la vaciaba y la llenaba con su contexto—, así que no había forma de
armar una escucha cogiendo temas de discos distintos. `encolar()` es la
otra mitad, y con la cola vacía arranca, porque un botón que no hace nada
es peor que no tenerlo.
Los géneros de una canción entran en el modelo: `cancion_genero` existía
en la base y no llegaba a la web. Son cosa distinta del estilo —el estilo
es cómo se ordena la obra aquí y es uno solo; los géneros son de qué está
hecha la canción y pueden ser varios— y llevan al glosario.
De paso: las letras largas van a dos columnas de 37ch, que es el mínimo
que deja caber dos y a la vez el verso más largo del catálogo —importa
afinarlo, porque en una letra dónde acaba cada verso es parte del texto—;
la página baja de 2808 a 2023 píxeles. Contacto sale de la barra de menú
y se queda solo en el pie. Y el titular pasa a «Letrista y creador
musical».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Las mismas cinco paradas y el mismo ángulo que el botón dorado, recortadas
sobre las letras, y la banda de luz recorre el nombre al pasar el puntero igual
que recorre el botón. De 24 a 34 píxeles, que es el siguiente peldaño de la
escala y no un número a ojo.
El recorte va dentro de un `@supports` y el color plano se queda fuera: sin el
recorte, `color: transparent` dejaría la marca invisible, y eso es peor que
dejarla dorada y lisa. Es el mismo cuidado que ya tenía el botón, que guarda un
color de reserva por si el degradado no llega a pintar.
Y hace falta un segundo degradado, `--oro-escrito`. Sobre marino es
literalmente el mismo que el del botón —la peor de sus paradas da 5,4 de
contraste—, pero sobre papel no vale: medido contra el fondo claro, la parada
de brillo da **1,26**, la de superficie 2,05 y la de sombra 3,79, las tres por
debajo del mínimo de 4,5. Con el degradado del botón, el centro de las letras
desaparecía. En claro la banda va al revés: el punto más claro es el propio
`--c-accent` —5,29— y los extremos se oscurecen. Sigue pareciendo metal porque
lo que hace el efecto es la banda que cruza, no que llegue a brillar.
Es la regla que el proyecto ya tenía escrita y que yo no había aplicado: el
dorado hace dos trabajos, y de relleno tiene que ser claro mientras que escrito
tiene que ser hondo.
La prueba comprueba **cada parada** del degradado contra el fondo, en los dos
temas. A ojo, en la captura, el fallo se veía como «algo más pálido»; en
números, como ilegible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sin esto la sección solo se alcanzaba desde el taller, que es justo lo
contrario de sacarla a su propio sitio.
La prueba del menú en móvil pasa a comprobar los nombres en vez de la
cuenta: así dice cuál falta en lugar de que el número no cuadra. Y las
cuatro páginas nuevas entran en la medida de desbordamiento a 320, 375,
768 y 1440.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Con lo que pediste: portada con mapa de Iberoamérica, buscador y un aside
que empieza por Inicio, Los principios, Glosario y Letristas antes de la
lista de géneros.
El mapa es interactivo de verdad: cada país se pinta según lo que tenga
—marcado si es cuna de algún género, tenue si solo lo adoptó, apagado si
no hay nada—, y al pulsarlo la lista de abajo se parte en «nació aquí» y
«también se canta ahí». Esa diferencia es lo que un mapa cuenta mejor que
una lista.
Los contornos son de `BlankMap-Americas.svg` de Wikimedia, en dominio
público, recortados a los veinte países de Iberoamérica en América. De
640 KB a 36 KB comprimidos, y solo en esta ruta: coordenadas a un decimal
y fuera los 134 islotes que a este tamaño miden menos de un píxel. Va por
script y no pegado a mano, para poder rehacerlo.
Cada país lleva además un punto de agarre calculado —el promedio de los
vértices de su costa, no el centro de su rectángulo, que en Cuba cae en
el mar—: sin él, Puerto Rico mide cuatro píxeles y una isla alargada no
hay quien la acierte.
«Letristas» es nueva: las mismas personas que salen repartidas en nueve
fichas, aquí juntas, con su papel en cada género y sus obras.
La página del capítulo pasa a `$lib/components/Capitulo.svelte` y la usan
las dos secciones: son setecientas líneas, y copiarlas habría sido dos
sitios donde arreglar cada cosa.
Las direcciones viejas redirigen con 301 —`/taller/bolero` a
`/generos/bolero`— resolviendo contra el catálogo de capítulos, no contra
una lista escrita a mano que alguien tenga que ampliar. Un enlace
compartido no puede empezar a dar 404 porque hayamos reordenado el menú.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El color forma parte de lo que cuenta el sitio, y seguir a
`prefers-color-scheme` hacía que media parte de las visitas viera una
versión que no es la de la marca —un portátil configurado en claro no
tiene nada que decir sobre el aspecto de un catálogo de canciones—. Al
revés que en una herramienta de trabajo, aquí nadie va a pasar ocho horas
seguidas leyendo.
El claro sigue entero y a un clic, y la elección se recuerda. Lo que se
deja de hacer es decidirlo por nadie.
La prueba de contraste emulaba el esquema del sistema, así que con este
cambio habría pasado a comprobar dos veces lo mismo sin que nadie lo
notara: ahora pone `data-tema` a mano. Y la medida de claridad convierte
el `oklch()` pintándolo en un lienzo, porque desmontar la cadena a mano
daba NaN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cinco páginas legales en /legal, enlazadas desde el pie de todas las páginas:
información legal, condiciones de uso, privacidad, cookies y condiciones de
venta. Son borradores con los datos del titular marcados entre corchetes y un
aviso de que hace falta revisión de un abogado antes de vender: cubren lo que
pide la ley española, no son un documento validado.
La política de cookies y la de privacidad están escritas mirando el código, no
copiadas de una plantilla: dos cookies, las dos necesarias, y dos terceros
—Stripe y Resend— que reciben lo imprescindible.
Y eran tres, porque las tipografías venían de Google: cada visita mandaba la IP
del visitante a un tercero sin pedirlo y sin que aportara nada. Ahora se sirven
desde el propio dominio. Cargar la web ya no contacta con ningún dominio ajeno,
y hay una prueba que lo comprueba.
Aparte: el símbolo de marca registrada en el aviso de derechos, los botones de
reproducir de las tarjetas pasan de 48 a 36 píxeles —eran casi una quinta parte
de la portada, y cambiaban de proporción según la tarjeta— y el de ver el
carrito solo aparece cuando hay algo dentro.
De paso se arregla un fallo de tipos que estaba latente: `resolve()` tiene una
sobrecarga por ruta y no acepta una unión, así que la navegación dejaba de
compilar en cuanto se añadían rutas. Ahora se resuelve en `site.ts`, una a una.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El texto del botón dorado se aclaraba al pasar el puntero y quedaba en 1,67 de
contraste sobre el oro. La causa no era el color del botón: `.btn:hover` pone
`color: inherit` —para que la regla general de los enlaces no tiña de dorado los
secundarios— y le gana por especificidad. Ahora el dorado repite su color ahí.
De paso, el dorado se parte en dos tokens porque hacía dos trabajos
incompatibles: de relleno tiene que ser claro para que encima se lea el marino
—7,6 de contraste en el tema claro— y escrito tiene que ser hondo para leerse
sobre el fondo —5,3—. Con un solo valor, el tema claro se quedaba en 4,4, por
debajo del mínimo. Y encima del oro va marino, no blanco, en los dos temas.
Los botones tenían tres tamaños distintos: el dorado a 16px con relleno 8/16 y
el de al lado a 14px con 12/24. Dos botones juntos con distinta letra se ven
como un descuido. Ahora hay una sola escala, y los 15px son los del menú.
El botón de ver el carrito solo aparece cuando hay algo dentro, con la cuenta:
uno que lleva a una página vacía gasta el sitio de una llamada a la acción para
decir que no hay nada.
Tres pruebas nuevas lo fijan, incluida la del contraste con el puntero encima,
que es donde estaba el fallo y donde una captura en reposo no lo enseña.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El botón dorado se quedaba en un rectángulo oscuro con el borde de oro al pasar
el puntero. La causa: `.btn:hover` usaba el atajo `background`, que reinicia
también `background-image` y se llevaba el degradado por delante. Las reglas
base pasan a `background-color`, que es la longhand que no pisa lo que no le
toca.
De comprobarlo salió que no había ningún `a:hover` global: cualquier enlace sin
regla propia —el del feed, los del pie— no daba ninguna señal de ser pulsable.
Ahora todos responden con el dorado, que es el color de lo accionable aquí.
Tampoco la tenía el conmutador de vista de la página de música.
El botón secundario acusa el puntero con el fondo y el canto, no tiñendo el
texto de dorado: sería una señal doble y competiría con el botón dorado que
suele tener al lado.
Seis pruebas nuevas cubren la respuesta al puntero de cada tipo de control. Es
lo que faltaba: un estado de hover roto no se ve en una captura en reposo, y así
fue como se coló.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El campo `volverA` de los formularios se comprobaba en tres sitios con tres
copias del mismo filtro, y las tres dejaban pasar `/\otro-dominio.com`: el
navegador convierte la barra invertida en barra normal al resolver, así que eso
es `//otro-dominio.com` con otra ropa. En la pantalla de entrar eso es phishing
servido: enlace a nuestro propio acceso, y quien escribe su correo acaba fuera.
Ahora hay un solo `destinoSeguro`, que no compara prefijos sino que resuelve la
URL y solo acepta lo que sigue en el origen. Con pruebas de cada disfraz.
Fuera siete funciones exportadas que no llamaba nadie. Las escribí "por si
acaso" y una API que no se usa no es una API: es código que leer y mantener sin
que nada dependa de ella.
Del repaso responsive salen dos fallos reales. Los puntos del carrusel medían
8×8 píxeles, la tercera parte del mínimo para acertarles con el dedo; ahora el
punto se ve igual y el botón mide 24×24. Y el hueco reservado bajo el contenido
para la barra de reproducción estaba escrito a mano en 88 píxeles cuando la
barra mide 97 en escritorio y 145 apilada en un móvil: se comía el pie. Ahora la
barra se mide sola y publica su alto.
Todo ello fijado en `responsive.e2e.ts`, que recorre diez páginas a cuatro
anchos.
Sobre rendimiento, nada que hacer: parecía que las páginas tardaban 215 ms, pero
eran de la resolución de `localhost` por IPv6 en la medición. Por IPv4 se
renderizan en 5-9 ms, así que memorizar el catálogo habría sido optimizar un
problema inexistente.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>