El nombre de la cabecera, en oro de verdad y más grande

Las mismas cinco paradas y el mismo ángulo que el botón dorado, recortadas
sobre las letras, y la banda de luz recorre el nombre al pasar el puntero igual
que recorre el botón. De 24 a 34 píxeles, que es el siguiente peldaño de la
escala y no un número a ojo.

El recorte va dentro de un `@supports` y el color plano se queda fuera: sin el
recorte, `color: transparent` dejaría la marca invisible, y eso es peor que
dejarla dorada y lisa. Es el mismo cuidado que ya tenía el botón, que guarda un
color de reserva por si el degradado no llega a pintar.

Y hace falta un segundo degradado, `--oro-escrito`. Sobre marino es
literalmente el mismo que el del botón —la peor de sus paradas da 5,4 de
contraste—, pero sobre papel no vale: medido contra el fondo claro, la parada
de brillo da **1,26**, la de superficie 2,05 y la de sombra 3,79, las tres por
debajo del mínimo de 4,5. Con el degradado del botón, el centro de las letras
desaparecía. En claro la banda va al revés: el punto más claro es el propio
`--c-accent` —5,29— y los extremos se oscurecen. Sigue pareciendo metal porque
lo que hace el efecto es la banda que cruza, no que llegue a brillar.

Es la regla que el proyecto ya tenía escrita y que yo no había aplicado: el
dorado hace dos trabajos, y de relleno tiene que ser claro mientras que escrito
tiene que ser hondo.

La prueba comprueba **cada parada** del degradado contra el fondo, en los dos
temas. A ojo, en la captura, el fallo se veía como «algo más pálido»; en
números, como ilegible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
master
dev 4 weeks ago
parent 31a28b18ab
commit de3215ea71

