auth: footer band review fixes: one block, no pill or border, color-mix fallback, sRGB test
All checks were successful
Build pull request / Build pull request (pull_request) Successful in 2m45s
All checks were successful
Build pull request / Build pull request (pull_request) Successful in 2m45s
The first footer block had spliced into the sign-in card's rule (a Y-only mask duplicate that overrode the side fade, a pill radius and border on the footer); the footer is now one rule. The test mixes in encoded sRGB as Chrome does. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
56bc40abe6
commit
2e6d4d042a
3 changed files with 23 additions and 49 deletions
|
|
@ -12,7 +12,7 @@ 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, which gives less contrast over dark terrain than the linear mix the test
|
||||
* computes, so the value carries margin: measured on the drawn pixels (Chrome, GTX 1070), 0.75 gave 4.49 at Dusk.
|
||||
* (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.
|
||||
*/
|
||||
export const FOOTER_TINT = 0.82;
|
||||
|
|
|
|||
|
|
@ -162,17 +162,28 @@ describe('regions that sit on the plate', () => {
|
|||
});
|
||||
|
||||
describe('the sign-in footer band', () => {
|
||||
// The footer line is the plate's ink on a band that mixes FOOTER_TINT of the plate's fill over the blurred scene.
|
||||
// The scene behind it is the terrain: as dark as 0, as light as the sky state's lightest backdrop. Ink contrast is
|
||||
// lowest at one of those two ends, so both are checked.
|
||||
const tinted = (fill: string, behind: number) =>
|
||||
FOOTER_TINT * luminance(fill) + (1 - FOOTER_TINT) * behind;
|
||||
// The footer line is the plate's ink on a band that mixes FOOTER_TINT of the plate's fill over the blurred scene,
|
||||
// as the browser does: per channel, in encoded sRGB (not in linear light, which flatters dark scenes). The scene
|
||||
// behind it is the terrain, taken as grey: as dark as 0, as light as the sky state's lightest backdrop. Ink
|
||||
// contrast is lowest at one of those two ends, so both are checked.
|
||||
const encode = (l: number) => (l <= 0.0031308 ? 12.92 * l : 1.055 * l ** (1 / 2.4) - 0.055);
|
||||
const channels = (hex: string) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
||||
const hexOf = (c: number[]) =>
|
||||
`#${c
|
||||
.map((v) =>
|
||||
Math.round(v * 255)
|
||||
.toString(16)
|
||||
.padStart(2, '0')
|
||||
)
|
||||
.join('')}`;
|
||||
const bandOver = (fill: string, behind: number) =>
|
||||
hexOf(channels(fill).map((f) => FOOTER_TINT * f + (1 - FOOTER_TINT) * encode(behind)));
|
||||
it.each(fixed)(
|
||||
'%s: the ink clears AA over the darkest and the lightest scene behind it',
|
||||
(state, id) => {
|
||||
const { Other, OtherText } = SKY[id].bubbles;
|
||||
[0, skyBackdrop(state).lightest].forEach((behind) => {
|
||||
expect(against(OtherText, tinted(Other, behind))).toBeGreaterThanOrEqual(AA);
|
||||
expect(contrast(OtherText, bandOver(Other, behind))).toBeGreaterThanOrEqual(AA);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -92,57 +92,20 @@ globalStyle(`${scene} ${AuthCardContent}`, {
|
|||
|
||||
// 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
|
||||
// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it.
|
||||
// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts.
|
||||
// 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.
|
||||
const FADE_Y =
|
||||
'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)';
|
||||
const FADE_X =
|
||||
'linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent)';
|
||||
const tint = `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${FOOTER_TINT * 100}%, transparent)`;
|
||||
globalStyle(`${scene} ${AuthFooter}`, {
|
||||
paddingTop: config.space.S500,
|
||||
paddingBottom: config.space.S500,
|
||||
backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${
|
||||
FOOTER_TINT * 100
|
||||
}%, transparent)`,
|
||||
backgroundColor: ['var(--sky-bubble, #0d1a26)', tint],
|
||||
backdropFilter: 'blur(12px)',
|
||||
WebkitBackdropFilter: 'blur(12px)',
|
||||
maskImage: `${FADE_Y}, ${FADE_X}`,
|
||||
WebkitMaskImage: `${FADE_Y}, ${FADE_X}`,
|
||||
maskComposite: 'intersect',
|
||||
WebkitMaskComposite: 'source-in',
|
||||
color: 'var(--sky-bubble-text, #b7c7d9)',
|
||||
borderRadius: config.radii.R500,
|
||||
// A faint inner edge, so a pane this clear still reads as a surface.
|
||||
borderColor: 'rgba(183, 199, 217, 0.14)',
|
||||
});
|
||||
globalStyle(`${scene} body:not(.gamecult-theme) ${AuthCard}`, {
|
||||
vars: { '--tc-link': '#99c7ff' },
|
||||
});
|
||||
globalStyle(`${scene} ${AuthHeader}`, {
|
||||
backgroundColor: 'transparent',
|
||||
});
|
||||
globalStyle(`${scene} ${AuthCardContent}`, {
|
||||
paddingTop: config.space.S500,
|
||||
paddingBottom: config.space.S400,
|
||||
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
|
||||
// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it.
|
||||
// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts.
|
||||
globalStyle(`${scene} ${AuthFooter}`, {
|
||||
paddingTop: config.space.S500,
|
||||
paddingBottom: config.space.S500,
|
||||
backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${
|
||||
FOOTER_TINT * 100
|
||||
}%, transparent)`,
|
||||
backdropFilter: 'blur(12px)',
|
||||
WebkitBackdropFilter: 'blur(12px)',
|
||||
maskImage:
|
||||
'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)',
|
||||
WebkitMaskImage:
|
||||
'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)',
|
||||
color: 'var(--sky-bubble-text, #b7c7d9)',
|
||||
});
|
||||
globalStyle(`${scene} ${AuthFooter} a`, {
|
||||
|
|
|
|||
Loading…
Reference in a new issue