Files
sharper.ly/apps/web/app/components/ui/Navigation/Brand.tsx
T

98 lines
3.1 KiB
TypeScript
Raw Normal View History

2026-09-11 02:46:56 +02:00
import { Link } from "react-router";
import { cx, FOCUS_RING } from "./styles";
import type { NavSurface } from "./types";
2026-09-23 22:47:04 +02:00
/** One half of the animated brand lockup, drawn as its own SVG file. */
interface BrandMark {
src: string;
/** Intrinsic size of the file, for the aspect ratio the browser reserves. */
width: number;
height: number;
/** The mark's own drawn width, so the pair flexes to one scale. */
drawnWidth: number;
}
2026-09-11 02:46:56 +02:00
2026-09-23 22:47:04 +02:00
/**
* The SHARP-ER lockup, split into its two drawn pieces so the "-ER" can slide
* out from behind the P and fade in once the page has settled (Navigation.css).
* Each surface ships its own export of the two pieces: white on the dark
* surface, the two-tone ink on the light surface. Both exports of a piece are
* the exact same box (218x46 and 112x46), so the pair shares one scale; each
* mark takes the share of the row its box gives it, and the suffix rides flush
* against the P's right edge.
*/
const BRAND_LOCKUP: Record<
NavSurface,
{ base: BrandMark; suffix: BrandMark }
> = {
light: {
base: { src: "/assets/sharp-wordmark.svg", width: 218, height: 46, drawnWidth: 218 },
suffix: { src: "/assets/sharp-er-suffix.svg", width: 112, height: 46, drawnWidth: 112 },
},
dark: {
base: { src: "/assets/sharp-wordmark-inverse.svg", width: 218, height: 46, drawnWidth: 218 },
suffix: { src: "/assets/sharp-er-suffix-inverse.svg", width: 112, height: 46, drawnWidth: 112 },
},
};
2026-09-11 02:46:56 +02:00
export interface BrandProps {
/** Locale-prefixed home path, e.g. "/en". */
href: string;
2026-09-23 22:47:04 +02:00
/** Accessible name of the link; the images are decorative. */
2026-09-11 02:46:56 +02:00
label: string;
surface: NavSurface;
className?: string;
}
/** The SHARP-ER wordmark, linking home. Sits at the inline start of the bar. */
export function Brand({ href, label, surface, className }: BrandProps) {
2026-09-23 22:47:04 +02:00
const { base, suffix } = BRAND_LOCKUP[surface];
2026-09-11 02:46:56 +02:00
return (
<Link
to={href}
aria-label={label}
className={cx(
"inline-flex shrink-0 items-center rounded-control",
FOCUS_RING,
className,
)}
>
2026-09-23 22:47:04 +02:00
{/* `dir="ltr"` because a Latin wordmark never mirrors; its position on
the bar still follows the page's direction. */}
<span dir="ltr" className="flex w-[100px] items-start">
<img
src={base.src}
alt=""
width={base.width}
height={base.height}
loading="eager"
decoding="async"
draggable={false}
style={{ flex: `${base.drawnWidth} 1 0%` }}
className="block h-auto min-w-0"
/>
{/* The clipping box starts at the P's right edge, so the mark inside
it can slide out from behind the P (Navigation.css). */}
<span
style={{ flex: `${suffix.drawnWidth} 1 0%` }}
className="sh-brand-suffix block min-w-0"
>
<img
src={suffix.src}
alt=""
width={suffix.width}
height={suffix.height}
loading="eager"
decoding="async"
draggable={false}
className="sh-brand-suffix__mark block h-auto w-full"
/>
</span>
</span>
2026-09-11 02:46:56 +02:00
</Link>
);
}