Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
55 lines
2.1 KiB
TypeScript
55 lines
2.1 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { HERO_TAIL_SCREENS } from "./HeroField";
|
|
|
|
/*
|
|
* O mergulho do hero é uma conta que vive em dois lugares: o CSS (altura do hero e margem de Projetos) e o
|
|
* HeroField (de onde o progresso --hero-p sai). Se um mudar sem o outro, o progresso acaba antes ou depois de
|
|
* Projetos começar a subir e volta o tempo morto (ou a tela vazia) no fim do mergulho.
|
|
*/
|
|
const css = readFileSync(join(__dirname, "../../globals.css"), "utf8");
|
|
|
|
function diveBlock() {
|
|
const start = css.indexOf(".hero[data-dive] {");
|
|
expect(start).toBeGreaterThan(-1);
|
|
return css.slice(start, start + 6000);
|
|
}
|
|
|
|
describe("hero dive (globals.css <-> HeroField)", () => {
|
|
it("the hero is HERO_TAIL_SCREENS screens taller than the dive", () => {
|
|
const height = /height: calc\((\d+)svh \+ var\(--hero-dive\)\)/.exec(
|
|
diveBlock(),
|
|
);
|
|
expect(height).not.toBeNull();
|
|
expect(Number(height?.[1]) / 100).toBe(HERO_TAIL_SCREENS);
|
|
});
|
|
|
|
it("Projects rises over the stage by the tail minus the pinned stage (one screen)", () => {
|
|
const margin = /margin-top: -(\d+)svh/.exec(diveBlock());
|
|
expect(margin).not.toBeNull();
|
|
expect(Number(margin?.[1]) / 100).toBeCloseTo(HERO_TAIL_SCREENS - 1);
|
|
});
|
|
|
|
it("the last caption stays on stage until Projects covers it (its end is past the end of the dive)", () => {
|
|
const last =
|
|
/li:nth-child\(3\) \{\s*--from: ([\d.]+);\s*--to: ([\d.]+);/.exec(
|
|
diveBlock(),
|
|
);
|
|
expect(last).not.toBeNull();
|
|
expect(Number(last?.[2])).toBeGreaterThan(1);
|
|
});
|
|
|
|
it("each caption starts before the previous one is gone far enough that the gap between them is short", () => {
|
|
const ranges = [
|
|
...diveBlock().matchAll(
|
|
/li:nth-child\((\d)\) \{\s*--from: ([\d.]+);\s*--to: ([\d.]+);/g,
|
|
),
|
|
].map((match) => [Number(match[2]), Number(match[3])]);
|
|
expect(ranges).toHaveLength(3);
|
|
for (let i = 1; i < ranges.length; i++) {
|
|
// sem texto entre uma legenda e a seguinte: no máximo 0,1 de progresso (~100 px de rolagem)
|
|
expect(ranges[i][0] - ranges[i - 1][1]).toBeLessThanOrEqual(0.1);
|
|
}
|
|
});
|
|
});
|