feat(brand): new brand module + logo assets

Adds the canonical brand definition used by the v0.42.0 brand refresh:

- client/lib/brand.ts — single source of truth for name, tagline,
  repository URL, asset paths, navigation glyphs, and accent palette
  (BRAND_GLYPHS / accent tokens consumed by Brand.tsx + page chrome).
- client/components/brand/Brand.tsx — BrandMark (img with the
  logo asset), BrandWordmark, BrandGlyph, and a small BrandStack
  used by the layout chrome.
- client/public/brand/{logo,pwa-192,pwa-512,apple-touch-icon}.png —
  replaces the inline img-cut references in README/HISTORY with a
  canonical set; PWA manifest + favicon now point here.

This is commit 1 of 9 splitting the working-tree brand refresh into
per-layer commits. Each commit is independently buildable; the order
matches the dependency direction (lib → component → chrome → UI
primitives → page consumers → admin/dialog consumers → docs).
This commit is contained in:
null 2026-07-05 17:18:04 -05:00
parent 1f57b22ff8
commit dfee7f9401
6 changed files with 102 additions and 0 deletions

View File

@ -0,0 +1,53 @@
import type { ComponentProps } from 'react';
import { cn } from '@/lib/utils';
import { BRAND, BRAND_GLYPHS, type BrandGlyphName } from '@/lib/brand';
type ImgProps = Omit<ComponentProps<'img'>, 'src' | 'alt'>;
export function BrandMark({ className, ...props }: ImgProps) {
return (
<img
src={BRAND.assets.logo}
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,
iconClassName,
}: {
name: BrandGlyphName;
className?: string;
iconClassName?: string;
}) {
const glyph = BRAND_GLYPHS[name];
const Icon = glyph.icon;
return (
<span
className={cn(
'relative inline-grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-xl',
'border border-primary/20 bg-primary/[0.08] text-primary shadow-sm shadow-primary/10',
'before:absolute before:inset-x-2 before:bottom-1.5 before:h-4 before:rounded-t-sm before:bg-gradient-to-t before:from-primary/25 before:to-primary/5',
'after:absolute after:inset-x-3 after:bottom-1.5 after:h-6 after:rounded-t-sm after:border-l after:border-r after:border-primary/15',
className,
)}
aria-label={glyph.label}
role="img"
>
<Icon className={cn('relative z-10 h-5 w-5', iconClassName)} />
</span>
);
}

49
client/lib/brand.ts Normal file
View File

@ -0,0 +1,49 @@
import {
BarChart3,
CalendarDays,
Database,
Landmark,
LockKeyhole,
Receipt,
ShieldCheck,
TrendingDown,
type LucideIcon,
} from 'lucide-react';
export const BRAND = {
name: 'Bill Tracker',
compactName: 'Bill Tracker',
tagline: 'Calm command for household money.',
repositoryUrl: 'https://dream.scheller.ltd/null/BillTracker',
assets: {
logo: '/brand/logo.png',
favicon: '/brand/logo.png',
appleTouchIcon: '/brand/apple-touch-icon.png',
pwa192: '/brand/pwa-192.png',
pwa512: '/brand/pwa-512.png',
legacyLogo: '/img/logo.png',
},
} as const;
export type BrandGlyphName =
| 'tracker'
| 'bills'
| 'calendar'
| 'analytics'
| 'data'
| 'banking'
| 'snowball'
| 'security';
export const BRAND_GLYPHS: Record<BrandGlyphName, { label: string; icon: LucideIcon }> = {
tracker: { label: 'Tracker', icon: BarChart3 },
bills: { label: 'Bills', icon: Receipt },
calendar: { label: 'Calendar', icon: CalendarDays },
analytics: { label: 'Analytics', icon: BarChart3 },
data: { label: 'Data', icon: Database },
banking: { label: 'Banking', icon: Landmark },
snowball: { label: 'Snowball', icon: TrendingDown },
security: { label: 'Security', icon: ShieldCheck },
};
export const ADMIN_GLYPH = { label: 'Admin', icon: LockKeyhole } as const;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB