Fix white-on-red contrast: deepen the button fill to #d42f46 - #20
Merged
Conversation
Hack Club red (#ec3750) measures 4.02:1 against white, below WCAG AA's
4.5:1 for normal text. WCAG's large-text exemption starts at 18.66px bold
or 24px regular, which no button in Attend reaches, so no type change can
fix this — the fill has to move.
Wherever white text sits on a red surface, the resting fill is now
--accent-strong (#d42f46, 4.89:1), hovering to #b82840 (6.15:1). The brand
red keeps every use that carries no text on it: unread dots, the wallet
pass bar, the Turbo progress bar, borders, icon fills, and /10 tints. The
two reds are one perceptual step apart.
themes.css maps the new utilities to the same semantic vars the old ones
used (.bg-[#d42f46] -> var(--accent), .hover:bg-[#b82840] ->
var(--accent-strong)), so the seven non-light themes render identically to
before. Only the default light theme changes.
Two bugs fixed on the way:
- Three buttons in admin/events (index, show, _form) had
`bg-[#ec3750] hover:bg-[#ec3750]` — a no-op hover with transition-colors
doing nothing. They now have a real hover state.
- Eight sites used `hover:bg-[#d63146]`, a one-off hex themes.css never
remapped, so they broke out of theming on hover. All now use the
mapped token.
Also adds PRODUCT.md and DESIGN.md, which record the product context and
the incumbent visual system this rule (The Deepened Red Rule) lives in.
Known gaps, deliberately out of scope:
- The decorative wallet-pass mock on home#index (10px text-white/80 on
red, plus initials on a red->orange gradient) still fails.
- Dark theme resolves text-white to --bg-elev (#161922), giving 4.37:1
on --accent. Same class of problem, separate fix.
This was referenced Aug 13, 2026
leowilkin
added a commit
that referenced
this pull request
Aug 13, 2026
Both guardian surfaces — the invite wizard and the portal center — were generic Tailwind blue/gray, wrapped in fully branded Hack Club chrome. For most parents this is the only Attend screen they ever see, and it is where they hand over their child's medical and emergency details. DESIGN.md names this directly: the blue running through the onboarding wizard and guardian portal is "drift from a Tailwind default, not a decision". Adds a portal-* component layer in app/assets/tailwind/application.css. Every rule resolves through the semantic tokens in themes.css per The No Literal Hex Rule; there are no hexes in the templates. It follows the Two- Radius Rule (6px controls, 8px containers), the Flat-By-Default Rule (border and tonal step, no resting shadow), the Fourteen Rule, Full-Width-Then-Auto on every primary action, and the documented button, input, and badge specs. The Deepened Red Rule is applied in all eight themes rather than only light. The rule names --accent-strong, but that token lightens in the dark themes (#ff5670), where white on it is 3.08:1 — worse than the red it replaces. PR #20 avoids this because themes.css maps its .bg-[#d42f46] utility back to --accent outside light; a CSS layer has no such escape. Deriving from --accent lands on #d43248 in light, indistinguishable from the rule's #d42f46, and clears AA everywhere. Hover lands on --accent-deep. Flow and hierarchy: - The overview had three overlapping progress representations (bar, checklist, footer callout). One spine now: checklist, slim meter, single next-action CTA. - Step names now say whose data they hold: "Participant Info"/"Details" -> "Participant details"/"Your details", each with a one-line description. - step.html.erb had no position indicator. It has one now — a rail on desktop, a compact "step 2 of 4" meter below sm. Correctness and accessibility: - ~40 travel-section controls had visible labels with no programmatic association, plus two colliding ids between the train and bus blocks. - Every consent form rendered checkboxes with a duplicate consent_agreed id, so clicking a label focused the wrong control. - Guardian fields had no autocomplete attributes at all. - white on --success is 1.86:1, so filled "done" markers deepen it; --text-muted is 3.16:1 on white, so secondary copy moved to --text-soft. Every token pair verified against WCAG AA in light and dark. - f.submit -> button, since themes.css's unlayered input rule makes a styled input[type=submit] impossible in non-light themes. Removes five unreachable views (consents, details, emergency, _wizard_nav, complete): no route, no action, and three called guardian_step_path, a route helper that does not exist — they would have raised if anything rendered them. Also drops two console.log calls and a javascript:history.back() link. Adds shared/_portal_masthead: the app header only renders for signed-in users, so guardians saw an unbranded form above a Hack Club footer. Spec assertions updated for the new copy. Sentence case throughout, matching the newer portal center; no factual claims changed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Hack Club red (
#ec3750) measures 4.02:1 against white — below WCAG AA's 4.5:1 for normal text. WCAG's large-text exemption (3:1) starts at 18.66px bold or 24px regular, which no button in Attend reaches, so no type change fixes this. The fill has to move.Wherever white text sits on a red surface, the resting fill is now
--accent-strong(#d42f46, 4.89:1), hovering to#b82840(6.15:1). The brand red keeps every use that carries no text on it: unread dots, the wallet-pass bar, the Turbo progress bar, borders, icon fills, and/10tints. The two reds are one perceptual step apart — the change is invisible at a glance and measurable to a contrast checker.112 fills across 84 view files.
Theming
themes.cssmaps the new utilities to the same semantic vars the old ones used:.bg-[#d42f46]→var(--accent).hover:bg-[#b82840]→var(--accent-strong)So the seven non-light themes render identically to before. Only the default light theme changes.
Two bugs fixed on the way
admin/events(index, show,_form) hadbg-[#ec3750] hover:bg-[#ec3750]— a no-op hover withtransition-colorsdoing nothing. They now have a real hover state.hover:bg-[#d63146], a one-off hexthemes.cssnever remapped, so they broke out of theming on hover. All now use the mapped token.Docs
Adds
PRODUCT.mdandDESIGN.mdrecording the product context and the incumbent visual system, where this rule lives as The Deepened Red Rule, plus the.impeccable/design.jsonsidecar.Verification
Known gaps, deliberately out of scope
home#index(10pxtext-white/80on red, plus initials on a red→orange gradient at ~2.2:1) still fails. It's illustration, not interactive UI.text-whiteto--bg-elev(#161922), giving 4.37:1 on--accent. Same class of problem, separate fix.