Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
# Senza Paura
|
|
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Web de un letrista y compositor, con tienda. La obra se agrupa en **estilos
|
|
|
|
|
|
por estilo** —las baladas románticas como estilos principal, y junto a ellas
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
clásica, rock y pop melódico—, se escucha desde un **reproductor persistente**
|
|
|
|
|
|
que no se corta al navegar, y se puede **comprar en packs de cinco canciones**
|
|
|
|
|
|
que se entregan como descarga en MP3.
|
|
|
|
|
|
|
|
|
|
|
|
El catálogo vive en Markdown dentro del repositorio: publicar una canción es
|
|
|
|
|
|
añadir un archivo. La base de datos guarda solo lo que generan las personas que
|
|
|
|
|
|
visitan la web —cuentas, carritos, pedidos y compras—.
|
|
|
|
|
|
|
|
|
|
|
|
## Estado
|
|
|
|
|
|
|
|
|
|
|
|
La estructura, la funcionalidad y la **dirección visual** están hechas. Todo el
|
|
|
|
|
|
color, la tipografía y el ritmo salen de las variables de `:root` en
|
|
|
|
|
|
[`src/app.css`](src/app.css): cambiar la estética sigue siendo cambiar esos
|
|
|
|
|
|
tokens, no recorrer los componentes.
|
|
|
|
|
|
|
|
|
|
|
|
Los colores son los de la marca: **azul marino oscuro aterciopelado y dorado**.
|
|
|
|
|
|
El marino lleva croma de verdad (0,04 en oklch) en lugar de ser un gris
|
|
|
|
|
|
azulado, que es lo que le da el aspecto de terciopelo en vez del de pizarra;
|
|
|
|
|
|
todos los fondos comparten tono y solo cambian de claridad. El dorado es el
|
|
|
|
|
|
único acento y lo llevan las cosas accionables, nada más.
|
|
|
|
|
|
|
|
|
|
|
|
Hay **dos modos, y el claro es una inversión de verdad**: el texto pasa a ser
|
|
|
|
|
|
el propio marino y los fondos, blancos apenas teñidos de él. El dorado baja de
|
|
|
|
|
|
claridad en el modo claro —el que se lee bien sobre marino desaparece sobre
|
|
|
|
|
|
blanco—, así que ahí es un dorado más hondo, casi bronce.
|
|
|
|
|
|
|
|
|
|
|
|
Funciona en tres estados: mientras no se elija nada se sigue al sistema, sin
|
|
|
|
|
|
JavaScript, y el botón de la cabecera escribe `data-tema` en la raíz y manda
|
|
|
|
|
|
sobre él. Lo aplica un script en línea de `app.html` **antes de pintar**: si se
|
|
|
|
|
|
hiciera al hidratar, quien tenga el claro guardado vería un destello oscuro en
|
|
|
|
|
|
cada carga.
|
|
|
|
|
|
|
|
|
|
|
|
Dos familias: **Newsreader** (serif) para titulares y para las letras, que son
|
|
|
|
|
|
texto para leer despacio, y **Archivo** (sans) para interfaz, datos y
|
|
|
|
|
|
navegación.
|
|
|
|
|
|
|
|
|
|
|
|
Lo que sigue siendo provisional son los **materiales**: las portadas son SVG
|
|
|
|
|
|
generados y el clip de la cabecera es un degradado con grano, no metraje real.
|
|
|
|
|
|
|
|
|
|
|
|
**Antes de vender de verdad** hacen falta dos cosas que no son código: las
|
|
|
|
|
|
claves de Stripe (abajo se explica dónde van) y decidir el tratamiento del IVA.
|
|
|
|
|
|
Vender bienes digitales a particulares en la UE obliga a repercutir el IVA del
|
|
|
|
|
|
país del comprador; Stripe Tax lo resuelve activándolo en la cuenta y añadiendo
|
|
|
|
|
|
`automatic_tax` a la sesión de pago, en
|
|
|
|
|
|
[`src/lib/server/stripe.ts`](src/lib/server/stripe.ts). También faltan las
|
|
|
|
|
|
páginas de condiciones de venta y privacidad.
|
|
|
|
|
|
|
|
|
|
|
|
## Empezar
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm install
|
|
|
|
|
|
cp .env.example .env # basta con dejarlo como está para desarrollo
|
|
|
|
|
|
npm run db:migrate # crea local.db
|
|
|
|
|
|
npm run dev
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
La web queda en `http://localhost:5173`.
|
|
|
|
|
|
|
|
|
|
|
|
Sin claves de Stripe la tienda funciona salvo el cobro: el carrito calcula, pero
|
|
|
|
|
|
el botón de pagar avisa de que no hay pasarela. Sin clave de correo, en
|
|
|
|
|
|
desarrollo **el código de acceso se imprime por consola**, así que se puede
|
|
|
|
|
|
entrar en una cuenta sin configurar nada.
|
|
|
|
|
|
|
|
|
|
|
|
| Comando | Qué hace |
|
|
|
|
|
|
| --------------------- | --------------------------------------------- |
|
|
|
|
|
|
| `npm run dev` | Servidor de desarrollo (panel en `/admin`) |
|
|
|
|
|
|
| `npm run build` | Compila a `build/` (adapter-node) |
|
|
|
|
|
|
| `npm run preview` | Sirve la compilación de producción en `:4173` |
|
|
|
|
|
|
| `npm run check` | Comprobación de tipos |
|
|
|
|
|
|
| `npm run lint` | Prettier + ESLint |
|
|
|
|
|
|
| `npm run format` | Formatea el proyecto |
|
|
|
|
|
|
| `npm run test:unit` | Tests unitarios y de componente (Vitest) |
|
|
|
|
|
|
| `npm run test:e2e` | Compila y ejecuta los end-to-end (Playwright) |
|
|
|
|
|
|
| `npm test` | Todo lo anterior |
|
|
|
|
|
|
| `npm run db:generate` | Genera una migración a partir del esquema |
|
|
|
|
|
|
| `npm run db:migrate` | Aplica las migraciones pendientes |
|
|
|
|
|
|
| `npm run db:studio` | Abre el visor de la base de datos |
|
|
|
|
|
|
|
|
|
|
|
|
## El panel de contenido
|
|
|
|
|
|
|
|
|
|
|
|
En `/admin` hay un panel ([Sveltia CMS](https://sveltiacms.app)) para editar el
|
|
|
|
|
|
catálogo sin tocar archivos: formularios por tipo de contenido, con desplegables
|
|
|
|
|
|
para elegir el estilo o el disco de una canción en vez de escribir su slug.
|
|
|
|
|
|
|
|
|
|
|
|
Sube también el **audio** y las **portadas**: el máster va a una bandeja y
|
|
|
|
|
|
`npm run medios` lo coloca al arrancar (más abajo, en «Dónde va el audio»).
|
|
|
|
|
|
|
|
|
|
|
|
**Escribe los mismos Markdown de `src/content/`.** Esa es la razón de haberlo
|
|
|
|
|
|
elegido en lugar de mover el catálogo a una base de datos: se gana el panel sin
|
|
|
|
|
|
perder nada de lo que ya funcionaba. El esquema los sigue validando al
|
|
|
|
|
|
compilar, git sigue guardando el historial de cada letra —que en una web de un
|
|
|
|
|
|
letrista es el patrimonio— y el catálogo sigue sin hacer ni una consulta,
|
|
|
|
|
|
porque va compilado en el bundle.
|
|
|
|
|
|
|
|
|
|
|
|
### Editar en local
|
|
|
|
|
|
|
|
|
|
|
|
El flujo local de Sveltia usa la File System Access API del navegador, sin
|
|
|
|
|
|
servidor intermedio. Requiere dos cosas:
|
|
|
|
|
|
|
|
|
|
|
|
1. Un **navegador basado en Chromium** (Chrome, Edge, Brave). Firefox y Safari
|
|
|
|
|
|
todavía no traen esa API.
|
|
|
|
|
|
2. Que el proyecto sea un **repositorio git**. Ya lo es.
|
|
|
|
|
|
|
|
|
|
|
|
Con `npm run dev` en marcha, se abre `http://localhost:5173/admin`, se pulsa
|
|
|
|
|
|
«Trabajar con un repositorio local» y se elige la carpeta del proyecto. A partir
|
|
|
|
|
|
de ahí, guardar en el panel escribe el archivo, y el sitio se recarga solo.
|
|
|
|
|
|
|
|
|
|
|
|
### Editar desde cualquier sitio
|
|
|
|
|
|
|
|
|
|
|
|
Para eso hace falta que el repositorio esté en GitHub y cambiar `repo` en
|
|
|
|
|
|
[`static/admin/config.yml`](static/admin/config.yml) por el nombre real. Con eso,
|
|
|
|
|
|
el panel funciona desde el navegador de cualquier equipo y cada guardado es un
|
|
|
|
|
|
commit.
|
|
|
|
|
|
|
|
|
|
|
|
### Al añadir un campo
|
|
|
|
|
|
|
|
|
|
|
|
`config.yml` replica el esquema de
|
|
|
|
|
|
[`schema.ts`](src/lib/content/schema.ts). **Si añades un campo allí, añádelo
|
|
|
|
|
|
también aquí**: el panel guarda lo que conoce, así que un campo que no esté
|
|
|
|
|
|
declarado se pierde al editar esa entrada desde el panel.
|
|
|
|
|
|
|
|
|
|
|
|
Donde el panel puede, comprueba lo mismo que el esquema: el formato del ISWC y
|
|
|
|
|
|
el ISRC, la duración como `m:ss`, la ruta relativa del audio y el rango de los
|
|
|
|
|
|
porcentajes. Lo que no puede comprobar —que un reparto sume exactamente 100— lo
|
|
|
|
|
|
sigue cazando la compilación.
|
|
|
|
|
|
|
|
|
|
|
|
## Cómo se organiza el contenido
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/content/
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
estilos/ un archivo por estilo → un estilos de /musica
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
albumes/ discos, EPs, singles y colecciones
|
|
|
|
|
|
canciones/ un archivo por tema; el cuerpo del Markdown es la letra
|
|
|
|
|
|
taller/ los capítulos del taller de letras
|
|
|
|
|
|
paginas/ textos sueltos (bio)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
El **slug** de cualquier pieza de contenido es el nombre de su archivo sin
|
|
|
|
|
|
extensión, y es lo que aparece en la URL. `src/content/albumes/como-si-nada.md`
|
|
|
|
|
|
se publica en `/albumes/como-si-nada`.
|
|
|
|
|
|
|
|
|
|
|
|
Las relaciones se declaran por slug. **Si una referencia no existe, la
|
|
|
|
|
|
compilación falla** con un mensaje que dice el archivo y el campo exactos; no
|
|
|
|
|
|
hay contenido que desaparezca en silencio de los listados.
|
|
|
|
|
|
|
|
|
|
|
|
Cada estilo, cada álbum y cada canción tienen su propia página.
|
|
|
|
|
|
|
|
|
|
|
|
### Canciones sueltas: el álbum «Singles»
|
|
|
|
|
|
|
|
|
|
|
|
Una canción sin campo `album` es un single suelto. No se queda fuera de la web:
|
|
|
|
|
|
los singles de cada estilo se agrupan en un álbum **«Singles»** que se genera
|
|
|
|
|
|
solo, con su propia página en `/albumes/singles-<estilo>`, y que aparece el
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
último dentro de su estilos. No existe como archivo y no hay que mantenerlo.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
### Añadir un estilo (un estilos nuevo)
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
|
|
|
|
|
|
`src/content/estilos/bolero.md`
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
---
|
|
|
|
|
|
nombre: 'Bolero'
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
resumen: 'Frase corta que acompaña al título del estilos.'
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
orden: 4 # menor = más arriba en /musica
|
|
|
|
|
|
---
|
|
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Texto introductorio del estilos. Es opcional y aparece en /musica/bolero.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Solo un estilo puede llevar `principal: true`: es el que abre la portada y
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
encabeza los estilos.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
|
|
|
|
|
|
### Añadir un álbum
|
|
|
|
|
|
|
|
|
|
|
|
`src/content/albumes/nombre-del-disco.md`
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
---
|
|
|
|
|
|
titulo: 'Nombre del disco'
|
|
|
|
|
|
estilo: baladas-romanticas # slug de un archivo de estilos/
|
|
|
|
|
|
fecha: '2026-03-15'
|
|
|
|
|
|
tipo: album # album | ep | single | coleccion
|
|
|
|
|
|
portada: /images/albumes/nombre-del-disco.jpg
|
|
|
|
|
|
sello: 'Autoeditado' # opcional
|
|
|
|
|
|
resumen: 'Una frase sobre el disco.'
|
|
|
|
|
|
destacado: true # opcional
|
|
|
|
|
|
edicion: # opcional: lo que iría en la contraportada
|
|
|
|
|
|
numeroCatalogo: 'SP-004'
|
|
|
|
|
|
formatos: [digital, cd] # digital | cd | vinilo | casete
|
|
|
|
|
|
pais: España
|
|
|
|
|
|
creditos:
|
|
|
|
|
|
- rol: 'Producción'
|
|
|
|
|
|
nombre: 'Senza Paura'
|
|
|
|
|
|
- rol: 'Diseño de portada'
|
|
|
|
|
|
nombre: 'Estudio Bermejo'
|
|
|
|
|
|
enlaces: # opcional
|
|
|
|
|
|
spotify: https://open.spotify.com/...
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Notas del disco. Aparecen bajo el tracklist.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Añadir una canción
|
|
|
|
|
|
|
|
|
|
|
|
`src/content/canciones/nombre-del-tema.md`
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
---
|
|
|
|
|
|
titulo: 'Nombre del tema'
|
|
|
|
|
|
estilo: baladas-romanticas
|
|
|
|
|
|
album: nombre-del-disco # quítalo y el tema pasa a ser un single suelto
|
|
|
|
|
|
numero: 1 # obligatorio si hay álbum; se ignora en los singles
|
|
|
|
|
|
duracion: '4:12'
|
|
|
|
|
|
ano: 2026
|
|
|
|
|
|
audio: nombre-del-disco/01-nombre-del-tema.mp3 # clave privada, sin barra inicial
|
|
|
|
|
|
portada: /images/canciones/nombre-del-tema.jpg # opcional: si falta, se usa la del álbum
|
|
|
|
|
|
interpretes:
|
|
|
|
|
|
- 'Quien lo canta'
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
destacada: true # opcional: aparece en portada y en su estilos
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
versiones: # opcional: otros arreglos del mismo tema
|
|
|
|
|
|
- nombre: 'Versión orquestal'
|
|
|
|
|
|
audio: nombre-del-disco/01-nombre-del-tema-orquestal.mp3
|
|
|
|
|
|
duracion: '4:48'
|
|
|
|
|
|
nota: 'Con cuerda y arpa, para el concierto de presentación.'
|
|
|
|
|
|
registro: # opcional: la obra como propiedad registrada
|
|
|
|
|
|
iswc: 'T-034.524.680-1' # identifica la OBRA, igual en todas sus versiones
|
|
|
|
|
|
isrc: 'ES-A2B-24-00001' # identifica ESTA grabación
|
|
|
|
|
|
entidad: SGAE
|
|
|
|
|
|
numero: '9400685'
|
|
|
|
|
|
editorial: 'Ediciones Senza Paura' # distinta del sello del disco
|
|
|
|
|
|
reparto: # tiene que sumar 100; si no, el build falla
|
|
|
|
|
|
- nombre: 'Senza Paura'
|
|
|
|
|
|
rol: letra y musica # letra | musica | letra y musica
|
|
|
|
|
|
porcentaje: 50
|
|
|
|
|
|
- nombre: 'Coral Vidal'
|
|
|
|
|
|
rol: letra y musica
|
|
|
|
|
|
porcentaje: 50
|
|
|
|
|
|
inspiracion: |
|
|
|
|
|
|
De dónde sale el tema. Se escribe como bloque y se separa en párrafos por
|
|
|
|
|
|
líneas en blanco.
|
|
|
|
|
|
|
|
|
|
|
|
Este es el segundo párrafo.
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Aquí va la letra completa.
|
|
|
|
|
|
Los saltos de línea se respetan,
|
|
|
|
|
|
porque en una canción el verso es contenido.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
La ficha del tema abre con la portada y el reproductor, y reparte el contenido
|
|
|
|
|
|
en **pestañas**: «Letra» (abierta por defecto, porque es lo que se viene a
|
|
|
|
|
|
leer), «De dónde sale» y «Alternativas musicales». Las dos últimas solo
|
|
|
|
|
|
aparecen si el tema las tiene. Los créditos van al lado, siempre visibles.
|
|
|
|
|
|
|
|
|
|
|
|
Las pestañas siguen el patrón de WAI-ARIA —flechas, `Inicio` y `Fin` mueven
|
|
|
|
|
|
entre ellas— y **el contenido de todas está siempre en el HTML**, aunque su
|
|
|
|
|
|
pestaña esté cerrada: es lo que indexan los buscadores. Sin JavaScript la fila
|
|
|
|
|
|
de pestañas desaparece y se ven los tres bloques seguidos, cada uno con su
|
|
|
|
|
|
encabezado; la letra de una canción no puede depender de que el navegador
|
|
|
|
|
|
ejecute nada.
|
|
|
|
|
|
|
|
|
|
|
|
Detalles que ahorran trabajo:
|
|
|
|
|
|
|
|
|
|
|
|
- **La autoría se presupone.** Sin `letra:` ni `musica:`, ambas se atribuyen al
|
|
|
|
|
|
artista. Solo hay que escribirlas cuando firma alguien más.
|
|
|
|
|
|
- **`instrumental: true`** marca las piezas sin letra (las de clásica, por
|
|
|
|
|
|
ejemplo). El cuerpo del Markdown pasa a leerse como notas de la pieza.
|
|
|
|
|
|
- **Las versiones no se venden aparte.** Quien compra un tema se lleva todos sus
|
|
|
|
|
|
arreglos.
|
|
|
|
|
|
- **`audio` es opcional.** Sin él, la ficha y la letra se publican igual, el
|
|
|
|
|
|
botón de reproducción sale desactivado y el tema no está a la venta. Para
|
|
|
|
|
|
retirar de la venta un tema que sí tiene audio, `alaVenta: false`.
|
|
|
|
|
|
- **El extracto para las tarjetas no se escribe.** Los primeros versos salen
|
|
|
|
|
|
solos del cuerpo del Markdown, así que nunca se desincronizan de la letra. Si
|
|
|
|
|
|
esos versos concretos no son los que mejor presentan el tema, un campo
|
|
|
|
|
|
`extracto:` con una lista de líneas manda sobre el automático.
|
|
|
|
|
|
|
|
|
|
|
|
### La obra como propiedad registrada
|
|
|
|
|
|
|
|
|
|
|
|
El bloque `registro` es lo que separa un catálogo de una web. Los códigos se
|
|
|
|
|
|
validan y se normalizan al compilar —da igual si escribes `T0345246801` o
|
|
|
|
|
|
`T-034.524.680-1`—, y **un reparto que no suma 100 rompe el build**: un reparto
|
|
|
|
|
|
descuadrado solo se descubre cuando la entidad de gestión rechaza el registro, y
|
|
|
|
|
|
para entonces ya nadie recuerda de dónde salió el número.
|
|
|
|
|
|
|
|
|
|
|
|
La distinción que más se confunde: el **ISWC identifica la obra** y es el mismo
|
|
|
|
|
|
en todas sus versiones; el **ISRC identifica una grabación**, así que la versión
|
|
|
|
|
|
orquestal de un tema comparte ISWC con la original y tiene ISRC propio.
|
|
|
|
|
|
|
|
|
|
|
|
Estos datos se publican en la ficha del tema, bajo «Registro». Es deliberado: el
|
|
|
|
|
|
ISRC ya aparece en cada plataforma, el ISWC está en los repertorios públicos, y
|
|
|
|
|
|
quien quiera licenciar una obra necesita localizarla y saber con quién negociar.
|
|
|
|
|
|
Si prefieres no enseñar el reparto de autoría, no lo rellenes: lo que falta no
|
|
|
|
|
|
se pinta.
|
|
|
|
|
|
|
Carrusel en la portada y letras a dos columnas
La portada abre con tres láminas: el disco en el tocadiscos, una invitación al
taller y otra a la tienda. Un carrusel mal hecho es de las piezas más hostiles
que hay —cambia mientras lees, no se para, y para un lector de pantalla es un
cajón que aparece y desaparece—, así que este se para al pasar el puntero o al
entrar el foco, tiene botón de pausa, no gira solo con `prefers-reduced-motion`
y deja las láminas ocultas con `inert`, fuera del orden de tabulación.
Las letras que no están en español se enfrentan con su traducción en dos
columnas, verso a verso. Cada columna declara su `lang`, que es lo que impide
que un lector de pantalla lea el inglés con voz española, y la traducción se
pinta con `pre-line`: es verso, y perder los cortes la deja inservible para
seguir la original. El nombre del idioma sale de `Intl.DisplayNames`.
Sobre el reproductor: ya sobrevivía a la navegación, porque vive en el layout
raíz fuera de `main`. Lo que faltaba era comprobarlo de verdad; el test decía
que la barra seguía visible, no que siguiera sonando. Ahora mira que la posición
avance.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Letras en otro idioma
|
|
|
|
|
|
|
|
|
|
|
|
Una canción declara su `idioma` (ISO 639, español por defecto). Cuando no está
|
|
|
|
|
|
en español y trae `traduccion`, la pestaña de la letra se parte en **dos
|
|
|
|
|
|
columnas**: la original a la izquierda y el castellano a la derecha, verso a
|
|
|
|
|
|
verso. Con una sola de las dos cosas se queda en una columna, porque una columna
|
|
|
|
|
|
vacía sería peor que ninguna.
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
idioma: en
|
|
|
|
|
|
traduccion: |
|
|
|
|
|
|
Dejé las luces encendidas al salir,
|
|
|
|
|
|
no porque fuera a volver
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Dos detalles que importan. Cada columna lleva su `lang`, que es lo que impide
|
|
|
|
|
|
que un lector de pantalla lea el inglés con voz española. Y la traducción se
|
|
|
|
|
|
guarda tal cual y se pinta con `white-space: pre-line`: es verso, no prosa, y
|
|
|
|
|
|
una traducción que pierde los cortes deja de servir para seguir la original.
|
|
|
|
|
|
|
|
|
|
|
|
El nombre del idioma se saca de `Intl.DisplayNames`, no de una tabla que
|
|
|
|
|
|
mantener.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Etiquetas
|
|
|
|
|
|
|
|
|
|
|
|
Cada tema y cada entrada del blog pueden llevar **etiquetas** libres: de qué
|
|
|
|
|
|
habla, cómo suena, con quién se canta. Existen porque el estilo y el disco no
|
|
|
|
|
|
cruzan el catálogo por donde a veces hace falta: un desamor puede ser una balada
|
|
|
|
|
|
o una ranchera, y el año no ayuda a encontrarlo.
|
|
|
|
|
|
|
|
|
|
|
|
Se escriben tal cual en el frontmatter y **agrupan por slug**, así que «Desamor»
|
|
|
|
|
|
y «desamor» son la misma; el nombre que se lee es el primero que apareció. En
|
|
|
|
|
|
`/etiquetas` están todas, de más usada a menos, y cada una tiene su página.
|
|
|
|
|
|
|
|
|
|
|
|
También son lo que hace útil el buscador de `/musica`: quien recuerda «el dueto»
|
|
|
|
|
|
o «desamor» llega igual que quien recuerda el título.
|
|
|
|
|
|
|
|
|
|
|
|
## Blog
|
|
|
|
|
|
|
|
|
|
|
|
Apuntes fechados, en `src/content/blog/`. Es lo único del sitio que se fecha: el
|
|
|
|
|
|
catálogo no caduca, pero un apunte sobre cómo salió un tema pertenece al día en
|
|
|
|
|
|
que se escribió.
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
---
|
|
|
|
|
|
titulo: 'Lo que no cabe en una canción'
|
|
|
|
|
|
fecha: 2026-02-14
|
|
|
|
|
|
resumen: 'Una frase; es lo que se lee en el listado y en los buscadores.'
|
|
|
|
|
|
etiquetas:
|
|
|
|
|
|
- 'Oficio'
|
|
|
|
|
|
canciones:
|
|
|
|
|
|
- como-si-nada
|
|
|
|
|
|
borrador: false
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
El cuerpo del Markdown es el artículo.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Dos cosas que conviene saber. El campo `canciones` enlaza temas del catálogo, y
|
|
|
|
|
|
la entrada los enseña al final con su reproductor, así que se puede escuchar lo
|
|
|
|
|
|
que se está leyendo. Y `borrador: true` deja la entrada fuera **en el catálogo**,
|
|
|
|
|
|
no en cada página: no hay forma de escribir un listado nuevo y olvidarse de
|
|
|
|
|
|
filtrarlos.
|
|
|
|
|
|
|
|
|
|
|
|
## Taller de letras
|
|
|
|
|
|
|
|
|
|
|
|
Un apartado propio (`/taller`) con el oficio puesto por escrito, en
|
|
|
|
|
|
`src/content/taller/`. Son dos bloques y se distinguen por el campo `tipo`:
|
|
|
|
|
|
|
|
|
|
|
|
- **`principio`** — el curso: qué hace que una letra funcione, la imagen
|
|
|
|
|
|
concreta, métrica y acento, la rima, la estructura y el estribillo. Se leen
|
|
|
|
|
|
en orden y cada capítulo enlaza con el siguiente.
|
|
|
|
|
|
- **`ficha`** — una por estilo hispanoamericano (bolero, son cubano, ranchera,
|
|
|
|
|
|
tango, vals criollo, bachata, vallenato y cumbia). No son historia de la
|
|
|
|
|
|
música: son lo que hay que tener delante para escribir en ese estilo, con
|
|
|
|
|
|
compás, tempo, métrica, estructura, rima, de qué se habla y qué rasgos de
|
|
|
|
|
|
escritura lo distinguen.
|
|
|
|
|
|
|
|
|
|
|
|
Añadir un estilo es un archivo:
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
---
|
|
|
|
|
|
titulo: 'Milonga'
|
|
|
|
|
|
tipo: ficha
|
|
|
|
|
|
orden: 9
|
|
|
|
|
|
resumen: 'Una frase sobre qué aporta el estilo a quien escribe.'
|
|
|
|
|
|
ficha:
|
|
|
|
|
|
origen: 'Río de la Plata, siglo XIX'
|
|
|
|
|
|
compas: '2/4'
|
|
|
|
|
|
tempo: 'Vivo'
|
|
|
|
|
|
metrica: 'Octosílabo'
|
|
|
|
|
|
estructura: 'Estrofas encadenadas'
|
|
|
|
|
|
rima: 'Consonante en los pares'
|
|
|
|
|
|
temas:
|
|
|
|
|
|
- 'El desafío'
|
|
|
|
|
|
rasgos:
|
|
|
|
|
|
- 'Se canta casi hablando'
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
El cuerpo del Markdown: cómo se escribe una, el error más común y un ejercicio.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Todas** las páginas del taller llevan el índice en una barra lateral a la
|
|
|
|
|
|
izquierda —la portada y cada capítulo—, con el capítulo abierto marcado: en un
|
|
|
|
|
|
curso, saber dónde estás y qué queda es parte de poder seguirlo. Vive en
|
|
|
|
|
|
[`+layout.svelte`](src/routes/taller/+layout.svelte) de la sección, no en cada
|
|
|
|
|
|
página, así que no se vuelve a pintar al cambiar de capítulo —no parpadea ni
|
|
|
|
|
|
pierde el desplazamiento— y no hay forma de añadir una página y olvidarse de
|
|
|
|
|
|
ponerlo. En pantallas estrechas no se oculta: baja debajo del texto.
|
|
|
|
|
|
|
|
|
|
|
|
Los rótulos de sus dos bloques **no son encabezados**. En la portada repetirían
|
|
|
|
|
|
palabra por palabra los títulos de la página y el esquema diría dos veces «Los
|
|
|
|
|
|
principios»; nombran su lista con `aria-labelledby`, que es lo que hacía falta.
|
|
|
|
|
|
|
|
|
|
|
|
Los capítulos anterior y siguiente se calculan **dentro de su bloque**: pasar de
|
|
|
|
|
|
los principios a las fichas es cambiar de cosa, no seguir leyendo.
|
|
|
|
|
|
|
|
|
|
|
|
## Dónde va el audio
|
|
|
|
|
|
|
|
|
|
|
|
Como el catálogo se vende, **el archivo completo no es público**:
|
|
|
|
|
|
|
|
|
|
|
|
Todo va agrupado **por disco**, con el mismo nombre de carpeta en los tres
|
|
|
|
|
|
árboles:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
media/audio/<album>/NN-<cancion>.mp3 archivo completo — privado, nunca se sirve
|
|
|
|
|
|
static/muestras/<album>/NN-<cancion>.mp3 fragmento — público, es lo que suena en la web
|
|
|
|
|
|
static/images/<album>/portada.jpg portada del disco
|
|
|
|
|
|
static/images/<album>/<cancion>.jpg portada propia de un tema
|
|
|
|
|
|
static/images/sin-portada.svg reserva cuando no hay ninguna
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Los temas sueltos usan `singles/` como carpeta, y ahí vive también la portada
|
|
|
|
|
|
del álbum «Singles» que se genera solo.
|
|
|
|
|
|
|
|
|
|
|
|
La `<clave>` del audio es lo que declara el campo `audio` de la canción, sin
|
|
|
|
|
|
barra inicial: `como-si-nada/01-como-si-nada.mp3`. De ahí salen la ruta privada
|
|
|
|
|
|
y la de la muestra, así que no hay que escribirlas por duplicado.
|
|
|
|
|
|
|
|
|
|
|
|
El directorio privado se configura con `MEDIA_DIR`. Los detalles de formato, la
|
|
|
|
|
|
convención de nombres y cómo sustituir los archivos de marcador de posición
|
|
|
|
|
|
están en [`media/LEEME.md`](media/LEEME.md).
|
|
|
|
|
|
|
|
|
|
|
|
### Subir un tema sin tocar el disco
|
|
|
|
|
|
|
|
|
|
|
|
El campo **Archivo de audio** del panel sube el máster a `media/audio/subidas/`.
|
|
|
|
|
|
Lo que el navegador no puede hacer —recortar la muestra y medir la duración— lo
|
|
|
|
|
|
hace [`scripts/medios.mjs`](scripts/medios.mjs), que corre solo antes de
|
|
|
|
|
|
`npm run dev` y de `npm run build`, y también a mano con `npm run medios`:
|
|
|
|
|
|
coloca el archivo en la carpeta de su disco con el nombre de la convención,
|
|
|
|
|
|
reescribe la clave en el Markdown, genera el fragmento y rellena `duracion` si
|
|
|
|
|
|
estaba vacía.
|
|
|
|
|
|
|
|
|
|
|
|
Necesita **ffmpeg** en el equipo de quien edita. Si falta, avisa de lo que no ha
|
|
|
|
|
|
podido generar y sigue; no rompe el arranque ni el build. El servidor de
|
|
|
|
|
|
despliegue no lo necesita: las muestras están en el repositorio.
|
|
|
|
|
|
|
|
|
|
|
|
Un archivo que se quede en `subidas/` sin ficha que lo reclame se avisa en cada
|
|
|
|
|
|
arranque, para que no se pierda ahí.
|
|
|
|
|
|
|
|
|
|
|
|
Los `.wav` y `.svg` del repositorio son **provisionales**, para poder probar el
|
|
|
|
|
|
reproductor y las descargas: seis segundos como archivo completo y dos como
|
|
|
|
|
|
muestra, con una nota distinta por pista.
|
|
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## La portada
|
|
|
|
|
|
|
Carrusel en la portada y letras a dos columnas
La portada abre con tres láminas: el disco en el tocadiscos, una invitación al
taller y otra a la tienda. Un carrusel mal hecho es de las piezas más hostiles
que hay —cambia mientras lees, no se para, y para un lector de pantalla es un
cajón que aparece y desaparece—, así que este se para al pasar el puntero o al
entrar el foco, tiene botón de pausa, no gira solo con `prefers-reduced-motion`
y deja las láminas ocultas con `inert`, fuera del orden de tabulación.
Las letras que no están en español se enfrentan con su traducción en dos
columnas, verso a verso. Cada columna declara su `lang`, que es lo que impide
que un lector de pantalla lea el inglés con voz española, y la traducción se
pinta con `pre-line`: es verso, y perder los cortes la deja inservible para
seguir la original. El nombre del idioma sale de `Intl.DisplayNames`.
Sobre el reproductor: ya sobrevivía a la navegación, porque vive en el layout
raíz fuera de `main`. Lo que faltaba era comprobarlo de verdad; el test decía
que la barra seguía visible, no que siguiera sonando. Ahora mira que la posición
avance.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Un carrusel de tres láminas: el disco en el tocadiscos, una invitación al taller
|
|
|
|
|
|
y otra a la tienda. Un carrusel mal hecho es de las piezas más hostiles que hay,
|
|
|
|
|
|
así que este:
|
|
|
|
|
|
|
|
|
|
|
|
- **se para solo** al pasar el puntero o al entrar el foco, y sigue al salir;
|
|
|
|
|
|
- tiene un **botón de pausa** de verdad, cuyo estado manda sobre lo demás;
|
|
|
|
|
|
- **no gira solo** con `prefers-reduced-motion`;
|
|
|
|
|
|
- deja las láminas ocultas **fuera del orden de tabulación** (`inert`), así que
|
|
|
|
|
|
el teclado no cae en botones invisibles;
|
|
|
|
|
|
- se mueve con las flechas del teclado desde sus controles.
|
|
|
|
|
|
|
|
|
|
|
|
Las tres se pintan a la vez y se cruzan con opacidad, no se desmontan: así el
|
|
|
|
|
|
fondo de la primera no se recarga al volver a ella. Las dos láminas de llamada
|
|
|
|
|
|
reutilizan el mismo fondo, de modo que no añaden peso.
|
|
|
|
|
|
|
|
|
|
|
|
Debajo del carrusel, el catálogo en tres pasos:
|
|
|
|
|
|
|
|
|
|
|
|
el **estilo principal a fila completa**, sus discos debajo, y el resto de
|
|
|
|
|
|
estilos en una rejilla donde cada tarjeta dice cuánto hay —discos y temas— y de
|
|
|
|
|
|
qué se habla, con las etiquetas más usadas de sus canciones.
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
|
|
|
|
|
|
El estilo principal no compite en la rejilla a propósito: es el grueso de la
|
|
|
|
|
|
obra, y ponerlo del mismo tamaño que los demás diría lo contrario de lo que es.
|
|
|
|
|
|
Cuál es lo marca `principal: true` en su archivo.
|
|
|
|
|
|
|
|
|
|
|
|
Las etiquetas de un estilo **no se declaran**: salen de sus temas
|
|
|
|
|
|
(`etiquetasDeEstilo`). Así no hay dos sitios que mantener ni forma de que la
|
|
|
|
|
|
etiqueta de un estilo contradiga a las de sus canciones.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## La página de música
|
|
|
|
|
|
|
|
|
|
|
|
Tres formas de recorrer lo mismo, porque no todo el mundo llega buscando igual:
|
|
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
- **Los estilos, siempre a la izquierda.** Están en el layout de la sección, así
|
|
|
|
|
|
que los llevan igual la portada de música y la página de cada estilo, con el
|
|
|
|
|
|
abierto marcado. Son la navegación de la sección; en pantalla estrecha suben
|
|
|
|
|
|
arriba en horizontal, no se esconden.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
- **Un buscador** que mira el título, el estilo, el disco, el año, las etiquetas
|
|
|
|
|
|
y los intérpretes, sin tildes y sin mayúsculas. Con una búsqueda en marcha los
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
estilos no dicen nada, así que se enseñan las coincidencias sueltas.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
- **Dos vistas, «Tarjetas» y «Tabla».** Las tarjetas sirven para descubrir; la
|
|
|
|
|
|
tabla, ordenable por cualquier columna, para comparar: cuál dura menos, qué
|
|
|
|
|
|
salió en 2022. Ordenar es cosa del cliente porque el catálogo va compilado en
|
|
|
|
|
|
el bundle: no hay consulta que hacer ni espera que enseñar.
|
|
|
|
|
|
|
|
|
|
|
|
En la página de **un estilo** se listan **todos** sus temas, no solo los
|
|
|
|
|
|
destacados. En la de música, donde conviven todos los estilos, solo caben los
|
|
|
|
|
|
destacados; ahí no, porque si no un tema recién añadido no se ve por ninguna
|
|
|
|
|
|
parte.
|
|
|
|
|
|
|
|
|
|
|
|
## Las tarjetas de canción
|
|
|
|
|
|
|
|
|
|
|
|
La tarjeta **identifica** un tema, no lo cuenta: portada, título, disco, año y
|
|
|
|
|
|
estilo. Nada más. La letra se lee en la ficha, que es donde tiene sitio para
|
|
|
|
|
|
respirar; en una rejilla, un fragmento de estrofa por tarjeta convierte el
|
|
|
|
|
|
listado en un muro de texto y estorba justo lo que el listado sirve para hacer,
|
|
|
|
|
|
que es recorrer y elegir.
|
|
|
|
|
|
|
|
|
|
|
|
Sigue existiendo el **extracto** —los primeros versos, que saca del cuerpo del
|
|
|
|
|
|
Markdown el preprocesador [`extracto.ts`](src/lib/content/extracto.ts) antes de
|
|
|
|
|
|
que mdsvex lo compile, cortando en la primera línea en blanco—. Ya no se usa en
|
|
|
|
|
|
las tarjetas, pero sí en la lista numerada de un disco, donde cada tema es una
|
|
|
|
|
|
fila y una línea de letra distingue mejor que unos créditos idénticos en todo el
|
|
|
|
|
|
álbum.
|
|
|
|
|
|
|
|
|
|
|
|
La animación es doble: aparición escalonada al entrar en pantalla, y respuesta
|
|
|
|
|
|
al puntero (la tarjeta se eleva, la portada se acerca y el botón de reproducir
|
|
|
|
|
|
sube desde la esquina). Tres cosas que conviene no romper al rediseñar:
|
|
|
|
|
|
|
|
|
|
|
|
- **Sin JavaScript se ve todo.** El estado oculto lo pone la acción
|
|
|
|
|
|
[`revelar`](src/lib/acciones/revelar.ts), nunca el CSS: si no hay JS no hay
|
|
|
|
|
|
atributo, y la tarjeta se pinta directamente.
|
|
|
|
|
|
- **`prefers-reduced-motion: reduce` desactiva el movimiento**, y entonces el
|
|
|
|
|
|
botón de reproducir se queda siempre visible.
|
|
|
|
|
|
- **En pantallas táctiles** el botón también se queda visible: sin hover que lo
|
|
|
|
|
|
revele, un botón invisible pero pulsable sería peor que ninguno.
|
|
|
|
|
|
|
|
|
|
|
|
Dónde se usa cada cosa: **tarjetas** donde se descubre música (portada, temas
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
destacados de cada estilos, colecciones de singles) y **lista numerada** en la
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
página de un disco, donde el orden de las pistas es la información.
|
|
|
|
|
|
|
Fondo real en la portada, cabecera translúcida y menos líneas
La portada estrena imagen de fondo propia, detrás del tocadiscos y del texto.
Llega en PNG de 2,1 MB y se sirve en tres anchos WebP: 10 kB en un móvil, 57 kB
en un panel grande. El velo que va encima se afloja hasta lo justo para que el
texto se lea, porque taparlo entero sería no haberlo puesto. El tema claro
todavía no tiene su versión, así que ahí sigue el clip de reserva.
La cabecera deja ver lo que pasa por debajo. La transparencia va con desenfoque
y no sola: sobre una imagen a sangre, un fondo solo translúcido dejaría el
contraste del menú a merced de lo que quedara detrás.
Y fuera las líneas entre secciones: separa el aire. Se quedan las que dibujan
una pieza —el pie, el reproductor, las filas de un listado, la tabla—.
Aparte, scripts/construir.mjs comprobaba `build/server/index.js`, un archivo
que adapter-node no genera: el servidor sale troceado en chunks. Daba por
fallido un build correcto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## El fondo de la portada
|
|
|
|
|
|
|
|
|
|
|
|
Una imagen a sangre detrás del tocadiscos y del texto, en
|
|
|
|
|
|
`static/background/`. Se sirve en **tres anchos WebP** y el navegador coge el
|
|
|
|
|
|
que le toca: 10 kB en un móvil, 57 kB en un panel grande. El PNG original de
|
|
|
|
|
|
2,1 MB se queda en el repositorio como fuente, pero no lo pide nadie.
|
|
|
|
|
|
|
|
|
|
|
|
El velo que va encima existe solo para que el texto de la izquierda se lea, y va
|
|
|
|
|
|
lo justo: el fondo es material de marca y taparlo entero sería no haberlo
|
|
|
|
|
|
puesto.
|
|
|
|
|
|
|
|
|
|
|
|
**El tema claro todavía no tiene imagen** —esta es marino oscuro, y sobre blanco
|
|
|
|
|
|
no valdría—, así que ahí sigue el clip de reserva. Mientras sea así, quien tenga
|
|
|
|
|
|
el claro se descarga la imagen oscura una vez: el servidor pinta el tema de
|
|
|
|
|
|
marca, que es el que no puede adivinar. Se acaba en cuanto exista la versión
|
|
|
|
|
|
clara.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Vídeo
|
|
|
|
|
|
|
|
|
|
|
|
Dos sitios llevan vídeo, y ninguno carga nada de terceros por su cuenta:
|
|
|
|
|
|
|
|
|
|
|
|
- **La cabecera de la portada** ([`Heroe.svelte`](src/lib/components/Heroe.svelte))
|
|
|
|
|
|
pone un disco en un [tocadiscos](src/lib/components/Tocadiscos.svelte) sobre
|
|
|
|
|
|
un clip propio, mudo y en bucle desde `static/video/` —hay uno por tema, sobre
|
|
|
|
|
|
marino y sobre blanco—. Es un archivo nuestro y
|
|
|
|
|
|
no un incrustado porque en la primera pantalla un iframe de YouTube pesa más
|
|
|
|
|
|
que todo lo demás junto. No se arranca con el atributo `autoplay` sino desde
|
|
|
|
|
|
código, para poder respetar `prefers-reduced-motion`. El disco que se pone se
|
|
|
|
|
|
elige en `heroe` (`src/lib/site.ts`).
|
|
|
|
|
|
|
|
|
|
|
|
El tocadiscos no es un adorno: está atado al reproductor de verdad, gira
|
|
|
|
|
|
mientras suena algo de ese disco y se para al pausar, y el propio disco es un
|
|
|
|
|
|
botón enfocable con teclado. El giro es una animación CSS que se pausa con
|
|
|
|
|
|
`animation-play-state`, no un bucle en JavaScript.
|
|
|
|
|
|
|
|
|
|
|
|
- **Los vídeos de una canción** ([`VideoYoutube.svelte`](src/lib/components/VideoYoutube.svelte))
|
|
|
|
|
|
se declaran en su frontmatter y aparecen en una pestaña más de la ficha.
|
|
|
|
|
|
Hasta que alguien pulsa, la página **no pide nada a YouTube**: se ve la
|
|
|
|
|
|
portada propia del tema con un botón encima, y solo al pulsar se inserta el
|
|
|
|
|
|
reproductor, contra `youtube-nocookie.com`. Evita también la miniatura
|
|
|
|
|
|
oficial, que se serviría desde los servidores de Google. Hay un test end-to-end
|
|
|
|
|
|
que comprueba que no sale ninguna petición antes de pulsar.
|
|
|
|
|
|
|
|
|
|
|
|
En el frontmatter se acepta el enlace tal cual se copia del navegador o el id
|
|
|
|
|
|
suelto; el esquema extrae el id y lo valida:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
videos:
|
|
|
|
|
|
- youtube: https://www.youtube.com/watch?v=XXXXXXXXXXX
|
|
|
|
|
|
titulo: 'Videoclip oficial'
|
|
|
|
|
|
- youtube: XXXXXXXXXXX
|
|
|
|
|
|
titulo: 'En directo en el Teatro Circo'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## La cabecera y las anclas
|
|
|
|
|
|
|
Fondo real en la portada, cabecera translúcida y menos líneas
La portada estrena imagen de fondo propia, detrás del tocadiscos y del texto.
Llega en PNG de 2,1 MB y se sirve en tres anchos WebP: 10 kB en un móvil, 57 kB
en un panel grande. El velo que va encima se afloja hasta lo justo para que el
texto se lea, porque taparlo entero sería no haberlo puesto. El tema claro
todavía no tiene su versión, así que ahí sigue el clip de reserva.
La cabecera deja ver lo que pasa por debajo. La transparencia va con desenfoque
y no sola: sobre una imagen a sangre, un fondo solo translúcido dejaría el
contraste del menú a merced de lo que quedara detrás.
Y fuera las líneas entre secciones: separa el aire. Se quedan las que dibujan
una pieza —el pie, el reproductor, las filas de un listado, la tabla—.
Aparte, scripts/construir.mjs comprobaba `build/server/index.js`, un archivo
que adapter-node no genera: el servidor sale troceado en chunks. Daba por
fallido un build correcto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
La cabecera **acompaña al desplazamiento** (`position: sticky`) y deja ver lo
|
|
|
|
|
|
que pasa por debajo. La transparencia va **con desenfoque, no sola**: sobre la
|
|
|
|
|
|
portada, un fondo solo translúcido dejaría el contraste del menú a merced de la
|
|
|
|
|
|
zona que quedara detrás; el desenfoque la promedia y la vuelve previsible. Donde
|
|
|
|
|
|
no hay `backdrop-filter` la cabecera se vuelve casi opaca, porque es preferible
|
|
|
|
|
|
perder el efecto a perder la legibilidad. El botón de claro/oscuro es el
|
|
|
|
|
|
**último elemento del menú**, después del carrito y de la cuenta.
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
|
|
|
|
|
|
Una cabecera fija tapa aquello a lo que se salta. Lo evita
|
|
|
|
|
|
`--altura-cabecera` en [`app.css`](src/app.css), que alimenta el
|
|
|
|
|
|
`scroll-padding-top` de la raíz y el `top` de las barras laterales que
|
|
|
|
|
|
también se quedan fijas —el índice y la ficha del taller—. Si cambia el
|
|
|
|
|
|
espaciado de la cabecera, ese número la sigue; nadie la mide en JavaScript.
|
|
|
|
|
|
|
|
|
|
|
|
Las **pestañas se pueden enlazar**: `#panel-videos` en la ficha de una canción
|
|
|
|
|
|
abre esa pestaña y baja hasta ella. Sin esto el enlace no llevaría a ninguna
|
|
|
|
|
|
parte, porque el panel cerrado está oculto. Lo hace
|
|
|
|
|
|
[`Pestanas.svelte`](src/lib/components/Pestanas.svelte) leyendo el hash de la
|
|
|
|
|
|
URL; un ancla que no corresponde a ninguna pestaña se ignora y se queda la de
|
|
|
|
|
|
siempre.
|
|
|
|
|
|
|
Fondo real en la portada, cabecera translúcida y menos líneas
La portada estrena imagen de fondo propia, detrás del tocadiscos y del texto.
Llega en PNG de 2,1 MB y se sirve en tres anchos WebP: 10 kB en un móvil, 57 kB
en un panel grande. El velo que va encima se afloja hasta lo justo para que el
texto se lea, porque taparlo entero sería no haberlo puesto. El tema claro
todavía no tiene su versión, así que ahí sigue el clip de reserva.
La cabecera deja ver lo que pasa por debajo. La transparencia va con desenfoque
y no sola: sobre una imagen a sangre, un fondo solo translúcido dejaría el
contraste del menú a merced de lo que quedara detrás.
Y fuera las líneas entre secciones: separa el aire. Se quedan las que dibujan
una pieza —el pie, el reproductor, las filas de un listado, la tabla—.
Aparte, scripts/construir.mjs comprobaba `build/server/index.js`, un archivo
que adapter-node no genera: el servidor sale troceado en chunks. Daba por
fallido un build correcto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Entre secciones **no hay líneas**: la separación la hace el aire. Los bordes
|
|
|
|
|
|
que quedan son de pieza, no de sección —el pie, la barra del reproductor, las
|
|
|
|
|
|
filas de un listado, la tabla—, y ahí sí dibujan algo.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Tarjetas
|
|
|
|
|
|
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Las tres clases —estilo, álbum y canción— comparten `.tarjeta` en `app.css` y
|
|
|
|
|
|
se mueven igual. Al acercarse: la tarjeta sube, se le enciende el borde en
|
|
|
|
|
|
dorado, la portada recupera el color que tenía apagado en reposo y un reflejo
|
|
|
|
|
|
**nace donde está el puntero** —lo escribe la acción
|
|
|
|
|
|
[`brillo`](src/lib/acciones/brillo.ts) en `--mx` y `--my`—. El subrayado del
|
|
|
|
|
|
título crece desde la izquierda y al pulsar la tarjeta cede.
|
|
|
|
|
|
|
|
|
|
|
|
Nada usa la curva `ease`, que es la de por defecto y hace que todo se parezca:
|
|
|
|
|
|
se sale rápido y se frena largo (`--sal`), que es como se mueve algo que pesa.
|
|
|
|
|
|
|
|
|
|
|
|
**Cuidado con los filtros de varias funciones.** El minificador de CSS los junta
|
|
|
|
|
|
sin el espacio que las separa —deja `saturate(.85)brightness(.92)`, que no es
|
|
|
|
|
|
válido— y el navegador descarta la declaración entera. Por eso esos valores van
|
|
|
|
|
|
dentro de una variable, donde se copian tal cual. Y no se declara nunca
|
|
|
|
|
|
`-webkit-backdrop-filter` a mano: si está, el minificador borra la versión sin
|
|
|
|
|
|
prefijo y deja la prefijada, que no llega a aplicarse. El prefijo lo pone él
|
|
|
|
|
|
solo.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
Hay tres clases —estilo, álbum y canción— y se comportan igual porque comparten
|
|
|
|
|
|
`.tarjeta` en `app.css`: aparecen escalonadas al entrar en pantalla, se elevan
|
|
|
|
|
|
al acercar el puntero y descubren el botón de reproducir sobre la imagen. Tres
|
|
|
|
|
|
cosas que conviene no romper: sin JavaScript se ven todas (el estado oculto lo
|
|
|
|
|
|
pone la acción [`revelar`](src/lib/acciones/revelar.ts), nunca el CSS),
|
|
|
|
|
|
`prefers-reduced-motion` desactiva el movimiento, y en pantallas táctiles el
|
|
|
|
|
|
botón de reproducir se queda siempre visible, porque sin puntero no hay nada
|
|
|
|
|
|
que lo revele.
|
|
|
|
|
|
|
|
|
|
|
|
## El reproductor
|
|
|
|
|
|
|
|
|
|
|
|
Vive en el layout raíz, fuera de la parte que cambia al navegar, así que la
|
|
|
|
|
|
música sigue sonando mientras se recorre la web. Su estado es una única
|
|
|
|
|
|
instancia de módulo ([`src/lib/player/player.svelte.ts`](src/lib/player/player.svelte.ts)),
|
|
|
|
|
|
no un contexto de Svelte, precisamente por eso.
|
|
|
|
|
|
|
|
|
|
|
|
**Lo que suena siempre es el fragmento de muestra**, y la barra lo dice. El
|
|
|
|
|
|
archivo completo solo se obtiene comprando.
|
|
|
|
|
|
|
|
|
|
|
|
Qué hace: cola con orden propio, aleatorio (respetando el tema en curso),
|
|
|
|
|
|
repetición de cola o de tema, búsqueda dentro del tema, volumen recordado entre
|
|
|
|
|
|
visitas, panel de cola e integración con la Media Session del sistema operativo.
|
|
|
|
|
|
|
|
|
|
|
|
Atajos, mientras no se esté escribiendo en un campo:
|
|
|
|
|
|
|
|
|
|
|
|
| Tecla | Acción |
|
|
|
|
|
|
| ------------------- | ------------------------- |
|
|
|
|
|
|
| `Espacio` | Reproducir / pausar |
|
|
|
|
|
|
| `←` / `→` | Retroceder / avanzar 5 s |
|
|
|
|
|
|
| `Mayús` + `←` / `→` | Tema anterior / siguiente |
|
|
|
|
|
|
| `M` | Silenciar |
|
|
|
|
|
|
|
|
|
|
|
|
Reproducir un tema desde cualquier listado carga **todo el listado** en la cola.
|
|
|
|
|
|
En la ficha de un tema, las alternativas musicales forman su propia cola, para
|
|
|
|
|
|
poder comparar arreglos seguidos.
|
|
|
|
|
|
|
|
|
|
|
|
## La tienda
|
|
|
|
|
|
|
|
|
|
|
|
### Precio
|
|
|
|
|
|
|
|
|
|
|
|
Las canciones se venden en **packs cerrados de cinco, a 1,50 € el pack**. El
|
|
|
|
|
|
carrito no deja pagar hasta completar un múltiplo de cinco, dice cuántas faltan
|
|
|
|
|
|
y propone temas para cerrar el pack. Toda esa aritmética está en
|
|
|
|
|
|
[`src/lib/tienda.ts`](src/lib/tienda.ts), en funciones puras que usan por igual
|
|
|
|
|
|
la página y el servidor: no hay dos versiones del cálculo.
|
|
|
|
|
|
|
|
|
|
|
|
Para cambiar el precio o el tamaño del pack, esas dos constantes.
|
|
|
|
|
|
|
|
|
|
|
|
### Cuentas
|
|
|
|
|
|
|
|
|
|
|
|
Alta y entrada con un **código de seis dígitos enviado por correo**. No hay
|
|
|
|
|
|
contraseñas que guardar ni recuperar, y la verificación del correo va incluida
|
|
|
|
|
|
en el propio registro. Si es la primera vez, la cuenta se crea sola.
|
|
|
|
|
|
|
|
|
|
|
|
Ni el código ni el token de sesión se guardan en claro: en la base de datos hay
|
|
|
|
|
|
hashes. El código caduca a los quince minutos, solo sirve una vez, y pedir uno
|
|
|
|
|
|
nuevo invalida el anterior. Los envíos están limitados por dirección IP.
|
|
|
|
|
|
|
|
|
|
|
|
`/entrar?paso=codigo&email=…` vuelve al paso del código sin gastar otro, para
|
|
|
|
|
|
quien recarga la página o vuelve desde el correo.
|
|
|
|
|
|
|
|
|
|
|
|
### Carrito
|
|
|
|
|
|
|
|
|
|
|
|
Se puede llenar **sin haber entrado**: mientras no hay cuenta se identifica con
|
|
|
|
|
|
una cookie, y al iniciar sesión se fusiona con el carrito de la cuenta. Nadie
|
|
|
|
|
|
pierde lo que estaba eligiendo por tener que registrarse a mitad.
|
|
|
|
|
|
|
|
|
|
|
|
Los botones son formularios de verdad, así que **funcionan sin JavaScript**: la
|
|
|
|
|
|
acción redirige de vuelta a la página de origen. Con JavaScript, `enhance` sigue
|
|
|
|
|
|
esa misma redirección sin recargar.
|
|
|
|
|
|
|
|
|
|
|
|
### Pago
|
|
|
|
|
|
|
|
|
|
|
|
Se usa **Stripe Checkout alojado**: la web no ve nunca los datos de la tarjeta.
|
|
|
|
|
|
El importe no viaja como total, sino como precio del pack por cantidad, para que
|
|
|
|
|
|
la factura de Stripe diga lo mismo que el carrito.
|
|
|
|
|
|
|
|
|
|
|
|
El pedido se guarda como pendiente antes de mandar a la pasarela y se cierra
|
|
|
|
|
|
cuando Stripe lo confirma. La confirmación llega por dos caminos —el webhook y
|
|
|
|
|
|
la vuelta a `/compra/completada`, que consulta el estado real—, así que la
|
|
|
|
|
|
compra queda registrada aunque se cierre la pestaña o el webhook tarde. Cerrar
|
|
|
|
|
|
un pedido es idempotente, de modo que llegar por los dos no molesta.
|
|
|
|
|
|
|
|
|
|
|
|
En local, para recibir el webhook:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
stripe listen --forward-to localhost:5173/api/stripe/webhook
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
y el secreto que imprime va en `STRIPE_WEBHOOK_SECRET`.
|
|
|
|
|
|
|
|
|
|
|
|
### Descargas
|
|
|
|
|
|
|
|
|
|
|
|
`/descargas/<cancion>` y `/descargas/<cancion>/<version>` comprueban sesión y
|
|
|
|
|
|
compra en cada petición y transmiten el archivo desde el directorio privado. No
|
|
|
|
|
|
hay URLs firmadas a propósito: el servidor ya tiene la sesión delante, y una URL
|
|
|
|
|
|
firmada solo añadiría una copia del permiso que puede quedarse obsoleta.
|
|
|
|
|
|
|
|
|
|
|
|
Si algún día el catálogo se mueve a almacenamiento de objetos (S3, R2), lo que
|
|
|
|
|
|
cambia es [`src/lib/server/medios.ts`](src/lib/server/medios.ts): en vez de leer
|
|
|
|
|
|
del disco, firmaría una URL temporal. El resto de la aplicación no se entera.
|
|
|
|
|
|
|
|
|
|
|
|
## Favoritos
|
|
|
|
|
|
|
|
|
|
|
|
Quien tiene cuenta puede guardar temas en una lista personal, con el corazón que
|
|
|
|
|
|
hay en la ficha, en las tarjetas y en la tabla. La lista está en `/favoritos`.
|
|
|
|
|
|
|
|
|
|
|
|
Se puede marcar **cualquier** tema, esté a la venta o no, y marcarlo no dice
|
|
|
|
|
|
nada público sobre él: no hay contadores a la vista. Es lo que lo distingue de
|
|
|
|
|
|
una compra —que da derecho a la descarga— y de una valoración, que es un dato
|
|
|
|
|
|
agregado y visible.
|
|
|
|
|
|
|
|
|
|
|
|
Los slugs viajan enteros en `page.data.favoritos`, igual que el carrito: son
|
|
|
|
|
|
unas decenas y cualquier tarjeta necesita saber si el suyo está dentro, así que
|
|
|
|
|
|
resolverlo una vez en el layout raíz sale más barato que consultarlo por
|
|
|
|
|
|
componente.
|
|
|
|
|
|
|
|
|
|
|
|
Quien no ha entrado **ve el corazón igualmente**, pero lleva a entrar. Esconderlo
|
|
|
|
|
|
dejaría la interfaz cambiando de forma según quién mira, sin explicar por qué
|
|
|
|
|
|
falta algo.
|
|
|
|
|
|
|
|
|
|
|
|
## Valoraciones
|
|
|
|
|
|
|
|
|
|
|
|
Quien tiene cuenta puede puntuar un tema de 1 a 5 desde su ficha. La media y el
|
|
|
|
|
|
número de votos se ven siempre, también sin entrar: saber cómo está valorado un
|
|
|
|
|
|
tema no exige cuenta; lo que la exige es votar, para que un voto sea de alguien.
|
|
|
|
|
|
|
|
|
|
|
|
**No hace falta haber comprado el tema.** Lo que suena en la web es un fragmento,
|
|
|
|
|
|
y opinar sobre lo que se ha escuchado es el uso normal; exigir la compra
|
|
|
|
|
|
convertiría la valoración en una reseña de producto.
|
|
|
|
|
|
|
|
|
|
|
|
Un voto por cuenta y tema, y volver a votar reemplaza el anterior. Eso lo impone
|
|
|
|
|
|
la **clave primaria compuesta** de la tabla `valoracion`, no el código de la
|
|
|
|
|
|
acción: si mañana hubiera otra vía de escritura, seguiría sin poder duplicar.
|
|
|
|
|
|
|
|
|
|
|
|
El control son cinco radios dentro de un formulario, no cinco botones con
|
|
|
|
|
|
`onclick`. De ahí salen gratis tres cosas: funciona sin JavaScript, las flechas
|
|
|
|
|
|
del teclado se mueven entre las estrellas sin programar nada, y un lector de
|
|
|
|
|
|
pantalla anuncia «3 de 5, seleccionado» en vez de cinco botones sueltos.
|
|
|
|
|
|
|
|
|
|
|
|
## Sindicación y buscadores
|
|
|
|
|
|
|
|
|
|
|
|
Tres cosas generadas del catálogo, no escritas a mano: añadir un tema o un
|
|
|
|
|
|
capítulo no puede exigir acordarse de tocar un archivo aparte.
|
|
|
|
|
|
|
|
|
|
|
|
- **`/blog/rss.xml`** — el feed. Un blog sin feed obliga a volver a mirar si hay
|
|
|
|
|
|
algo nuevo, y eso no lo hace nadie. Es lo único que se sindica: el catálogo no
|
|
|
|
|
|
es una corriente de novedades, es una obra. Va anunciado con
|
|
|
|
|
|
`<link rel="alternate">` en toda la web, que es como lo encuentran los
|
|
|
|
|
|
lectores.
|
|
|
|
|
|
- **`/sitemap.xml`** — todas las páginas de contenido, con `robots.txt`
|
|
|
|
|
|
apuntando a él. Quedan fuera las que dependen de quién mira —cuenta, carrito,
|
|
|
|
|
|
favoritos, entrar— y las de descarga: no son contenido y no hay nada que
|
|
|
|
|
|
indexar. Un test lo comprueba, porque colar ahí una ruta privada es fácil.
|
|
|
|
|
|
- **`/og.jpg`** — la imagen de reserva al compartir. Sin ella, una página sin
|
|
|
|
|
|
portada propia se pega en un chat como un enlace pelado. Es **provisional**,
|
|
|
|
|
|
como el resto del material visual: sale del fotograma de la portada.
|
|
|
|
|
|
|
Cierra una redirección abierta, quita código muerto y afina el responsive
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>
1 month ago
|
|
|
|
## Volver a donde estabas
|
|
|
|
|
|
|
|
|
|
|
|
Los formularios llevan un campo `volverA` para devolver a quien pulsa
|
|
|
|
|
|
exactamente a donde estaba. Ese campo lo escribe cualquiera, así que pasa por
|
|
|
|
|
|
[`destinoSeguro`](src/lib/server/destino.ts) antes de convertirse en una
|
|
|
|
|
|
redirección.
|
|
|
|
|
|
|
|
|
|
|
|
No compara prefijos a mano. Se intentó, y se le escapaba `/otro-dominio.com`:
|
|
|
|
|
|
el navegador convierte la barra invertida en barra normal al resolver, así que
|
|
|
|
|
|
eso es `//otro-dominio.com` disfrazado, y comprobar `//` no basta. Ahora decide
|
|
|
|
|
|
la propia resolución de URL —la misma que hará el navegador— y solo se acepta lo
|
|
|
|
|
|
que sigue en el origen del sitio.
|
|
|
|
|
|
|
|
|
|
|
|
Importa más de lo que parece en la página de entrar: sin filtrar, basta un
|
|
|
|
|
|
enlace a nuestra propia pantalla de acceso con `?volverA=` apuntando fuera para
|
|
|
|
|
|
que alguien escriba su correo aquí y acabe en una página ajena creyendo que
|
|
|
|
|
|
sigue en esta.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Variables de entorno
|
|
|
|
|
|
|
|
|
|
|
|
Copia `.env.example` a `.env`. Todas son opcionales en desarrollo:
|
|
|
|
|
|
|
|
|
|
|
|
| Variable | Para qué |
|
|
|
|
|
|
| ----------------------- | ----------------------------------------------------- |
|
|
|
|
|
|
| `DATABASE_URL` | SQLite local (`file:local.db`) o Turso (`libsql://…`) |
|
|
|
|
|
|
| `MEDIA_DIR` | Directorio privado del audio completo |
|
|
|
|
|
|
| `STRIPE_SECRET_KEY` | Cobros |
|
|
|
|
|
|
| `STRIPE_WEBHOOK_SECRET` | Firma del webhook de Stripe |
|
|
|
|
|
|
| `RESEND_API_KEY` | Códigos de acceso y formulario de contacto |
|
|
|
|
|
|
| `CONTACTO_DESTINO` | Buzón que recibe los mensajes |
|
|
|
|
|
|
| `CONTACTO_REMITENTE` | Remitente verificado en el dominio |
|
|
|
|
|
|
| `ORIGIN` | Origen público, que adapter-node necesita conocer |
|
|
|
|
|
|
|
Cierra una redirección abierta, quita código muerto y afina el responsive
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>
1 month ago
|
|
|
|
## Responsive
|
|
|
|
|
|
|
|
|
|
|
|
Se comprueba en pruebas, no de vista: [`responsive.e2e.ts`](e2e/responsive.e2e.ts)
|
|
|
|
|
|
recorre diez páginas a 320, 375, 768 y 1440 px y exige que **ninguna desborde a
|
|
|
|
|
|
lo ancho**, que ningún control suelto baje de 24×24 —el mínimo para acertarle
|
|
|
|
|
|
con el dedo; los enlaces dentro de un texto quedan fuera, los limita la línea—,
|
|
|
|
|
|
que la navegación móvil se despliegue entera y que las barras laterales **bajen
|
|
|
|
|
|
en vez de desaparecer**: son la navegación de su sección.
|
|
|
|
|
|
|
|
|
|
|
|
Dos cosas que salieron de ahí y no se veían a simple vista. Los puntos del
|
|
|
|
|
|
carrusel medían 8×8; ahora el punto se ve igual pero el botón que lo rodea mide
|
|
|
|
|
|
24×24. Y el hueco que se reserva bajo el contenido para la barra de
|
|
|
|
|
|
reproducción estaba escrito a mano —88 px— cuando la barra mide 97 en
|
|
|
|
|
|
escritorio y **145 apilada en un móvil**: se comía el pie de página. Ahora la
|
|
|
|
|
|
barra mide su propio alto con un `ResizeObserver` y lo publica en
|
|
|
|
|
|
`--player-height`; el valor de `app.css` se queda solo como reserva para el
|
|
|
|
|
|
primer pintado.
|
|
|
|
|
|
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
## Tests
|
|
|
|
|
|
|
|
|
|
|
|
- **Unitarios** (Vitest, Node): validación del frontmatter, esquemas de
|
|
|
|
|
|
contenido, reglas de la tienda, fechas y duraciones, lógica de cola del
|
|
|
|
|
|
reproductor, validación del formulario y limitador de envíos.
|
|
|
|
|
|
- **De componente** (Vitest en Chromium): renderizado de listados de temas.
|
Portada por estilos, tarjetas con otro movimiento, y fuera la palabra «segmento»
La palabra se va del proyecto entero, interfaz e identificadores. Venía del
encargo inicial pero no es la del oficio y se colaba en la interfaz: lo que hay
son estilos, discos y temas. Lo que agrupa un estilo con su obra pasa a ser
`EstiloConObra`, y el componente que lo pinta, `BloqueDeEstilo`.
La portada cuenta el catálogo en tres pasos: el estilo principal a fila
completa, sus discos debajo, y el resto en una rejilla donde cada tarjeta dice
cuánto hay y de qué se habla. Las etiquetas de un estilo no se declaran: salen
de sus temas, así que no hay dos sitios que mantener.
La página de un estilo estrena su propio `h1` —antes heredaba un `h2` del
listado, y se quedaba sin encabezado de nivel uno— y pierde el botón de
reproducir suelto en la cabecera, que no decía qué iba a sonar. Los demás
estilos la acompañan en una barra lateral que ahora vive en el layout de la
sección, así que la llevan también la portada de música y cualquier página que
se añada.
Las tarjetas se mueven de otra manera: reflejo que nace bajo el puntero, borde
dorado, portada que recupera el color, subrayado que crece y respuesta al
pulsar. Nada usa la curva por defecto.
Dos rarezas del minificador de CSS, ambas comprobadas en el build y ambas
dejaban efectos muertos: junta las funciones de un filtro sin el espacio que
las separa, y borra `backdrop-filter` sin prefijo si se declara también la
versión `-webkit-`.
Y la canción real estrena su vídeo de YouTube.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
- **End-to-end** (Playwright): navegación entre estilos, reproducción y su
|
Web de Senza Paura: catálogo, taller, blog y tienda
Sitio del letrista y compositor, con SvelteKit 2 y Svelte 5.
El catálogo es contenido, no base de datos: estilos, álbumes, canciones y
capítulos son Markdown en `src/content/`, validados al compilar y compilados
dentro del bundle, así que recorrer la música no hace ni una consulta. La base
de datos guarda solo lo que generan las personas: cuentas, carritos, pedidos,
compras, valoraciones y favoritos.
Lo que hay:
- Música agrupada por estilos, con página propia para cada estilo, álbum y
tema, buscador y tabla ordenable.
- Reproductor persistente que sobrevive a la navegación. Suena un fragmento;
el archivo completo solo se obtiene comprando.
- Taller de letras: los principios del oficio y una ficha de creación por
estilo hispanoamericano.
- Blog con feed RSS, y etiquetas que cruzan blog y catálogo.
- Tienda con cuentas por código de correo, carrito, Stripe y descargas
protegidas.
- Panel de contenido en `/admin` que escribe los mismos Markdown.
- Tema oscuro de marca y su inversión clara, aplicado antes de pintar.
Lo que falta y no es código: las claves de Stripe y de correo, el tratamiento
del IVA, las páginas de condiciones y privacidad, y el material real. Las
canciones de ejemplo son inventadas y están para sustituirse; el audio son
marcadores salvo un tema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 month ago
|
|
|
|
persistencia al cambiar de página, cola, letras, pestañas de la ficha, taller
|
|
|
|
|
|
de letras, cambio de tema, formulario de contacto, carrito anónimo, reglas del
|
|
|
|
|
|
pack, alta por código y control de acceso a las descargas.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm run test:unit -- --run
|
|
|
|
|
|
npm run test:e2e
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Despliegue
|
|
|
|
|
|
|
|
|
|
|
|
El proyecto usa `@sveltejs/adapter-node`: `npm run build` deja un servidor en
|
|
|
|
|
|
`build/`, que se arranca con `node build`. Hace falta un proceso persistente
|
|
|
|
|
|
(VPS, contenedor), no una función serverless, porque el audio privado se sirve
|
|
|
|
|
|
desde el disco.
|
|
|
|
|
|
|
|
|
|
|
|
Al desplegar:
|
|
|
|
|
|
|
|
|
|
|
|
1. `npm run db:migrate` con la `DATABASE_URL` de producción.
|
|
|
|
|
|
2. Copiar el directorio de `MEDIA_DIR` junto a la aplicación (no lo genera el
|
|
|
|
|
|
build).
|
|
|
|
|
|
3. Configurar el webhook de Stripe apuntando a `/api/stripe/webhook`.
|
|
|
|
|
|
4. `ORIGIN` con el dominio público.
|
|
|
|
|
|
|
|
|
|
|
|
Lo que el servidor necesita **al ejecutarse** va en `dependencies`, no en
|
|
|
|
|
|
`devDependencies`: hoy `@libsql/client`, `drizzle-orm` y `stripe`. No es
|
|
|
|
|
|
cosmético. Vite deja fuera del paquete lo que está en `dependencies` y empaqueta
|
|
|
|
|
|
el resto; al empaquetar `@libsql/client` se rompe la carga de su binario nativo
|
|
|
|
|
|
(`@libsql/win32-x64-msvc` y equivalentes) y el servidor no llega a arrancar.
|
|
|
|
|
|
Al añadir una dependencia, la pregunta es si hace falta al construir o al servir.
|
|
|
|
|
|
|
|
|
|
|
|
Todas las páginas se renderizan en el servidor. El sitio dejó de prerenderizarse
|
|
|
|
|
|
al añadir las cuentas: la cabecera y los listados dependen de quién pide la
|
|
|
|
|
|
página.
|
|
|
|
|
|
|
|
|
|
|
|
## Un fallo conocido del entorno
|
|
|
|
|
|
|
|
|
|
|
|
En Windows, el empaquetador nativo de Vite 8 (rolldown) termina con SIGSEGV **al
|
|
|
|
|
|
salir del proceso** de forma intermitente —unas dos de cada tres veces en la
|
|
|
|
|
|
máquina donde se escribió esto—. El build se completa y los artefactos son
|
|
|
|
|
|
correctos; lo que falla es el cierre.
|
|
|
|
|
|
|
|
|
|
|
|
`npm run build` no lo disimula: devuelve el código de error tal cual, que es lo
|
|
|
|
|
|
que debe llegar a una plataforma de despliegue. Lo que sí lo tolera es
|
|
|
|
|
|
[`scripts/construir.mjs`](scripts/construir.mjs), que usa solo
|
|
|
|
|
|
`npm run test:e2e`: comprueba que los artefactos existan y se hayan escrito en
|
|
|
|
|
|
esa ejecución, y si es así continúa avisando por consola. Un build que falla de
|
|
|
|
|
|
verdad no deja artefactos nuevos y sigue fallando ahí.
|
|
|
|
|
|
|
|
|
|
|
|
Si el fallo no aparece en el servidor de despliegue (es habitual: en Linux no se
|
|
|
|
|
|
ha visto), no hay nada más que hacer.
|
|
|
|
|
|
|
|
|
|
|
|
## Decisiones que conviene conocer
|
|
|
|
|
|
|
|
|
|
|
|
- **El catálogo no está en la base de datos.** Los pedidos referencian las
|
|
|
|
|
|
canciones por slug, y guardan el título del momento de la compra: una factura
|
|
|
|
|
|
no debe cambiar porque se renombre un tema.
|
|
|
|
|
|
- **Markdown con validación estricta.** El frontmatter se convierte a datos
|
|
|
|
|
|
tipados en un único punto ([`src/lib/content/fields.ts`](src/lib/content/fields.ts)),
|
|
|
|
|
|
y los errores dicen archivo y campo.
|
|
|
|
|
|
- **Fechas siempre en UTC.** Las del contenido son días sueltos. Formateadas en
|
|
|
|
|
|
la zona local del navegador, un disco publicado el 1 de marzo se mostraría
|
|
|
|
|
|
como 28 de febrero al oeste de Greenwich.
|
|
|
|
|
|
- **`remark-breaks` está activo.** Sin él, Markdown juntaría los versos de una
|
|
|
|
|
|
letra en un único párrafo.
|
|
|
|
|
|
- **El formulario de acceso está limitado en varias capas**
|
|
|
|
|
|
([`src/lib/server/limite.ts`](src/lib/server/limite.ts)), y no por costumbre:
|
|
|
|
|
|
manda un correo a la dirección que le escriban, sea de quien sea, así que sin
|
|
|
|
|
|
frenos es un botón para bombardear el buzón de cualquiera desde nuestro
|
|
|
|
|
|
dominio. Hay enfriamiento por dirección, tope por hora y por día, tope por IP
|
|
|
|
|
|
y un techo global que salva la cuota de envío. Los contadores viven en la base
|
|
|
|
|
|
de datos, no en memoria: uno que se borra al reiniciar no protege a nadie. Y
|
|
|
|
|
|
el tope por dirección usa la forma canónica del correo
|
|
|
|
|
|
([`correo-clave.ts`](src/lib/server/correo-clave.ts)), porque si contara la
|
|
|
|
|
|
cadena literal bastaría con ir cambiando `+1`, `+2` para saltárselo.
|
|
|
|
|
|
- **Una cuenta solo existe tras verificar el código.** Pedir un código para una
|
|
|
|
|
|
dirección ajena no crea nada ni da acceso a nada.
|
|
|
|
|
|
- **El contenido va entero en el bundle.** Los listados necesitan el frontmatter
|
|
|
|
|
|
de forma síncrona, lo que obliga a importar los `.md` de forma estática, y eso
|
|
|
|
|
|
impide separar el cuerpo (las letras) en trozos aparte. Con un catálogo
|
|
|
|
|
|
pequeño el coste es despreciable. Si crece hasta cientos de temas, la salida
|
|
|
|
|
|
es un plugin de Vite que exponga el frontmatter como módulo virtual; está
|
|
|
|
|
|
anotado en [`src/lib/content/index.ts`](src/lib/content/index.ts).
|