Institutional A-grade nav logo: SVG lockups, WCAG audit, and deploy hygiene.

Add transparent nav SVG assets with raster fallbacks, header contrast and focus fixes, automated a11y audit scripts, and evidence reports for institutional review.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
defiQUG
2026-06-15 19:06:00 -07:00
co-authored by Cursor
parent 23e61dc49c
commit 5c465d176e
23 changed files with 3712 additions and 64 deletions
+1 -1
View File
@@ -4545,7 +4545,7 @@ function AppContent() {
</div>
)}
<header className="sticky top-0 z-50 backdrop-blur supports-[backdrop-filter]:bg-white/50 dark:supports-[backdrop-filter]:bg-black/40 border-b border-white/30 dark:border-white/10">
<header className="sticky top-0 z-50 border-b border-secondary-200/40 bg-[var(--mim-warm-white)]/95 backdrop-blur dark:border-white/10 dark:bg-black/85">
<Navigation
darkMode={darkMode}
setDarkMode={setDarkMode}
+1 -2
View File
@@ -25,10 +25,9 @@ export function Footer() {
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-neutral-700 transition hover:text-primary-600 dark:text-neutral-300 dark:hover:text-secondary-400"
aria-label="Follow Miracles in Motion Foundation on Instagram"
>
<Instagram className="h-5 w-5" aria-hidden />
@miraclesinmotionfoundation
Follow @miraclesinmotionfoundation on Instagram
</a>
</div>
</div>
+1 -1
View File
@@ -22,7 +22,7 @@ describe('Footer Component', () => {
it('renders service area and Instagram', () => {
render(<Footer />)
expect(screen.getByText(/Los Angeles County/)).toBeInTheDocument()
expect(screen.getByLabelText(/Instagram/i)).toHaveAttribute(
expect(screen.getByRole('link', { name: /Follow @miraclesinmotionfoundation on Instagram/i })).toHaveAttribute(
'href',
'https://www.instagram.com/miraclesinmotionfoundation'
)
+50 -35
View File
@@ -1,18 +1,59 @@
export type LogoMarkVariant = 'nav' | 'footer' | 'square'
export interface LogoMarkProps {
/** nav: symbol on mobile, horizontal on md+; footer: horizontal; square: stacked lockup */
/** nav: symbol on mobile, horizontal on md+; footer: symbol on xs, horizontal on sm+ */
variant?: LogoMarkVariant
className?: string
}
const BRAND = {
symbolSvg: '/brand/logo-symbol.svg',
symbolPng: '/brand/logo-symbol-512.png',
symbolNavSvg: '/brand/logo-symbol-nav.svg',
symbolNav: '/brand/logo-symbol-nav.png',
horizontalNavSvg: '/brand/logo-horizontal-nav.svg',
horizontalNav: '/brand/logo-horizontal-nav.png',
horizontalNavWebp: '/brand/logo-horizontal-nav.webp',
horizontal: '/brand/logo-horizontal.png',
square: '/brand/logo-square.png',
} as const
function ResponsiveLockup({
symbolClassName,
horizontalClassName,
}: {
symbolClassName: string
horizontalClassName: string
}) {
return (
<>
<picture className={symbolClassName}>
<source srcSet={BRAND.symbolNavSvg} type="image/svg+xml" />
<img
src={BRAND.symbolNav}
alt=""
className="logo-mark__img logo-mark__img--symbol"
width={256}
height={256}
decoding="async"
fetchPriority="high"
/>
</picture>
<picture className={horizontalClassName}>
<source srcSet={BRAND.horizontalNavSvg} type="image/svg+xml" />
<source srcSet={BRAND.horizontalNavWebp} type="image/webp" />
<img
src={BRAND.horizontalNav}
alt=""
className="logo-mark__img logo-mark__img--horizontal"
width={1024}
height={354}
decoding="async"
fetchPriority="high"
/>
</picture>
</>
)
}
export function LogoMark({ variant = 'nav', className = '' }: LogoMarkProps) {
const isDecorative = variant === 'nav' || variant === 'footer'
@@ -22,23 +63,9 @@ export function LogoMark({ variant = 'nav', className = '' }: LogoMarkProps) {
className={['logo-mark', 'logo-mark--nav', className].filter(Boolean).join(' ')}
aria-hidden={true}
>
<img
src={BRAND.symbolSvg}
alt=""
className="logo-mark__img logo-mark__img--symbol block lg:hidden dark:block dark:lg:block"
width={100}
height={99}
decoding="async"
fetchPriority="high"
/>
<img
src={BRAND.horizontal}
alt=""
className="logo-mark__img logo-mark__img--horizontal hidden lg:block dark:hidden"
width={1024}
height={354}
decoding="async"
fetchPriority="high"
<ResponsiveLockup
symbolClassName="logo-mark__img-wrap logo-mark__img-wrap--symbol block md:hidden"
horizontalClassName="logo-mark__img-wrap logo-mark__img-wrap--horizontal hidden md:block"
/>
</span>
)
@@ -50,21 +77,9 @@ export function LogoMark({ variant = 'nav', className = '' }: LogoMarkProps) {
className={['logo-mark', 'logo-mark--footer', className].filter(Boolean).join(' ')}
aria-hidden={true}
>
<img
src={BRAND.symbolSvg}
alt=""
className="logo-mark__img logo-mark__img--symbol hidden dark:block"
width={100}
height={99}
decoding="async"
/>
<img
src={BRAND.horizontal}
alt=""
className="logo-mark__img logo-mark__img--horizontal block dark:hidden"
width={1024}
height={354}
decoding="async"
<ResponsiveLockup
symbolClassName="logo-mark__img-wrap logo-mark__img-wrap--symbol block sm:hidden"
horizontalClassName="logo-mark__img-wrap logo-mark__img-wrap--horizontal hidden sm:block"
/>
</span>
)
@@ -0,0 +1,57 @@
import { describe, it, expect } from 'vitest'
import { render } from '@testing-library/react'
import { LogoMark } from '../LogoMark'
describe('LogoMark', () => {
it('renders nav symbol and transparent horizontal lockup sources with SVG preference', () => {
const { container } = render(<LogoMark variant="nav" />)
const symbolSvg = container.querySelector('picture source[type="image/svg+xml"]')
const symbol = container.querySelector('picture img.logo-mark__img--symbol')
const horizontal = container.querySelectorAll('picture')[1]?.querySelector('img.logo-mark__img--horizontal')
const horizontalSvg = container.querySelectorAll('picture')[1]?.querySelector('source[type="image/svg+xml"]')
const webp = container.querySelectorAll('picture')[1]?.querySelector('source[type="image/webp"]')
expect(symbolSvg).toHaveAttribute('srcset', '/brand/logo-symbol-nav.svg')
expect(symbol).toHaveAttribute('src', '/brand/logo-symbol-nav.png')
expect(horizontalSvg).toHaveAttribute('srcset', '/brand/logo-horizontal-nav.svg')
expect(horizontal).toHaveAttribute('src', '/brand/logo-horizontal-nav.png')
expect(webp).toHaveAttribute('srcset', '/brand/logo-horizontal-nav.webp')
})
it('marks nav and footer logos as decorative for screen readers', () => {
const { container: nav } = render(<LogoMark variant="nav" />)
const { container: footer } = render(<LogoMark variant="footer" />)
expect(nav.querySelector('.logo-mark--nav')).toHaveAttribute('aria-hidden', 'true')
expect(footer.querySelector('.logo-mark--footer')).toHaveAttribute('aria-hidden', 'true')
})
it('uses the same transparent nav assets in footer', () => {
const { container } = render(<LogoMark variant="footer" />)
expect(container.querySelector('picture img.logo-mark__img--symbol')).toHaveAttribute(
'src',
'/brand/logo-symbol-nav.png'
)
expect(container.querySelectorAll('picture')[1]?.querySelector('img.logo-mark__img--horizontal')).toHaveAttribute(
'src',
'/brand/logo-horizontal-nav.png'
)
})
it('renders square lockup with accessible alt text', () => {
const { container } = render(<LogoMark variant="square" />)
const img = container.querySelector('img')
expect(img).toHaveAttribute('src', '/brand/logo-square.png')
expect(img).toHaveAttribute('alt', 'Miracles in Motion Foundation')
})
it('applies responsive visibility classes for nav breakpoints', () => {
const { container } = render(<LogoMark variant="nav" />)
expect(container.querySelector('picture')?.className).toMatch(/block md:hidden/)
expect(container.querySelectorAll('picture')[1]?.className).toMatch(/hidden md:block/)
})
})
+54 -12
View File
@@ -21,11 +21,16 @@
--mim-cream: #faf8f5;
--mim-warm-beige: #f5f0e8;
--mim-warm-white: #fffefb;
--mim-focus-ring: var(--mim-brand-gold);
--mim-focus-ring: var(--mim-brand-green-deep);
--mim-eyebrow-text: #7d6232;
--mim-shadow-brand: rgba(26, 60, 52, 0.28);
--mim-shadow-gold: rgba(197, 160, 89, 0.32);
}
.dark {
--mim-focus-ring: var(--mim-brand-gold-bright);
}
@layer base {
html {
scroll-behavior: smooth;
@@ -103,7 +108,7 @@
}
@layer components {
/* Brand logos: /brand/logo-symbol.svg, logo-horizontal.png, logo-square.png */
/* Brand logos: nav lockups use transparent /brand/logo-*-nav.*; print/social use opaque PNGs */
.logo-mark {
display: inline-flex;
align-items: center;
@@ -112,30 +117,51 @@
line-height: 0;
}
.logo-mark__img {
.logo-mark__img,
.logo-mark__img-wrap {
display: block;
line-height: 0;
}
.logo-mark__img {
object-fit: contain;
object-position: left center;
}
/* Nav: symbol (square) on mobile, horizontal lockup on md+ */
.logo-mark--nav {
height: 3rem;
.logo-mark__img-wrap--horizontal .logo-mark__img--horizontal {
display: block;
height: 100%;
width: auto;
filter: drop-shadow(0 1px 1px rgb(15 41 34 / 0.12));
}
/* Nav: symbol on mobile, transparent horizontal lockup on md+ */
.logo-mark--nav {
height: 3.25rem;
}
.logo-mark--nav .logo-mark__img-wrap--symbol,
.logo-mark--nav .logo-mark__img--symbol {
height: 100%;
width: auto;
max-width: 3.25rem;
max-width: 3.5rem;
filter: drop-shadow(0 1px 1px rgb(15 41 34 / 0.1));
}
.logo-mark--nav .logo-mark__img-wrap--symbol .logo-mark__img--symbol {
height: 100%;
width: auto;
}
@media (min-width: 768px) {
.logo-mark--nav {
height: 4.25rem;
}
.logo-mark--nav .logo-mark__img--horizontal {
.logo-mark--nav .logo-mark__img-wrap--horizontal {
height: 100%;
width: auto;
max-width: min(20rem, 48vw);
}
.logo-mark--nav .logo-mark__img--horizontal {
max-width: min(20rem, 48vw);
}
}
@@ -144,6 +170,7 @@
.logo-mark--nav {
height: 4.5rem;
}
.logo-mark--nav .logo-mark__img-wrap--horizontal,
.logo-mark--nav .logo-mark__img--horizontal {
max-width: 21rem;
}
@@ -153,20 +180,33 @@
.logo-mark--nav {
height: 4.75rem;
}
.logo-mark--nav .logo-mark__img-wrap--horizontal,
.logo-mark--nav .logo-mark__img--horizontal {
max-width: 23rem;
}
}
/* Footer: horizontal on light; symbol on dark (horizontal PNG has opaque panel) */
/* Footer: symbol on xs, transparent horizontal on sm+ (same assets as nav) */
.logo-mark--footer {
height: 2.75rem;
}
.logo-mark--footer .logo-mark__img-wrap--symbol,
.logo-mark--footer .logo-mark__img--symbol {
height: 100%;
width: auto;
max-width: 3.25rem;
filter: drop-shadow(0 1px 1px rgb(15 41 34 / 0.1));
}
.logo-mark--footer .logo-mark__img-wrap--symbol .logo-mark__img--symbol {
height: 100%;
width: auto;
}
.logo-mark--footer .logo-mark__img-wrap--horizontal {
height: 100%;
max-width: min(12rem, 80vw);
}
.logo-mark--footer .logo-mark__img--horizontal {
@@ -179,6 +219,7 @@
.logo-mark--footer {
height: 3.25rem;
}
.logo-mark--footer .logo-mark__img-wrap--horizontal,
.logo-mark--footer .logo-mark__img--horizontal {
max-width: 14rem;
}
@@ -188,6 +229,7 @@
.logo-mark--footer {
height: 3.5rem;
}
.logo-mark--footer .logo-mark__img-wrap--horizontal,
.logo-mark--footer .logo-mark__img--horizontal {
max-width: 16rem;
}
@@ -240,7 +282,7 @@
}
.nav-brand-link:focus-visible {
outline: 2px solid var(--mim-brand-gold);
outline: 2px solid var(--mim-focus-ring);
outline-offset: 3px;
}
@@ -481,7 +523,7 @@
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--mim-brand-gold);
color: var(--mim-eyebrow-text);
font-weight: 600;
}
.dark .section-eyebrow {
+1 -1
View File
@@ -33,7 +33,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({
return (
<div className={`min-h-screen bg-gradient-to-br from-primary-50 via-white to-secondary-50 dark:from-gray-900 dark:via-gray-800 dark:to-primary-900 ${className}`}>
<a href="#content" className="skip-link">Skip to content</a>
<header className="sticky top-0 z-40 bg-white/80 backdrop-blur dark:bg-black/80">
<header className="sticky top-0 z-40 border-b border-secondary-200/40 bg-[var(--mim-warm-white)]/95 backdrop-blur dark:border-white/10 dark:bg-black/85">
<Navigation
darkMode={darkMode}
setDarkMode={setDarkMode}
+2 -2
View File
@@ -40,8 +40,8 @@ export function BrandAssetsPage() {
Keep clear space around the lockup (at least the height of the capital M in MIRACLES).
</li>
<li>
<strong>Website header:</strong> horizontal lockup on tablet/desktop; symbol mark on
mobile.
<strong>Website header:</strong> transparent nav lockups (`logo-horizontal-nav.svg` preferred, plus WebP/PNG;
`logo-symbol-nav.svg` or PNG on mobile) on light and dark UI shells. Opaque horizontal PNG for print/partner kits only.
</li>
<li>
<strong>Favicon / app icon:</strong> symbol on forest green (<code>#1a3c34</code>).