From 5e0a16068f702940a6252fb2163ecca9d848881c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 23:48:20 +0000 Subject: [PATCH 1/3] Drop the hover colours on the CTA band buttons Both hovers were doing harm rather than nothing. The secondary is an opaque brand-700 fill, and the band behind it is a gradient travelling brand-500 down to brand-900. The buttons sit low in that travel, where the ground is already near brand-700, so a brand-800 hover landed the fill within 1.08:1 of the colour behind it. The button did not darken on hover, it disappeared. Measured across all eight palettes: 1.49 at rest falling to 1.08 hovered, worst case, and no palette better than 1.10. The primary is near-white on a dark field and had nowhere brighter to go. Its hover mixed the foreground with white, which inside .invert-section means mixing a near-white with white: the fill moved by 1.7 per cent. There was a hover rule; it did nothing visible. The pair also wanted opposite directions to read at all here - lighter for the dark secondary, darker for the light primary - which no single hover convention gives you. Hover is carried by the arrow instead. Both buttons are .group and both arrows match `.group:hover [data-icon="arrow-right"]`, shifting 4px; a press is carried by `active:scale-[0.98]` on Button, and keyboard focus by the ring, which was checked against a screenshot rather than a computed style. Deleting the two rules is enough on its own. The rest-state declarations carry !important, so Button's own `hover:bg-foreground/90` and `hover:bg-secondary` cannot come through behind them. Verified on all eight palettes in light and dark: both buttons hold their colour under the cursor. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BwJB1u6U3vdEZ9LrHRebST --- src/styles/global.css | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/styles/global.css b/src/styles/global.css index 71dee0e7a..c87e28704 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1309,18 +1309,21 @@ } /* CTA primary button on mobile / coarse-pointer CTA sections. - brand-700 fill / brand-800 hover in both modes: the lightest brand step - that holds white label text at WCAG AA (>=4.5:1) across every theme. */ + brand-700 fill in both modes: the lightest brand step that holds white + label text at WCAG AA (>=4.5:1) across every theme. + + No hover colour. On the CTA band this button sits on a gradient running + brand-500 down to brand-900, and the buttons land low in that travel where + the ground is already near brand-700. A darker hover put the fill within + 1.1:1 of the ground behind it, so the button dissolved rather than + responded. Hover is carried by the arrow instead — it shifts 4px — and a + press by `active:scale-[0.98]` on Button itself. */ .cta-btn-brand { background-color: var(--color-brand-700) !important; background-image: none !important; color: white !important; border-color: transparent !important; } -.cta-btn-brand:hover { - background-color: var(--color-brand-800) !important; - background-image: none !important; -} /* Primary button inside an inverted section. The primary variant is `bg-foreground text-background`, but the Button's own @@ -1379,10 +1382,18 @@ fails on emerald, teal, cyan and sky — the same four the tag pill had to move past. - Secondary: `.cta-btn-brand`, unchanged — an opaque brand fill with an - opaque brand hover and no border. `.hero-btn-brand` would look the same at - rest but fades to `opacity: 0.85` on hover, which makes the whole button - translucent and shows the grid through it. */ + Neither button takes a hover colour. This one is already near-white on a + dark field, so it has nowhere brighter to go: its hover mixed the + foreground with white and moved the fill by under 2%, which read as no + response at all. The pair also needed opposite directions to be legible + here — lighter for the dark secondary, darker for this one — which one + hover convention cannot give. Hover is carried by the arrow instead, 4px on + both buttons, and a press by `active:scale-[0.98]` on Button itself. + + Secondary: `.cta-btn-brand`, an opaque brand fill with no border. + `.hero-btn-brand` would look the same at rest but fades to `opacity: 0.85` + on hover, which makes the whole button translucent and shows the grid + through it. */ .cta-band.invert-section .btn-primary { background-color: var(--color-foreground) !important; background-image: linear-gradient( @@ -1393,10 +1404,6 @@ border: 1px solid var(--color-foreground-secondary) !important; color: var(--color-brand-700) !important; } -.cta-band.invert-section .btn-primary:hover { - background-image: none !important; - background-color: color-mix(in oklch, var(--color-foreground) 90%, white) !important; -} .hero-btn-learn { background-color: transparent !important; From e839ed07e2fd976d412eb6f1c39fd4025d6f43c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 00:01:14 +0000 Subject: [PATCH 2/3] Give the band's secondary button a visible edge Its brand-700 fill sits on a field travelling brand-500 down to brand-900, and the buttons land where the ground is already near brand-700. The fill was 1.47:1 to 1.61:1 against what sits behind it, so the button's shape barely read as a control - short of the 3:1 WCAG 1.4.11 asks for identifying one. The fill could not fix it. brand-600 reaches only 2.04 to 2.34 against the band; brand-500 clears 3:1 in five of the eight palettes and drops the white label to between 2.82 and 4.52, under the 4.5 it needs. brand-700 was chosen in the first place because white sits at 6.03 to 8.43 on it. So the edge does the work instead. A 1px white line at 35 per cent composites over the existing fill: no colour changes, the button only gains a boundary. Sampled off the rendered page, the edge reads 3.21:1 to 3.45:1 against the band across all eight palettes. 45 per cent is the same line with more room, at 3.91 worst, if a future palette needs it. The primary already carries a 1px border, so the pair now match. Scoped to the band. `.cta-btn-brand` is used on light sections too, including the contact form's submit button, where a white edge would be wrong. Three of the eight bands carry only a primary button and are unaffected. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BwJB1u6U3vdEZ9LrHRebST --- src/styles/global.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/styles/global.css b/src/styles/global.css index c87e28704..bec077b48 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1405,6 +1405,27 @@ color: var(--color-brand-700) !important; } +/* The secondary's edge, on the band only. + + Its brand-700 fill sits on a field travelling brand-500 to brand-900, and + the buttons land where the ground is already near brand-700 — so the fill + is 1.5:1 against what is behind it and the button's shape barely reads. + WCAG 1.4.11 asks 3:1 to identify a control, which the fill cannot give + without breaking something else: brand-500 would clear the boundary in only + five of the eight palettes and would drop the white label to 2.8:1, and + brand-600 clears neither. + + A 1px white line at 35% composites over the fill, so no colour changes — + the button only gains an edge. Worst of the eight is 3.21:1 (teal), which + clears the bar with little to spare; 45% is the same line at 3.91 if a + future palette needs the room. + + Scoped to the band because `.cta-btn-brand` is used on light sections too, + including the contact form's submit, where a white edge would be wrong. */ +.cta-band.invert-section .cta-btn-brand { + border-color: rgb(255 255 255 / 0.35) !important; +} + .hero-btn-learn { background-color: transparent !important; background-image: none !important; From 0b7ce42122d97013e44b863f4469540a40cf993f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 15 Sep 2026 00:22:39 +0000 Subject: [PATCH 3/3] Add the travelling arrow to the blog CTA button Every other call to action in the theme ends in an arrow that shifts 4px under the cursor. The blog band's button carried only the GitHub mark in front of its label, so it was the one button on the site that did not respond to a hover. The GitHub mark stays where it is; the arrow goes after the text. No CSS was needed - Icon renders `data-icon="arrow-right"`, which the existing `.group:hover [data-icon="arrow-right"]` rule already matches. Both variants take it, so the button behaves the same either side of the glitch breakpoint: the band below 1024px and the LetterGlitch version above it. Checked on the blog index, the tag pages and a post at both widths - the arrow moves, the GitHub mark holds still. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BwJB1u6U3vdEZ9LrHRebST --- src/components/blog/BlogCta.astro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/blog/BlogCta.astro b/src/components/blog/BlogCta.astro index 3f4631387..64295c7e8 100644 --- a/src/components/blog/BlogCta.astro +++ b/src/components/blog/BlogCta.astro @@ -71,6 +71,7 @@ const repoUrl = > {t('blog.cta.button', locale)} + @@ -94,6 +95,7 @@ const repoUrl = > {t('blog.cta.button', locale)} +