Fix mismatched fallback text colour on the initials badge/button

The initials badge and reset button hardcoded two independent
fallback literals -- a background (#ec4899 / #0ea5e9) and a text
colour (#ffffff) -- that were only ever chosen together for a club's
own colour via Club._content_color_for. #ffffff on #ec4899 or
#0ea5e9 actually contrasts worse than black by the same WCAG formula
the app already uses elsewhere (verified: 6.4:1 vs 3.3:1, and 7.6:1
vs 2.8:1). Added a contrast_color filter so the text colour is always
derived from whatever background hex is actually in play -- real
club colour or fallback alike -- instead of a second, independently
guessed literal that can silently drift out of sync with the first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 09:50:07 +02:00
parent 9901a90266
commit 67841491a6
7 changed files with 72 additions and 4 deletions

View File

@@ -318,6 +318,27 @@ class PasswordResetEmailTests(TestCase):
self.assertIn("AU", with_club) # initials fallback: no logo set
self.assertIn("https://ajax-united.rosterchief.app/accounts/password/reset/key/abc-def/", with_club)
def test_the_initials_badge_text_contrasts_against_the_fallback_colour(self):
# Club has no secondary_color, so the badge falls back to #ec4899 --
# black text (contrast_color("#ec4899")) reads far better on it than
# the white the template used to hardcode. See
# club/templatetags/club_email.py::contrast_color.
club = Club.objects.create(name="Ajax United", slug="ajax-united")
rendered = render_to_string("account/email/password_reset_key_message.html", {"club": club, "password_reset_url": "https://ajax-united.rosterchief.app/accounts/password/reset/key/abc-def/"})
self.assertIn("background-color:#ec4899", rendered)
self.assertIn("color:#000000", rendered)
def test_the_reset_button_text_contrasts_against_the_no_club_fallback_colour(self):
# No club at all (the base-domain flow) -- the button falls back to
# #0ea5e9, RosterChief's own sky blue, which also needs black text
# for a passing contrast ratio, not the white previously hardcoded.
rendered = render_to_string("account/email/password_reset_key_message.html", {"club": None, "password_reset_url": "https://rosterchief.app/accounts/password/reset/key/abc-def/", "current_site": None})
self.assertIn("background-color:#0ea5e9", rendered)
self.assertIn("color:#000000", rendered)
without_club = render_to_string("account/email/password_reset_key_message.html", {"club": None, "password_reset_url": "https://rosterchief.app/accounts/password/reset/key/abc-def/", "current_site": SimpleNamespace(name="rosterchief.app")})
self.assertIn("Roster", without_club)
self.assertIn("Chief", without_club)