@import './fuentes.css'; @import './marca.css'; @import './tokens.css'; @import './controles.css'; /* * --------------------------------------------------------------------------- * DIRECCIÓN VISUAL * --------------------------------------------------------------------------- * Los colores de marca: azul marino oscuro aterciopelado y dorado. * * El marino lleva croma de verdad (0,04) en lugar de ser un gris azulado: eso * es lo que le da el aspecto de terciopelo en vez de el de pizarra. Todos los * fondos comparten el mismo tono, así que la profundidad la marca la claridad, * nunca un cambio de color. * * El dorado es el único acento y lo llevan las cosas accionables; nada más lo * usa, que es lo que hace que se note. * * Se compromete con un solo modo, sin variante clara: la portada es un fondo a * sangre con texto encima y esa composición no sobrevive a un fondo blanco sin * rehacerla entera. `color-scheme: dark` avisa al navegador para que sus * controles vayan a juego. * --------------------------------------------------------------------------- */ :root { color-scheme: dark; /* Marino, del más hondo al más cercano. Mismo tono, distinta claridad. */ /* Blancos apenas teñidos: por encima de 0,02 de croma dejarían de leerse como blancos y competirían con el dorado. */ /* * El oro. El plano de antes tiraba a amarillo; este baja de claridad y sube * de croma, que es lo que separa un metal de un color de rotulador. */ /* * El metal de la marca no cambia con el tema. Los acentos funcionales sí * pueden adaptarse al fondo claro, pero el logotipo conserva estas tres * tintas exactas en ambos modos. */ --c-accent: var(--c-marca-oro); --c-form-action: oklch(0.8 0.095 230); --c-form-action-hover: oklch(0.86 0.075 230); --c-form-action-text: oklch(0.22 0.05 255); --c-toggle-active: oklch(0.76 0.13 155); --c-toggle-text: oklch(0.2 0.045 155); --c-toggle-rail: var(--c-bg-raised); --c-accent-text: oklch(0.16 0.042 264); /* * El dorado como superficie va aparte del dorado como texto, porque no * pueden ser el mismo valor: relleno necesita ser claro para que encima se * lea el marino, y escrito necesita ser oscuro para leerse sobre el fondo. * En el tema oscuro coinciden; en el claro no, y por eso existe la * distinción. */ --c-accent-superficie: var(--c-marca-oro); /* Los dos extremos del reflejo: la luz que lo cruza y la sombra del canto. */ --c-accent-brillo: var(--c-marca-oro-brillo); --c-accent-sombra: var(--c-marca-oro-sombra); --c-accent-borde: var(--c-accent-sombra); /* Los controles finos necesitan contraste incluso en la zona de brillo. */ --c-control-oro: var(--c-marca-oro); --c-control-oro-brillo: var(--c-marca-oro-brillo); --c-control-oro-sombra: var(--c-marca-oro-sombra); /* * El verdemar: el tercer color de la paleta, para datos. * * El mapa necesitaba dos intensidades de algo que no fuera el dorado, y * mezclarlo con el fondo marino daba barro —literalmente un marrón sucio—. * Este es de la misma familia que el marino, un paso hacia el verde, así que * conviven sin pelearse; y deja el dorado para lo que se pulsa y lo que se * elige, que es su trabajo en el resto del sitio. */ --c-dato: oklch(0.74 0.09 198); /* El mismo tono, un paso por debajo: «también se canta aquí», no otro dato. */ --c-dato-tenue: oklch(0.66 0.085 198); --c-dato-vacio: oklch(0.29 0.03 255); /* * Dos familias: una serif con carácter para los títulos y para la letra de * las canciones —que es texto para leer despacio— y una sans de rejilla * para interfaz, datos y navegación. */ --font-versos: var(--font-display); /* * El oro de una superficie no es un color plano: un metal refleja, y lo que * lo hace parecer metal es la banda de luz que lo cruza. Cinco paradas —de * la sombra del canto al brillo y otra vez a la sombra— bastan para * sugerirlo sin que parezca un tubo de cromo. * * El fondo se pinta más alto que el elemento a propósito: así, al acercar el * puntero, se desplaza y la luz recorre la pieza en vez de limitarse a * aclararla. Es la diferencia entre algo que se ilumina y algo que gira. */ --gradiente-oro: linear-gradient( 166deg, var(--c-accent-sombra) 0%, var(--c-accent-superficie) 24%, var(--c-accent-brillo) 46%, var(--c-accent-superficie) 64%, var(--c-accent-sombra) 100% ); /* * El metal propio del logotipo. Usa los colores fijos de marca en vez de los * acentos funcionales del tema: cambiar el fondo no debe convertir el logo en * otro dorado ni aplanar su banda de luz. */ --oro-escrito: linear-gradient( 166deg, var(--c-marca-oro-sombra) 0%, var(--c-marca-oro) 24%, var(--c-marca-oro-brillo) 46%, var(--c-marca-oro) 64%, var(--c-marca-oro-sombra) 100% ); /* * El mismo metal, orientado de izquierda a derecha para controles lineales: * barras de progreso, volumen y barras de desplazamiento. Esta gama se * oscurece en claro para que incluso el reflejo se distinga del carril. */ --oro-control: linear-gradient( 90deg, var(--c-control-oro-sombra) 0%, var(--c-control-oro) 32%, var(--c-control-oro-brillo) 52%, var(--c-control-oro) 72%, var(--c-control-oro-sombra) 100% ); --oro-control-pulgar: radial-gradient( circle at 34% 28%, var(--c-control-oro-brillo) 0 12%, var(--c-control-oro) 46%, var(--c-control-oro-sombra) 100% ); /* * Los botones. Un solo tamaño y un solo relleno, porque antes convivían tres * —el dorado a 16px con relleno 8/16, el de al lado a 14px con 12/24— y dos * botones juntos con distinta letra se ven como un descuido. * * 15px es el mismo cuerpo del menú: lo pulsable se lee igual en todas * partes. */ --boton-texto: 0.9375rem; --boton-relleno: var(--sp-3) var(--sp-5); /* La versión reducida, para las esquinas de una tarjeta o una fila. */ --boton-texto-corto: 0.8125rem; --boton-relleno-corto: var(--sp-2) var(--sp-3); /* * El ancho útil: 1184px. Se probó a subirlo a 88rem (1408px) para aprovechar * los monitores anchos y salió al revés: la prosa tiene una medida de * lectura y no crece con el contenedor, así que lo único que se ensanchó * fue el hueco a su derecha. En la página legal se veía a la legua. * * Si algún día se ensancha, hay que dar antes a cada texto una columna que * crezca con él; sin eso, más ancho es más vacío. */ --container: 74rem; /* Alto de la barra del reproductor: lo usa el padding inferior del layout. */ --player-height: 88px; /* * Altura aproximada de la cabecera, que ahora es pegajosa. No la mide nadie: * es una referencia para separar de ella las anclas y las barras laterales * que también se quedan fijas. Si cambia el espaciado de la cabecera, este * número la sigue. */ --altura-cabecera: 4.75rem; } /* * La inversión clara, y SOLO cuando se pide. * * El marino es el modo por defecto se pida lo que se pida al sistema, y no es * un descuido: es la marca. Un sitio cuyo color forma parte de lo que cuenta no * puede salir en blanco porque el portátil de quien entra venga configurado * así; y al revés que en una herramienta de trabajo, aquí nadie va a pasar ocho * horas seguidas leyendo. Antes esto seguía a `prefers-color-scheme` y llevaba * a que media parte de las visitas viera una versión que no es la del sitio. * * El claro sigue entero y a un clic, en el botón de la cabecera, y la elección * se recuerda. Lo que se deja de hacer es decidirlo por nadie a partir de una * preferencia que se configuró pensando en otra cosa. * * Es una inversión de verdad, no un fondo blanco con lo mismo encima: el texto * pasa a ser el propio marino y los fondos, blancos apenas teñidos de él. Así * los dos modos son la misma marca vista de dos maneras. * * El dorado tiene que bajar de claridad aquí. El de 0,82 se lee bien sobre * marino y desaparece sobre blanco, así que este es un dorado más hondo, casi * bronce, que mantiene el contraste como texto. */ :root[data-tema='claro'] { --c-toggle-active: oklch(0.43 0.09 155); --c-toggle-text: oklch(0.99 0.005 155); --c-form-action: oklch(0.3 0.075 255); --c-form-action-hover: oklch(0.37 0.085 255); --c-form-action-text: oklch(0.98 0.005 255); color-scheme: light; --c-bg: oklch(0.98 0.008 264); --c-bg-raised: oklch(0.955 0.011 264); --c-bg-sunken: oklch(0.915 0.016 264); --c-border: oklch(0.86 0.02 264); --c-text: oklch(0.24 0.055 264); --c-text-muted: oklch(0.48 0.038 264); --c-accent: oklch(0.42 0.135 70); --c-accent-superficie: oklch(0.76 0.13 82); --c-accent-text: oklch(0.24 0.055 264); --c-accent-brillo: oklch(0.9 0.1 92); --c-accent-sombra: oklch(0.63 0.12 74); --c-accent-borde: var(--c-accent); --c-control-oro: oklch(0.46 0.1 78); --c-control-oro-brillo: oklch(0.58 0.11 78); --c-control-oro-sombra: oklch(0.4 0.09 78); /* En claro se invierten: el dato pintado tiene que ser oscuro sobre papel. */ --c-dato: oklch(0.5 0.085 200); /* El mismo tono, un paso por debajo: «también se canta aquí», no otro dato. */ --c-dato-tenue: oklch(0.62 0.08 200); --c-dato-vacio: oklch(0.93 0.012 255); --c-danger: oklch(0.5 0.17 25); --c-focus: oklch(0.5 0.13 264); } *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; /* * Sin esto, un enlace a un ancla dejaría su destino debajo de la cabecera * pegajosa. Vale para los saltos con teclado y para el enlace de saltar al * contenido, no solo para los `#` escritos a mano. */ scroll-padding-top: calc(var(--altura-cabecera) + var(--sp-4)); } body { margin: 0; background: var(--c-bg); color: var(--c-text); font-family: var(--font-body); font-size: var(--fs-base); line-height: var(--leading-body); -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } /* * Los desplazamientos forman parte de la interfaz visible. Se pintan con el * oro adaptado al fondo. Las reglas WebKit aportan el reflejo metálico; * `scrollbar-color` conserva su contraste en Firefox. */ * { scrollbar-width: thin; scrollbar-color: var(--c-control-oro) var(--c-bg-sunken); } *::-webkit-scrollbar { width: 0.65rem; height: 0.65rem; } *::-webkit-scrollbar-track { background: var(--c-bg-sunken); border-radius: var(--radius-round); } *::-webkit-scrollbar-thumb { min-width: 2.5rem; border: 2px solid var(--c-bg-sunken); border-radius: var(--radius-round); background-color: var(--c-control-oro); background-image: var(--oro-control); } *::-webkit-scrollbar-thumb:hover { background-color: var(--c-control-oro-brillo); } /* * Primitiva compartida por progreso y volumen. La zona recorrida usa el metal * de marca; la pendiente conserva su tamaño con `background-size`, de modo que * el brillo acaba siempre dentro de la parte ya recorrida. */ .rango-dorado { appearance: none; -webkit-appearance: none; height: 1rem; background: transparent; accent-color: var(--c-control-oro); cursor: pointer; } .rango-dorado::-webkit-slider-runnable-track { height: 5px; border: 1px solid var(--c-control-oro-sombra); border-radius: var(--radius-round); background-color: var(--c-bg-sunken); background-image: var(--oro-control); background-repeat: no-repeat; background-size: var(--avance, 0%) 100%; } .rango-dorado::-moz-range-track { height: 5px; border: 1px solid var(--c-control-oro-sombra); border-radius: var(--radius-round); background: var(--c-bg-sunken); } .rango-dorado::-moz-range-progress { height: 5px; border-radius: var(--radius-round); background-color: var(--c-control-oro); background-image: var(--oro-control); } .rango-dorado::-webkit-slider-thumb { appearance: none; -webkit-appearance: none; width: 14px; height: 14px; margin-top: calc((var(--sp-1) + var(--gap-hairline)) * -1); border: 1px solid var(--c-control-oro-sombra); border-radius: var(--radius-round); background-color: var(--c-control-oro); background-image: var(--oro-control-pulgar); box-shadow: 0 0 0 2px var(--c-bg-raised); } .rango-dorado::-moz-range-thumb { width: 14px; height: 14px; border: 1px solid var(--c-control-oro-sombra); border-radius: var(--radius-round); background-color: var(--c-control-oro); background-image: var(--oro-control-pulgar); box-shadow: 0 0 0 2px var(--c-bg-raised); } .rango-dorado:hover::-webkit-slider-thumb, .rango-dorado:focus-visible::-webkit-slider-thumb, .rango-dorado:hover::-moz-range-thumb, .rango-dorado:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 2px var(--c-bg-raised), 0 0 0 4px color-mix(in oklab, var(--c-control-oro) 25%, transparent); } h1, h2, h3, h4 { font-family: var(--font-display); line-height: var(--leading-heading); margin: 0 0 var(--sp-3); font-weight: var(--font-weight-regular); letter-spacing: -0.015em; text-wrap: balance; } h1 { font-size: var(--fs-3xl); } h2 { font-size: var(--fs-2xl); } h3 { font-size: var(--fs-xl); } p { margin: 0 0 var(--sp-4); text-wrap: pretty; } a { color: inherit; text-underline-offset: 0.2em; transition: color var(--duration-normal) var(--ease-standard); } /* * Todo enlace responde al puntero. Antes no había ninguna regla global, así que * los enlaces sin estilo propio —el del feed, los del pie— no daban ninguna * señal de ser pulsables. El dorado es el color de lo accionable en esta web, * así que es el que corresponde. * * Los componentes que ya deciden su propio color al pasar por encima ganan por * especificidad: esto es el suelo, no una imposición. */ a:hover { color: var(--c-accent); } /* Selección exclusiva: una barra común y un segmento verde activo. */ .toggle-rail { display: inline-flex; max-width: 100%; padding: calc(var(--sp-1) - var(--gap-hairline)); gap: var(--sp-1); border: 1px solid var(--c-border); border-radius: var(--radius-overlay); background: var(--c-toggle-rail); } .toggle-rail__option { display: inline-flex; flex: 1 1 auto; align-items: center; justify-content: center; gap: var(--sp-2); min-width: 0; min-height: var(--control-height-compact); padding: var(--sp-1) var(--sp-3); border: 0; border-radius: var(--radius-tarjeta); background: transparent; color: var(--c-text-muted); font: inherit; font-size: var(--boton-texto); font-weight: var(--font-weight-regular); letter-spacing: 0.01em; line-height: var(--leading-ui); cursor: pointer; transition: background-color var(--duration-normal) var(--ease-standard), color var(--duration-normal) var(--ease-standard); } .toggle-rail__option:hover { color: var(--c-text); } .toggle-rail__option[aria-pressed='true'] { background: var(--c-toggle-active); color: var(--c-toggle-text); } .toggle-rail__option:focus-visible { outline: 2px solid var(--c-toggle-active); outline-offset: 2px; } .toggle-rail__option:disabled { cursor: not-allowed; opacity: 0.5; } /* Los formularios usan azul; las acciones de reproducción conservan el oro. */ .btn.btn--form-primary { background: var(--c-form-action); border-color: var(--c-form-action); color: var(--c-form-action-text); box-shadow: none; transition: background-color var(--duration-normal) var(--ease-standard), border-color var(--duration-normal) var(--ease-standard); } .btn.btn--form-primary:hover { background: var(--c-form-action-hover); border-color: var(--c-form-action-hover); color: var(--c-form-action-text); } @media (prefers-reduced-motion: reduce) { a { transition: none; } } img { max-width: 100%; height: auto; display: block; } ul, ol { margin: 0 0 var(--sp-4); padding-left: var(--sp-5); } button { font: inherit; color: inherit; } :where(a, button, input, textarea, select, [tabindex]):focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; border-radius: var(--radius-detail); } /* --- primitivas de layout ------------------------------------------------ */ .container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--sp-4); } .stack > * + * { margin-top: var(--sp-4); } .muted { color: var(--c-text-muted); } .eyebrow { font-size: var(--fs-xs); letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-text-muted); margin: 0 0 var(--sp-2); } .entradilla-pagina { max-width: 56ch; color: var(--c-text-muted); font-size: var(--fs-lg); font-weight: var(--font-weight-regular); } .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: calc(var(--gap-hairline) * -1); overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; } .btn { /* * `background-color` y no el atajo `background`: el atajo reinicia también * `background-image`, así que una variante con degradado —el botón dorado— * lo perdía en cuanto esta regla o la de `:hover` se aplicaban, y se quedaba * en un rectángulo oscuro con el borde de oro. */ background-color: transparent; transition: background-color 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard); } /* * El botón secundario acusa el puntero con el fondo y el canto, no con el * color del texto: teñirlo de dorado además sería una señal doble, y encima * competiría con el botón dorado que suele tener al lado. El color se declara * aquí para ganarle a la regla general de los enlaces, ya que muchos de estos * botones lo son. * La condición de disponibilidad no suma especificidad: las variantes de * color deben conservar su texto, fondo y borde propios también en hover. */ .btn:hover:where(:not(:disabled)) { background-color: var(--c-bg-raised); border-color: var(--c-text-muted); color: inherit; } .btn:disabled { opacity: 0.45; cursor: default; } /* --- diálogos ------------------------------------------------------------ * * Una sola carcasa para acceso, contacto y búsqueda. El borde no forma parte * del diálogo: la separación del plano inferior la dan el fondo y la sombra. */ .modal-screen { width: 100%; max-width: 100%; height: 100%; max-height: 100%; margin: 0; padding: var(--sp-4); border: 0; background: transparent; color: var(--c-text); overflow: auto; place-items: center; } .modal-screen[open] { display: grid; } .modal-screen::backdrop { background: var(--c-overlay); backdrop-filter: blur(4px); } .modal-surface { position: relative; width: 100%; border-radius: var(--radius-overlay); background: var(--c-bg-raised); box-shadow: var(--shadow-modal); } .modal-close { position: absolute; top: var(--sp-3); right: var(--sp-3); display: inline-grid; place-items: center; width: var(--control-height); height: var(--control-height); padding: 0; border: 0; border-radius: var(--radius-round); background: transparent; color: var(--c-text-muted); cursor: pointer; transition: background-color var(--duration-normal) var(--ease-standard), color var(--duration-normal) var(--ease-standard); } .modal-close:hover, .modal-close:focus-visible { background: var(--c-bg-sunken); color: var(--c-text); } @media (max-width: 34rem) { .modal-screen { padding: var(--sp-3); } } /* * El aspecto dorado, aparte del botón que lo estrenó. * * Lo lleva también el conmutador de vista de la página de música, que no es un * `.btn` —va dentro de una barra y no puede tener canto ni esquinas propias— * pero sí tiene que ser el mismo oro. Copiarlo era garantizar que un día uno de * los dos se quedara plano. */ .btn--primary, .oro { /* El color plano queda de reserva: si el degradado no se pintara, el botón sigue siendo dorado y legible, no transparente. */ background-color: var(--c-accent-superficie); background-image: var(--gradiente-oro); background-size: 100% 240%; background-position: 0 22%; color: var(--c-accent-text); /* El filo de luz de arriba. Sin él, el degradado se ve pintado, no biselado. */ box-shadow: var(--shadow-control-highlight); transition: background-position 0.6s var(--ease-emphasized), border-color 0.2s var(--ease-standard); } /* El canto sí es del botón: dentro de una barra no hay borde que teñir. */ .btn--primary { border-color: var(--c-accent-borde); } /* * Al acercar el puntero, la luz recorre la pieza. El color de fondo se repite * aquí porque `.btn:hover` lo cambia por el gris de los botones normales: sin * esto, un dorado que no llegara a pintar el degradado se vería oscuro. */ .btn--primary:hover, .oro:hover { background-color: var(--c-accent-superficie); background-position: 0 78%; border-color: var(--c-accent); /* * El color se repite aquí porque `.btn:hover` lo pone en `inherit` —para * que la regla general de los enlaces no tiña de dorado los secundarios—. * Ambas reglas tienen la misma especificidad y esta variante, declarada * después, conserva el texto marino sobre el oro. */ color: var(--c-accent-text); } @media (prefers-reduced-motion: reduce) { .btn--primary, .oro { transition: none; } } /* --- etiquetas ----------------------------------------------------------- * * La pastilla de una etiqueta. Estaba repetida en cinco sitios con cinco copias * del mismo borde redondeado, y se escribe una vez. * * Va aquí y no en un componente porque a veces es un enlace —lleva a lo que * agrupa— y a veces es solo texto: dentro de una tarjeta que ya es clicable, * meter enlaces dentro de otro enlace deja una zona donde el clic hace una cosa * distinta según el píxel exacto. */ .chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); list-style: none; margin: 0; padding: 0; } .chip { display: inline-block; padding: var(--sp-1) var(--sp-3); border: 1px solid var(--c-border); border-radius: var(--radius-boton); font-size: var(--fs-xs); color: var(--c-text-muted); text-decoration: none; transition: border-color 0.3s var(--ease-standard), color 0.3s var(--ease-standard); } a.chip:hover, a.chip:focus-visible { border-color: var(--c-accent); color: var(--c-text); } @media (prefers-reduced-motion: reduce) { .chip { transition: none; } } /* --- tarjetas ------------------------------------------------------------ */ /* * El comportamiento de las tarjetas (aparición al entrar en pantalla y * respuesta al puntero) vive aquí y no en cada componente: son tres tipos de * tarjeta —estilo, álbum y canción— y tenían que moverse igual. Al ser CSS * global no hace falta pelearse con el ámbito de Svelte para los atributos que * escribe la acción `revelar` en tiempo de ejecución. * * Sobre el movimiento: nada usa `ease`, que es la curva por defecto y se nota * como tal —arranca y frena igual, y todo acaba pareciéndose—. Aquí se sale * rápido y se frena largo, que es como se mueve algo que pesa. */ .tarjeta { position: relative; display: flex; flex-direction: column; height: 100%; border: 1px solid var(--c-border); border-radius: var(--radius-tarjeta); background: var(--c-bg-raised); overflow: hidden; isolation: isolate; transition: background-color 0.38s var(--ease-standard), border-color 0.32s var(--ease-standard), box-shadow 0.42s var(--ease-emphasized); } /* * El selector de listas es un panel superpuesto que nace dentro de la tarjeta. * Mientras está abierto debe poder rebasar sus bordes y quedar sobre las * tarjetas vecinas; al cerrarlo se recupera el recorte de portada y efectos. */ .tarjeta:has(details[open]) { z-index: 10; overflow: visible; } .tarjeta:has(details[open])::before, .tarjeta:has(details[open])::after { border-radius: inherit; } .tarjeta:has(details[open]) .tarjeta__medio { border-radius: calc(var(--radius-tarjeta) - 1px) calc(var(--radius-tarjeta) - 1px) 0 0; } /* * El reflejo. Nace donde está el puntero —lo escribe la acción `brillo` en * `--mx` y `--my`— y por eso no se lee como un efecto enlatado. Sin JavaScript * las variables no existen y cae arriba en el centro, que es un valor * razonable, no un fallo. */ .tarjeta::before { content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: 0; background: radial-gradient( 18rem 18rem at var(--mx, 50%) var(--my, 0%), color-mix(in oklab, var(--c-accent) 11%, transparent), transparent 72% ); transition: opacity var(--duration-slow) var(--ease-standard); } .tarjeta:hover::before, .tarjeta:focus-within::before { opacity: 1; } /* * Un destello breve cose visualmente portada y cuerpo al entrar en la tarjeta. * Se anima esta capa, no el contenido, de modo que la portada no cambia de * posición ni de tamaño. El recorrido solo ocurre una vez por entrada. */ .tarjeta::after { content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none; background: linear-gradient( 112deg, transparent 24%, color-mix(in oklab, var(--c-accent-brillo) 5%, transparent) 40%, color-mix(in oklab, var(--c-accent-brillo) 21%, transparent) 50%, color-mix(in oklab, var(--c-accent-brillo) 6%, transparent) 60%, transparent 76% ); transform: translateX(-125%); opacity: 0; } .tarjeta:hover::after, .tarjeta:focus-within::after { animation: tarjeta-destello var(--duration-reveal) var(--ease-emphasized) both; } @keyframes tarjeta-destello { 0% { transform: translateX(-125%); opacity: 0; } 18% { opacity: 1; } 100% { transform: translateX(125%); opacity: 0; } } /* * Aparición al entrar en pantalla, para cualquier elemento con la acción * `revelar`. Vive aquí y no en cada componente por dos motivos: lo usan las * tarjetas y también las listas del taller, y sobre todo porque el atributo lo * escribe la acción en tiempo de ejecución, así que el compilador de Svelte no * lo ve en el marcado y podaría la regla como si no se usara. * * El estado oculto lo pone la acción, nunca el CSS por su cuenta: sin * JavaScript no hay atributo y todo se ve directamente. */ [data-revelar='oculto'] { opacity: 0; transform: translateY(18px) scale(0.985); } [data-revelar='visible'] { /* * backwards conserva el estado inicial durante el retraso, pero libera * transform al terminar. Con both, el último fotograma seguía mandando para * siempre y anulaba la respuesta de la tarjeta al puntero. */ animation: revelarse var(--duration-reveal) var(--ease-emphasized) var(--retraso-revelado, 0ms) backwards; } @keyframes revelarse { from { opacity: 0; transform: translateY(18px) scale(0.985); filter: blur(6px); } to { opacity: 1; transform: none; filter: blur(0); } } /* * Al acercarse, el borde, la sombra y el reflejo responden sin desplazar la * tarjeta. Así la portada permanece anclada y el puntero no provoca saltos. */ .tarjeta:hover, .tarjeta:focus-within { background-color: color-mix(in oklab, var(--c-bg-raised) 94%, var(--c-accent)); border-color: color-mix(in oklab, var(--c-accent) 62%, var(--c-border)); box-shadow: 0 2px 8px -5px rgb(0 0 0 / 0.5), 0 22px 42px -26px rgb(0 0 0 / 0.88), 0 0 30px -18px color-mix(in oklab, var(--c-accent) 58%, transparent); } /* Al pulsar, la sombra se recoge sin mover ni escalar la portada. */ .tarjeta:active { box-shadow: 0 1px 4px -3px rgb(0 0 0 / 0.5), 0 10px 22px -20px rgb(0 0 0 / 0.75), inset 0 0 0 1px color-mix(in oklab, var(--c-accent) 18%, transparent); transition-duration: var(--duration-fast); } .tarjeta__medio { position: relative; aspect-ratio: 1; overflow: hidden; background: var(--c-bg-sunken); } .tarjeta__medio--ancho { aspect-ratio: 16 / 10; } .tarjeta__medio img { width: 100%; height: 100%; object-fit: cover; transform: none; transition: none; } /* Sombra al pie de la portada, para que el cuerpo no arranque de golpe. */ .tarjeta__medio::after { content: ''; position: absolute; inset: auto 0 0; height: 45%; pointer-events: none; background: linear-gradient( to top, color-mix(in oklab, var(--c-bg) 70%, transparent), transparent ); opacity: 0.7; } .tarjeta__cuerpo { position: relative; z-index: 1; display: flex; flex-direction: column; gap: var(--sp-2); flex: 1; padding: var(--sp-4); } .tarjeta__cabecera { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); } .tarjeta__cabecera > :first-child { min-width: 0; } /* * Hace clicable la tarjeta entera sin envolverla en un enlace, que dejaría los * botones dentro de él: ni sería HTML válido ni se podría usar con teclado. */ .tarjeta__enlace::after { content: ''; position: absolute; inset: 0; z-index: 1; } /* * El subrayado del título crece desde la izquierda en vez de aparecer entero. * Va con el fondo y no con un borde para poder animar el ancho sin que la línea * cambie de sitio ni empuje al texto. */ .tarjeta__enlace { background-image: linear-gradient(var(--c-accent), var(--c-accent)); background-repeat: no-repeat; background-position: 0 100%; background-size: 0 1px; transition: background-size 0.45s var(--ease-emphasized), color 0.3s var(--ease-standard); } .tarjeta:hover .tarjeta__enlace, .tarjeta:focus-within .tarjeta__enlace { color: var(--c-accent); background-size: 100% 1px; } /* El reproductor vive en el cuerpo y no tapa ningún detalle de la portada. */ .tarjeta__reproduccion { position: relative; z-index: 2; display: flex; align-items: center; flex: 0 0 auto; } /* * El selector lleva `.tarjeta` delante a propósito: los estilos del botón * vienen de un componente y Svelte les añade su clase de ámbito, así que con * menos peso no se aplicarían. Las tarjetas comparten la escala compacta de * controles, proporcionada al título y al resto de sus acciones. */ .tarjeta .tarjeta__reproduccion button { --lado-play: var(--control-height-compact); min-width: var(--lado-play); min-height: var(--lado-play); width: var(--lado-play); height: var(--lado-play); padding: 0; } @media (pointer: coarse) { .tarjeta .tarjeta__reproduccion button { --lado-play: var(--control-height); } } /* Lo que se apoya sobre la tarjeta y tiene que quedar por encima del enlace. */ .tarjeta__encima { position: relative; z-index: 2; } @media (prefers-reduced-motion: reduce) { [data-revelar='visible'] { animation: none; } .tarjeta, .tarjeta::before, .tarjeta::after, .tarjeta__enlace { transition: none; animation: none; } .tarjeta:hover, .tarjeta:focus-within, .tarjeta:active { transform: none; } } /* --- términos del glosario ------------------------------------------------ * * El subrayado punteado es la convención de «esto tiene definición» desde * mucho antes que la web, y se distingue de un enlace de verdad: un enlace * lleva a otra página, esto abre un globo y te deja donde estabas. * * Va aquí y no en un componente porque el botón lo escribe el servidor dentro * del HTML del capítulo, donde el ámbito de Svelte no llega. */ .termino { border: 0; padding: 0; background: none; font: inherit; color: inherit; cursor: help; text-decoration: underline dotted; text-decoration-color: color-mix(in oklab, var(--c-accent) 60%, transparent); text-underline-offset: 0.2em; text-decoration-thickness: 1px; } .termino:hover, .termino:focus-visible { color: var(--c-accent); text-decoration-color: var(--c-accent); }