@ -251,6 +251,84 @@ test.describe('Contraste del dorado', () => {
await expect(page.locator('html')).toHaveAttribute('data-tema', 'claro');
});
/*
* El logo lleva el oro recortado sobre las letras, y ahí el degradado no
* puede usar las mismas paradas que un botón: en un botón el oro es el
* fondo y encima va marino, y en el logo el oro *es* el texto.
*
* Se comprueba parada por parada porque el fallo fue exactamente ese: con el
* degradado del botón, la parada de brillo daba 1,26 sobre el fondo claro y
* el centro de las letras desaparecía. A ojo, en la captura, se veía «algo
* más pálido»; en números, ilegible.
*/
for (const modo of ['oscuro', 'claro'] as const) {
test(`cada parada del oro del logo se lee en tema ${modo}`, async ({ page }) => {
await page.goto('/');
await page.evaluate((m) => {
document.documentElement.dataset.tema = m;
}, modo);
const peor = await page.evaluate(() => {
const marca = document.querySelector('header .marca')!;
const fondo = getComputedStyle(document.body).backgroundColor;
/*
* Las paradas se sacan recorriendo la cadena y contando paréntesis:
* `oklch(…)` y `color-mix(in oklab, …)` llevan comas dentro, así que
* partir por comas devuelve trozos que no son colores.
*/
const colores: string[] = [];
const gradiente = getComputedStyle(marca).backgroundImage;
/*
* Los nombres se buscan con un límite de palabra porque el navegador
* no devuelve lo que está escrito: en el tema claro resuelve los
* `color-mix()` a `oklab()`, y buscando «lab» sin límite se contaría
* dos veces cada uno.
*/
const empiezan = /\b(oklch|oklab|color-mix|rgba?|hsla?|lab|lch|color)\(/g;
for (let m = empiezan.exec(gradiente); m; m = empiezan.exec(gradiente)) {
let nivel = 0;
let i = m.index + m[1].length;
for (; i < gradiente.length; i++) {
if (gradiente[i] === '(') nivel++;
else if (gradiente[i] === ')' && --nivel === 0) break;
}
colores.push(gradiente.slice(m.index, i + 1));
empiezan.lastIndex = i + 1;
}
const pincel = document.createElement('canvas').getContext('2d')!;
const aRgb = (c: string) => {
pincel.fillStyle = c;
pincel.fillRect(0, 0, 1, 1);
const d = pincel.getImageData(0, 0, 1, 1).data;
return [d[0], d[1], d[2]];
};
const lum = (rgb: number[]) => {
const f = (v: number) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
};
return 0.2126 * f(rgb[0]) + 0.7152 * f(rgb[1]) + 0.0722 * f(rgb[2]);
};
const contraste = (c: string) => {
const [a, b] = [lum(aRgb(c)), lum(aRgb(fondo))].sort((m, n) => n - m);
return (a + 0.05) / (b + 0.05);
};
return {
cuantas: colores.length,
minimo: Math.min(...colores.map(contraste))
};
});
// Cinco paradas: si el degradado dejara de pintarse, esto lo dice.
expect(peor.cuantas).toBeGreaterThanOrEqual(3);
expect(peor.minimo).toBeGreaterThan(MINIMO);
});
}
test('todos los botones llevan la misma letra', async ({ page }) => {
// Dos botones juntos con distinto cuerpo se ven como un descuido, y así
// estaban: el dorado a 16px y el de al lado a 14.

@ -117,6 +117,18 @@
var(--c-accent-sombra) 100%
);
/*
* El mismo metal, pero para recortarlo sobre unas letras.
*
* El dorado hace dos trabajos y no puede ser el mismo valor en los dos: de
* relleno tiene que ser claro para que encima se lea el marino, y escrito
* tiene que ser hondo para leerse sobre el fondo. Sobre marino las cinco
* paradas de `--oro` valen —la peor da 5,4 de contraste—, así que aquí es
* literalmente el mismo. En claro no: ahí el brillo se queda en 1,26 y la
* marca desaparece, y por eso el tema claro lo redefine.
*/
--oro-escrito: var(--oro);
/*
* Dos radios y no más, para que nada se vea de otra familia:
*
@ -213,6 +225,27 @@
--c-accent-brillo: oklch(0.9 0.1 92);
--c-accent-sombra: oklch(0.6 0.12 74);
/*
* El oro escrito, sobre papel.
*
* Aquí la banda va al revés que en el botón: el punto más claro es el propio
* `--c-accent` y los extremos se oscurecen. Medido contra este fondo, el
* acento da 5,29 de contraste y las tres paradas del oro de relleno dan
* 1,26, 2,05 y 3,79 —las tres por debajo del mínimo—, así que un degradado
* que subiera del acento dejaría el centro de las letras ilegible.
*
* Sigue pareciendo metal porque lo que hace el efecto es la banda que cruza,
* no que llegue a brillar.
*/
--oro-escrito: linear-gradient(
166deg,
color-mix(in oklab, var(--c-accent) 68%, black) 0%,
color-mix(in oklab, var(--c-accent) 84%, black) 24%,
var(--c-accent) 46%,
color-mix(in oklab, var(--c-accent) 84%, black) 64%,
color-mix(in oklab, var(--c-accent) 68%, black) 100%
);
/* 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. */

@ -229,14 +229,51 @@
.marca {
margin-right: auto;
font-family: var(--font-display);
font-size: var(--fs-xl);
/* El siguiente peldaño de la escala, no un número a ojo: el nombre es lo
más grande de la barra y sigue midiendo lo que mide un título. */
font-size: var(--fs-2xl);
font-weight: 600;
line-height: 1.2;
line-height: 1.15;
letter-spacing: 0.01em;
color: var(--c-accent);
text-decoration: none;
}
/*
* El mismo oro de los botones, aquí recortado sobre las letras.
*
* Va dentro de un `@supports` y el color plano se queda fuera a propósito:
* `color: transparent` sin el recorte dejaría el nombre invisible, que es
* mucho peor que dejarlo dorado y liso. Es el mismo cuidado que tiene el
* botón, que guarda un color de reserva por si el degradado no llega a
* pintar.
*
* Las cinco paradas, el ángulo y el alto del fondo son los suyos, para que
* sea el mismo metal y no un dorado parecido. Y como en el botón, al pasar
* el puntero la banda de luz recorre las letras en vez de aclararlas.
*/
@supports (background-clip: text) or (-webkit-background-clip: text) {
.marca {
background-image: var(--oro-escrito);
background-size: 100% 240%;
background-position: 0 22%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
transition: background-position 0.6s var(--sal);
}
.marca:hover {
background-position: 0 78%;
}
}
@media (prefers-reduced-motion: reduce) {
.marca {
transition: none;
}
}
.nav ul {
display: flex;
gap: var(--sp-5);

Loading…
Cancel
Save

Powered by TurnKey Linux.