51 lines
1.4 KiB
TypeScript
51 lines
1.4 KiB
TypeScript
import { type ComponentProps } from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
import { BRAND, BRAND_GLYPHS, type BrandGlyphName } from '@/lib/brand';
|
|
import { useTheme } from '@/contexts/ThemeContext';
|
|
|
|
type ImgProps = Omit<ComponentProps<'img'>, 'src' | 'alt'>;
|
|
|
|
export function BrandMark({ className, ...props }: ImgProps) {
|
|
const { resolvedTheme } = useTheme();
|
|
const src = resolvedTheme === 'light' ? BRAND.assets.logoLight : BRAND.assets.logo;
|
|
|
|
return <img src={src} alt={BRAND.name} className={cn('object-contain', className)} {...props} />;
|
|
}
|
|
|
|
export function BrandWordmark({
|
|
compact = false,
|
|
className,
|
|
}: {
|
|
compact?: boolean;
|
|
className?: string;
|
|
}) {
|
|
return (
|
|
<span className={cn('font-semibold tracking-tight text-foreground', className)}>
|
|
{compact ? BRAND.compactName : BRAND.name}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function BrandGlyph({ name, className }: { name: BrandGlyphName; className?: string }) {
|
|
const glyph = BRAND_GLYPHS[name];
|
|
|
|
return (
|
|
<img
|
|
src={glyph.asset}
|
|
alt={glyph.label}
|
|
width={40}
|
|
height={40}
|
|
draggable={false}
|
|
// Fail soft: if the asset can't load, hide rather than show a broken image —
|
|
// the adjacent title/label carries the meaning.
|
|
onError={(e) => {
|
|
e.currentTarget.style.display = 'none';
|
|
}}
|
|
className={cn(
|
|
'inline-block h-10 w-10 shrink-0 select-none rounded-xl object-contain shadow-sm',
|
|
className,
|
|
)}
|
|
/>
|
|
);
|
|
}
|