Rework the Live preview to match D9's actual screenshot
Replaces the earlier sidebar+small-phone mock with D9's real layout: App/ Website/Email tabs (App is the only one with real content, so these are static labels not a working control), a full-size phone mock, and a public- site mock, plus the "Where the brand shows up" list. The phone's "Next up" card now uses primary, not secondary -- it's a notice, not a call to action, so it shouldn't compete visually with an actual accent-coloured button. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -1804,30 +1804,29 @@
|
|||||||
background: var(--color-paper);
|
background: var(--color-paper);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-frame {
|
/* Live preview tabs -- App is the only one with real content behind it
|
||||||
display: flex;
|
(Website/Email aren't built), so these are static labels, not a real
|
||||||
height: 220px;
|
tab control. */
|
||||||
overflow: hidden;
|
.preview-tab {
|
||||||
border: 1px solid var(--color-line);
|
padding: 0.3125rem 0.625rem;
|
||||||
border-radius: var(--radius-box);
|
font-family: var(--font-display);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-sidebar {
|
.preview-tab-active {
|
||||||
display: flex;
|
|
||||||
width: 40%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
background: var(--color-ink);
|
background: var(--color-ink);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
transition: background-color 0.15s ease, color 0.15s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-crest {
|
.preview-crest {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
@@ -1837,65 +1836,6 @@
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-club-name {
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.6875rem;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-nav-item {
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
padding: 0.375rem 0.5rem;
|
|
||||||
font-size: 0.6875rem;
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-nav-active {
|
|
||||||
opacity: 1;
|
|
||||||
font-weight: 700;
|
|
||||||
background: var(--color-club);
|
|
||||||
color: #fff;
|
|
||||||
transition: background-color 0.15s ease, color 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-panel {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.625rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background: var(--color-paper);
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-panel-heading {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--color-ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-button {
|
|
||||||
align-self: flex-start;
|
|
||||||
border: none;
|
|
||||||
border-radius: var(--radius-box);
|
|
||||||
padding: 0.375rem 0.875rem;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 0.6875rem;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
background: var(--color-club);
|
|
||||||
color: #fff;
|
|
||||||
cursor: default;
|
|
||||||
transition: background-color 0.15s ease, color 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-skeleton-line {
|
.preview-skeleton-line {
|
||||||
height: 0.5rem;
|
height: 0.5rem;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@@ -1906,11 +1846,13 @@
|
|||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The mobile app mock -- not built yet, but its header/role-switcher take
|
/* The mobile app mock -- not built yet, but its header/role-switcher/
|
||||||
the same primary/secondary colours the sidebar mock above does, so this
|
"Next up" card take the same primary/secondary colours a real one
|
||||||
preview stays accurate once it exists rather than needing a rework. */
|
would, so this preview stays accurate once it exists. Sized closer to
|
||||||
.preview-phone {
|
an actual phone aspect ratio (tall, mostly-empty screen below the
|
||||||
width: 11rem;
|
first couple of cards) rather than a cropped sliver. */
|
||||||
|
.preview-phone-lg {
|
||||||
|
width: 12.5rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 5px solid var(--color-ink);
|
border: 5px solid var(--color-ink);
|
||||||
@@ -1918,6 +1860,10 @@
|
|||||||
background: var(--color-paper);
|
background: var(--color-paper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-phone-lg .preview-phone-body {
|
||||||
|
min-height: 15rem;
|
||||||
|
}
|
||||||
|
|
||||||
.preview-phone-header {
|
.preview-phone-header {
|
||||||
padding: 0.75rem 0.625rem 0.625rem;
|
padding: 0.75rem 0.625rem 0.625rem;
|
||||||
background: var(--color-ink);
|
background: var(--color-ink);
|
||||||
@@ -1985,7 +1931,7 @@
|
|||||||
.preview-phone-card {
|
.preview-phone-card {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background: var(--color-club);
|
background: var(--color-ink);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
transition: background-color 0.15s ease, color 0.15s ease;
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
}
|
}
|
||||||
@@ -2021,4 +1967,82 @@
|
|||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The public-site mock -- same "not built yet, but coloured accurately"
|
||||||
|
reasoning as the phone mock above. */
|
||||||
|
.preview-website {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--color-line);
|
||||||
|
border-radius: 0.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.625rem 0.75rem;
|
||||||
|
background: var(--color-ink);
|
||||||
|
color: #fff;
|
||||||
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-name {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-nav {
|
||||||
|
margin-left: auto;
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 0.625rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-on-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-join {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.1875rem 0.4375rem;
|
||||||
|
background: var(--color-club);
|
||||||
|
color: #fff;
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 0.625rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-hero {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.875rem 0.75rem;
|
||||||
|
background: var(--color-subhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-website-headline {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: 1.375rem;
|
||||||
|
line-height: 0.98;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-brand-dot {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 0.125rem;
|
||||||
|
background: var(--color-club);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-brand-dot-neutral {
|
||||||
|
background: var(--color-dim);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,31 +115,17 @@
|
|||||||
<div class="xl:sticky xl:top-4 xl:self-start">
|
<div class="xl:sticky xl:top-4 xl:self-start">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<div class="flex items-center justify-between gap-2">
|
||||||
<div class="card-title">{% lucide "eye" size=16 %} {% trans "Live preview" %}</div>
|
<div class="card-title">{% lucide "eye" size=16 %} {% trans "Live preview" %}</div>
|
||||||
<p class="text-xs text-muted">{% trans "Where these colours actually show up -- updates as you edit, before you save anything." %}</p>
|
<div class="flex overflow-hidden rounded-md border border-line">
|
||||||
|
<span class="preview-tab preview-tab-active">{% trans "App" %}</span>
|
||||||
<div class="mt-2 font-display text-[11px] font-bold tracking-[.12em] text-muted uppercase">{% trans "Management app" %}</div>
|
<span class="preview-tab">{% trans "Website" %}</span>
|
||||||
<div class="preview-frame mt-1.5">
|
<span class="preview-tab">{% trans "Email" %}</span>
|
||||||
<div id="preview-sidebar" class="preview-sidebar">
|
|
||||||
<div class="preview-crest">{{ club.initials|default:"CL" }}</div>
|
|
||||||
<div class="preview-club-name">{{ club.name|default:_("Your club") }}</div>
|
|
||||||
<div class="preview-nav-item">{% trans "Overview" %}</div>
|
|
||||||
<div id="preview-nav-active" class="preview-nav-item preview-nav-active">{% trans "Members" %}</div>
|
|
||||||
<div class="preview-nav-item">{% trans "Calendar" %}</div>
|
|
||||||
</div>
|
|
||||||
<div class="preview-panel">
|
|
||||||
<div class="preview-panel-heading">{% trans "New member" %}</div>
|
|
||||||
<button type="button" id="preview-button" class="preview-button">{% trans "Save changes" %}</button>
|
|
||||||
<div class="preview-skeleton-line"></div>
|
|
||||||
<div class="preview-skeleton-line preview-skeleton-line-short"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 flex items-center justify-between gap-2">
|
<div class="mt-3 flex flex-wrap items-start gap-4">
|
||||||
<div class="font-display text-[11px] font-bold tracking-[.12em] text-muted uppercase">{% trans "Mobile app" %}</div>
|
<div class="preview-phone-lg">
|
||||||
<span class="badge badge-sm badge-ghost">{% trans "Coming soon" %}</span>
|
|
||||||
</div>
|
|
||||||
<div class="mt-1.5 preview-phone">
|
|
||||||
<div id="preview-phone-header" class="preview-phone-header">
|
<div id="preview-phone-header" class="preview-phone-header">
|
||||||
<div class="preview-phone-header-row">
|
<div class="preview-phone-header-row">
|
||||||
<span class="preview-crest preview-crest-sm">{{ club.initials|default:"CL" }}</span>
|
<span class="preview-crest preview-crest-sm">{{ club.initials|default:"CL" }}</span>
|
||||||
@@ -151,6 +137,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-phone-body">
|
<div class="preview-phone-body">
|
||||||
|
{# A notice, not a call to action -- primary (chrome), not secondary (accent), so it doesn't visually compete with an actual button. #}
|
||||||
<div id="preview-phone-card" class="preview-phone-card">
|
<div id="preview-phone-card" class="preview-phone-card">
|
||||||
<div class="preview-phone-eyebrow">{% trans "Next up" %}</div>
|
<div class="preview-phone-eyebrow">{% trans "Next up" %}</div>
|
||||||
<div class="preview-phone-title">{% trans "U16 vs Leuven" %}</div>
|
<div class="preview-phone-title">{% trans "U16 vs Leuven" %}</div>
|
||||||
@@ -163,8 +150,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 text-xs text-dim">
|
<div class="flex min-w-0 flex-1 flex-col gap-3">
|
||||||
{% trans "Never used for: status colours (paid/overdue, present/absent), table styling, or form fields -- those stay consistent for everyone regardless of club branding." %}
|
<div class="preview-website">
|
||||||
|
<div id="preview-website-header" class="preview-website-header">
|
||||||
|
<span class="preview-crest preview-crest-sm">{{ club.initials|default:"CL" }}</span>
|
||||||
|
<span class="preview-website-name">{{ club.name|default:_("Your club") }}</span>
|
||||||
|
<span class="preview-website-nav">{% trans "Teams · Club" %}</span>
|
||||||
|
<span id="preview-website-join" class="preview-website-join">{% trans "Join" %}</span>
|
||||||
|
</div>
|
||||||
|
<div class="preview-website-hero">
|
||||||
|
<div class="preview-website-headline">{% trans "#Fear the Sharks" %}</div>
|
||||||
|
<div class="preview-skeleton-line"></div>
|
||||||
|
<div class="preview-skeleton-line preview-skeleton-line-short"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-lg border border-line p-3">
|
||||||
|
<div class="mb-2 font-display text-[11px] font-bold tracking-[.12em] text-muted uppercase">{% trans "Where the brand shows up" %}</div>
|
||||||
|
<ul class="flex flex-col gap-1.5 text-[13px] text-slate">
|
||||||
|
<li class="flex items-center gap-2"><span id="preview-dot-1" class="preview-brand-dot"></span>{% trans "Sidebar, active nav item, primary buttons" %}</li>
|
||||||
|
<li class="flex items-center gap-2"><span id="preview-dot-2" class="preview-brand-dot"></span>{% trans "Public site header and join button" %}</li>
|
||||||
|
<li class="flex items-center gap-2"><span id="preview-dot-3" class="preview-brand-dot"></span>{% trans "Mobile app header and highlights" %}</li>
|
||||||
|
<li class="flex items-center gap-2"><span class="preview-brand-dot preview-brand-dot-neutral"></span>{% trans "Never: status colours, tables, form fields" %}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -218,17 +228,21 @@
|
|||||||
const primaryContent = contentColorFor(primary);
|
const primaryContent = contentColorFor(primary);
|
||||||
const secondaryContent = contentColorFor(secondary);
|
const secondaryContent = contentColorFor(secondary);
|
||||||
|
|
||||||
document.getElementById("preview-sidebar").style.background = primary;
|
|
||||||
document.getElementById("preview-sidebar").style.color = primaryContent;
|
|
||||||
document.getElementById("preview-nav-active").style.background = secondary;
|
|
||||||
document.getElementById("preview-nav-active").style.color = secondaryContent;
|
|
||||||
document.getElementById("preview-button").style.background = secondary;
|
|
||||||
document.getElementById("preview-button").style.color = secondaryContent;
|
|
||||||
|
|
||||||
document.getElementById("preview-phone-header").style.background = primary;
|
document.getElementById("preview-phone-header").style.background = primary;
|
||||||
document.getElementById("preview-phone-header").style.color = primaryContent;
|
document.getElementById("preview-phone-header").style.color = primaryContent;
|
||||||
document.getElementById("preview-phone-card").style.background = secondary;
|
// A notice, not a call to action -- primary (chrome), not secondary
|
||||||
document.getElementById("preview-phone-card").style.color = secondaryContent;
|
// (accent), so it doesn't compete for attention with an actual button.
|
||||||
|
document.getElementById("preview-phone-card").style.background = primary;
|
||||||
|
document.getElementById("preview-phone-card").style.color = primaryContent;
|
||||||
|
|
||||||
|
document.getElementById("preview-website-header").style.background = primary;
|
||||||
|
document.getElementById("preview-website-header").style.color = primaryContent;
|
||||||
|
document.getElementById("preview-website-join").style.background = secondary;
|
||||||
|
document.getElementById("preview-website-join").style.color = secondaryContent;
|
||||||
|
|
||||||
|
["preview-dot-1", "preview-dot-2", "preview-dot-3"].forEach((id) => {
|
||||||
|
document.getElementById(id).style.background = secondary;
|
||||||
|
});
|
||||||
|
|
||||||
setContrastBox("contrast-box-primary", "contrast-label-primary", "contrast-ratio-primary", primary, primaryContent, "{% trans 'on primary' %}");
|
setContrastBox("contrast-box-primary", "contrast-label-primary", "contrast-ratio-primary", primary, primaryContent, "{% trans 'on primary' %}");
|
||||||
setContrastBox("contrast-box-secondary", "contrast-label-secondary", "contrast-ratio-secondary", secondary, secondaryContent, "{% trans 'on secondary' %}");
|
setContrastBox("contrast-box-secondary", "contrast-label-secondary", "contrast-ratio-secondary", secondary, secondaryContent, "{% trans 'on secondary' %}");
|
||||||
|
|||||||
@@ -4465,18 +4465,19 @@ class ClubSettingsPreviewTests(ManagementTestBase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client.force_login(self.admin_user)
|
self.client.force_login(self.admin_user)
|
||||||
|
|
||||||
def test_the_preview_frame_and_colour_swatches_render(self):
|
def test_the_preview_tabs_and_colour_swatches_render(self):
|
||||||
response = self.club_get("club_settings")
|
response = self.club_get("club_settings")
|
||||||
|
|
||||||
self.assertContains(response, "preview-sidebar")
|
self.assertContains(response, "preview-tab-active")
|
||||||
self.assertContains(response, 'id="primary_color_swatch"')
|
self.assertContains(response, 'id="primary_color_swatch"')
|
||||||
self.assertContains(response, 'id="secondary_color_swatch"')
|
self.assertContains(response, 'id="secondary_color_swatch"')
|
||||||
|
|
||||||
def test_the_mobile_app_preview_renders(self):
|
def test_the_mobile_app_and_website_previews_render(self):
|
||||||
response = self.club_get("club_settings")
|
response = self.club_get("club_settings")
|
||||||
|
|
||||||
self.assertContains(response, "preview-phone")
|
self.assertContains(response, "preview-phone-lg")
|
||||||
self.assertContains(response, "Coming soon")
|
self.assertContains(response, "preview-website")
|
||||||
|
self.assertContains(response, "Where the brand shows up")
|
||||||
|
|
||||||
def test_the_website_field_is_editable(self):
|
def test_the_website_field_is_editable(self):
|
||||||
response = self.club_get("club_settings")
|
response = self.club_get("club_settings")
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user