BillTracker/client/components/brand/Brand.tsx

51 lines
1.4 KiB
TypeScript
Raw Permalink Normal View History

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,
)}
/>
);
}