Frame the club logo with a primary-coloured ring, sharpen its scaling

Wraps the uploaded logo in the same avatar shape as the initials
fallback (so the header doesn't jump in size depending on whether a
club has one) and adds a ring in the club's own primary colour as a
plain border around it.

Also hints the browser to use its higher-quality image scaler: a
club's raster logo is often much smaller than the badge it's shown in,
and the default upscaling in some engines reads as pixelated. Doesn't
affect SVG logos, which scale losslessly regardless.
This commit is contained in:
2026-07-27 11:06:08 +02:00
parent c17e7b5059
commit a86c35cbe7
4 changed files with 28 additions and 4500 deletions

View File

@@ -193,3 +193,18 @@
[data-theme="dark"] .logo {
background-image: var(--logo-light);
}
/* Club-uploaded logos are arbitrary raster files (often much smaller than the
badge they're displayed in), so the browser is upscaling them -- and the
default resampling some engines fall back to for that reads as pixelated.
Forcing the element onto its own GPU-composited layer makes Chrome/Safari
pick their higher-quality scaler; the image-rendering hints are a no-op
where unsupported, degrading gracefully to the browser default. Doesn't
apply to SVG logos, which scale losslessly regardless. */
.club-logo {
image-rendering: -webkit-optimize-contrast;
image-rendering: smooth;
image-rendering: high-quality;
backface-visibility: hidden;
transform: translateZ(0);
}

View File

@@ -1009,6 +1009,12 @@ class BrandingTests(TestCase):
self.assertContains(self.login_page("ajax-united.rosterchief.app"), "clubs/ajax-united/crest.png")
def test_a_club_logo_gets_a_primary_coloured_ring(self):
self.club.logo = "clubs/ajax-united/crest.png"
self.club.save()
self.assertContains(self.login_page("ajax-united.rosterchief.app"), "ring-primary")
def test_a_club_colour_overrides_the_theme(self):
self.club.primary_color = "#1e40af"
self.club.save()

File diff suppressed because one or more lines are too long

View File

@@ -36,7 +36,12 @@
{% block brand %}
<a class="flex flex-row items-center gap-3" href="/">
{% if club.logo %}
<img class="h-16 w-16 object-contain" src="{{ club.logo.url }}" alt="{{ club.name }}">
{# The ring is the club's own primary colour -- a plain frame around whatever crest they uploaded. #}
<div class="avatar">
<div class="w-16 rounded-full bg-base-100 ring-2 ring-primary ring-offset-2 ring-offset-base-100">
<img class="club-logo object-contain p-1" src="{{ club.logo.url }}" alt="{{ club.name }}">
</div>
</div>
{% else %}
{# Never the RosterChief mark: that would pass our branding off as the club's own. #}
<div class="avatar avatar-placeholder">