Merge pull request 'auth: test the emitted footer rule; wrap comment; correct the terrain luminance figure' (#62) from void/footer-lows into dev
Some checks failed
Rat dev preview / Build dev preview (push) Has been cancelled

This commit is contained in:
Void 2026-11-10 04:04:14 +00:00
commit 7494591624
5 changed files with 84 additions and 6 deletions

2
package-lock.json generated
View file

@ -86,6 +86,7 @@
"@types/ua-parser-js": "0.7.36",
"@typescript-eslint/eslint-plugin": "5.46.1",
"@typescript-eslint/parser": "5.46.1",
"@vanilla-extract/integration": "6.5.0",
"@vitejs/plugin-react": "4.2.0",
"buffer": "6.0.3",
"cz-conventional-changelog": "3.3.0",
@ -5095,6 +5096,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-6.5.0.tgz",
"integrity": "sha512-E2YcfO8vA+vs+ua+gpvy1HRqvgWbI+MTlUpxA8FvatOvybuNcWAY0CKwQ/Gpj7rswYKtC6C7+xw33emM6/ImdQ==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.20.7",
"@babel/plugin-syntax-typescript": "^7.20.0",

View file

@ -114,6 +114,7 @@
"@types/ua-parser-js": "0.7.36",
"@typescript-eslint/eslint-plugin": "5.46.1",
"@typescript-eslint/parser": "5.46.1",
"@vanilla-extract/integration": "6.5.0",
"@vitejs/plugin-react": "4.2.0",
"buffer": "6.0.3",
"cz-conventional-changelog": "3.3.0",

View file

@ -11,8 +11,10 @@ export const SKY_PLATE_FALLBACK = {
/**
* The sign-in footer's tint: how much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind the
* footer line. The footer is a blurred, tinted band with soft edges, not a plate: the scene shows through it. The
* footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1
* (skyTheme.test.ts). The browser mixes in sRGB, so the test does too (skyTheme.test.ts): over pure black 0.80 gives 4.36 and 0.82 gives 4.55, and
* the darkest terrain drawn is 0.0045, not black; measured on the drawn pixels the footer clears 6.2.
* footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1. The browser
* mixes in sRGB, so the test does too (skyTheme.test.ts): over pure black 0.80 gives 4.36 and 0.82 gives 4.55. The
* terrain is not black: in the shots at 390x844 (terrain scene, tier low) the darkest pixel in the footer's region is
* 0.0203 at Dusk, 0.0936 at Day (dark ink) and 0.0052 at Night (light ink). Measured on the drawn pixels with the band
* on, the worst link contrast is 5.0 at Dusk, 5.7 at Day and 11.2 at Night.
*/
export const FOOTER_TINT = 0.82;

View file

@ -0,0 +1,72 @@
import { describe, expect, it } from 'vitest';
import zlib from 'zlib';
import { compile, processVanillaFile } from '@vanilla-extract/integration';
import { AuthFooter } from './styles.css';
// The CSS styles.css.ts emits for the sign-in footer, read back from vanilla-extract's own compile.
const emitted = async () => {
const filePath = `${process.cwd()}/src/app/pages/auth/styles.css.ts`;
const { source } = await compile({ filePath, cwd: process.cwd(), identOption: 'debug' });
const code = await processVanillaFile({ source, filePath, identOption: 'debug' });
const encoded = (code.match(/source=#([A-Za-z0-9+/=]+)/) as RegExpMatchArray)[1];
return zlib.gunzipSync(Buffer.from(encoded, 'base64')).toString();
};
const declarations = (css: string, selector: string) =>
[...css.matchAll(/^([^{}\n]+) \{\n([^}]*)\}/gm)]
.filter((m) => m[1] === selector)
.map((m) =>
m[2]
.split('\n')
.map((l) => l.trim().replace(/;$/, ''))
.filter(Boolean)
);
describe('the sign-in footer band, as emitted', () => {
const over = `:root[data-scene] .${AuthFooter}`;
it('is one rule over the scene, so no later rule can override its mask', async () => {
expect(declarations(await emitted(), over)).toHaveLength(1);
});
it('puts the plate fill before the color-mix tint, so a browser without color-mix keeps the fill', async () => {
const [rule] = declarations(await emitted(), over);
const fills = rule.filter((d) => d.startsWith('background-color:'));
expect(fills).toHaveLength(2);
expect(fills[0]).toBe('background-color: var(--sky-bubble, #0d1a26)');
expect(fills[1]).toContain('color-mix(');
});
it('blurs what is behind it by a real radius', async () => {
const [rule] = declarations(await emitted(), over);
const blur = rule.find((d) => d.startsWith('backdrop-filter:')) ?? '';
const radius = Number(blur.match(/^backdrop-filter: blur\((\d+(?:\.\d+)?)px\)$/)?.[1]);
expect(radius).toBeGreaterThan(0);
});
it('fades along both axes over a soft ramp, not a hard edge', async () => {
const [rule] = declarations(await emitted(), over);
const mask = rule.find((d) => d.startsWith('mask-image:')) ?? '';
expect(rule).toContain('mask-composite: intersect');
['bottom', 'right'].forEach((axis) => {
const ramp = mask.match(
new RegExp(
`linear-gradient\\(to ${axis}, transparent, #000 (\\d+)px, #000 calc\\(100% - (\\d+)px\\), transparent\\)`
)
);
expect(ramp).not.toBeNull();
// Each end ramps from clear to opaque over at least 8px.
expect(Number(ramp?.[1])).toBeGreaterThanOrEqual(8);
expect(Number(ramp?.[2])).toBeGreaterThanOrEqual(8);
});
});
it('has no border or radius, in the scene rule or in the base footer rule', async () => {
const css = await emitted();
const [rule] = declarations(css, over);
const base = declarations(css, `.${AuthFooter}`);
expect(base).toHaveLength(1);
[rule, base[0]].forEach((r) => {
expect(r.filter((d) => /^(border|outline)/.test(d))).toEqual([]);
});
});
});

View file

@ -90,10 +90,11 @@ globalStyle(`${scene} ${AuthCardContent}`, {
gap: config.space.S500,
});
// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where
// the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's
// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 at dusk), where the
// sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's
// ink, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. A browser
// without color-mix gets the plate's opaque fill, as the chat bubbles do (message/layout/layout.css.ts). The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts.
// without color-mix gets the plate's opaque fill, as the chat bubbles do (message/layout/layout.css.ts). The tint
// (FOOTER_TINT) is checked against the ink in skyTheme.test.ts, the emitted rule in footerBand.test.ts.
const FADE_Y =
'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)';
const FADE_X =