feat(17-04): wire logo img into BrandSlot, set --brand-logo-border-radius 0

- Replace placeholder FS div with decorative <img src="/logo.svg" alt="" aria-hidden>
- Apply Surface B-1 style (width/height brand-logo-size, borderRadius token, contain/block)
- Update --brand-logo-border-radius from 50% to 0 (SVG draws its own rx=104 shape)
- h1 FamilySync and tagline p unchanged; LoginPage.tsx untouched (seam contract honored)
- No dangerouslySetInnerHTML (T-05-24 invariant maintained)
This commit is contained in:
Lucas Berger
2026-06-18 12:48:55 -04:00
parent 9e080fb22a
commit ce95aa3e6b
2 changed files with 18 additions and 28 deletions
+17 -27
View File
@@ -1,24 +1,20 @@
/** /**
* BrandSlot — Phase 17 seam component for the login page brand area. * BrandSlot — Phase 17 seam component for the login page brand area.
* *
* Phase 19 ships a minimal shippable placeholder: a 48px circle with "FS" * Phase 19 shipped a minimal shippable placeholder: a 48px circle with "FS"
* initials, the app name "FamilySync", and the tagline "Family calendar & lists". * initials, the app name "FamilySync", and the tagline "Family calendar & lists".
* *
* Phase 17 replaces the internals of this component (swap the placeholder div for * Phase 17 replaces the placeholder div with a decorative logo <img> — the approved
* an <img> with a real logo) without touching LoginPage's layout. This isolates * FamilySync family-house SVG. LoginPage layout is untouched (seam contract honored;
* the branding seam — see 19-UI-SPEC.md §Brand Slot section. * see 19-UI-SPEC.md §Brand Slot section).
* *
* CSS custom properties used (all set in tokens.css with placeholder defaults; * CSS custom properties used (all set in tokens.css):
* Phase 17 overrides these values): * --brand-logo-size — image size (default: 48px)
* --brand-logo-bg — logo circle background (default: var(--color-member-0)) * --brand-logo-border-radius — image border-radius (0 — SVG draws its own shape)
* --brand-logo-text — initials color (default: #ffffff)
* --brand-logo-size — circle diameter (default: 48px)
* --brand-logo-border-radius — circle shape (default: 50%)
* *
* Accessibility: * Accessibility:
* <h1> contains the app name — screen readers read "FamilySync" as the page title. * <h1> contains the app name — screen readers read "FamilySync" as the page title.
* The logo circle is aria-hidden (the text is the accessible label). * The logo image is decorative (alt="", aria-hidden="true").
* No <img> today → no broken image ref → no layout shift when Phase 17 replaces it.
* *
* Security: all copy is plain-text JSX children — no dangerouslySetInnerHTML (T-05-24). * Security: all copy is plain-text JSX children — no dangerouslySetInnerHTML (T-05-24).
*/ */
@@ -26,28 +22,22 @@
export function BrandSlot() { export function BrandSlot() {
return ( return (
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
{/* Phase 17 replaces this div with <img src="..." alt="" /> */} {/* Decorative brand logo — Phase 17 approved brand mark (logo.svg) */}
<div <img
src="/logo.svg"
alt=""
aria-hidden="true" aria-hidden="true"
style={{ style={{
width: 'var(--brand-logo-size, 48px)', width: 'var(--brand-logo-size, 48px)',
height: 'var(--brand-logo-size, 48px)', height: 'var(--brand-logo-size, 48px)',
borderRadius: 'var(--brand-logo-border-radius, 50%)', borderRadius: 'var(--brand-logo-border-radius)',
background: 'var(--brand-logo-bg, var(--color-member-0, #4a90d9))',
color: 'var(--brand-logo-text, #ffffff)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto var(--space-2, 8px)', margin: '0 auto var(--space-2, 8px)',
fontSize: 'var(--text-display-size, 24px)', display: 'block',
fontWeight: 600,
fontFamily: 'var(--font-family-base)',
flexShrink: 0,
aspectRatio: '1 / 1', aspectRatio: '1 / 1',
objectFit: 'contain',
flexShrink: 0,
}} }}
> />
FS
</div>
{/* App name — <h1> so screen readers identify the page (UI-SPEC §Accessibility) */} {/* App name — <h1> so screen readers identify the page (UI-SPEC §Accessibility) */}
<h1 <h1
+1 -1
View File
@@ -103,7 +103,7 @@
--brand-logo-bg: var(--color-member-0); /* placeholder circle background */ --brand-logo-bg: var(--color-member-0); /* placeholder circle background */
--brand-logo-text: #ffffff; /* placeholder initials color */ --brand-logo-text: #ffffff; /* placeholder initials color */
--brand-logo-size: 48px; /* reserved slot height; keep 1:1 aspect */ --brand-logo-size: 48px; /* reserved slot height; keep 1:1 aspect */
--brand-logo-border-radius: 50%; /* circle for initials; Phase 17 may change */ --brand-logo-border-radius: 0; /* SVG draws its own rounded-square background (rx=104); no CSS clip needed */
--brand-app-name: 'FamilySync'; /* drives doc only — not used as CSS content */ --brand-app-name: 'FamilySync'; /* drives doc only — not used as CSS content */
/* ───────────────────────────────────────────────────────────────────────── /* ─────────────────────────────────────────────────────────────────────────