spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1import { cn } from '@/lib/cn';2import { t } from '@/i18n';34/**5 * CountryAtlas brand mark — "the graticule A".6 * A globe ring carrying a light graticule (one inner meridian ellipse and a tropic parallel), whose equator is7 * the crossbar of a capital "A" drawn by two meridian legs meeting at the pole. Globe + atlas + the letter A in one8 * glyph; strokes only, `currentColor`, monochrome-capable, legible at 16 px (the graticule fades, the A and the9 * ring stay).10 *11 * Usage: <Logo variant="full" /> (header), <Logo variant="mark" size={24} /> (favicons, OG), <Logo variant="wordmark" />.12 */13export const MARK_PATHS = {14 ring: { cx: 16, cy: 16, r: 13 },15 meridian: { cx: 16, cy: 16, rx: 5.6, ry: 13 },16 equator: 'M3.6 19.6h24.8',17 tropic: 'M4.2 11.2h23.6',18 legs: 'M9.3 27.4 16 6.2l6.7 21.2',19} as const;2021export function LogoMark({ size = 28, className, title, strokeWidth = 2 }: { size?: number; className?: string; title?: string; strokeWidth?: number }) {22 const m = MARK_PATHS;23 return (24 <svg width={size} height={size} viewBox="0 0 32 32" fill="none" stroke="currentColor" strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" className={cn('shrink-0', className)} role={title ? 'img' : undefined} aria-hidden={title ? undefined : true} aria-label={title}>25 {title ? <title>{title}</title> : null}26 <circle cx={m.ring.cx} cy={m.ring.cy} r={m.ring.r} />27 <ellipse cx={m.meridian.cx} cy={m.meridian.cy} rx={m.meridian.rx} ry={m.meridian.ry} strokeWidth={strokeWidth * 0.6} opacity="0.5" />28 <path d={m.tropic} strokeWidth={strokeWidth * 0.6} opacity="0.5" />29 <path d={m.equator} />30 <path d={m.legs} />31 </svg>32 );33}3435export function Wordmark({ className }: { className?: string }) {36 return (37 <span className={cn('select-none whitespace-nowrap font-ui text-[1.0625rem] tracking-[-0.02em] text-ink', className)}>38 <span className="font-normal">Country</span>39 <span className="font-semibold">Atlas</span>40 </span>41 );42}4344export function Logo({ variant = 'full', size = 26, className }: { variant?: 'full' | 'mark' | 'wordmark'; size?: number; className?: string }) {45 if (variant === 'mark') return <LogoMark size={size} className={className} title={t('brand.logoAlt')} />;46 if (variant === 'wordmark') return <Wordmark className={className} />;47 return (48 <span className={cn('inline-flex items-center gap-2', className)}>49 <LogoMark size={size} className="text-accent" />50 <Wordmark />51 </span>52 );53}5455/** Raw SVG string of the mark (for favicons / OG rasterisation); `color` is a CSS colour. */56export function logoMarkSvg({ size = 512, color = '#1c5cab', background, radius = 0, strokeWidth = 2 }: { size?: number; color?: string; background?: string; radius?: number; strokeWidth?: number } = {}): string {57 const m = MARK_PATHS;58 const bg = background ? `<rect width="32" height="32" rx="${radius}" fill="${background}" stroke="none"/>` : '';59 const thin = (strokeWidth * 0.6).toFixed(2);60 return (61 `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 32 32" fill="none" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round">${bg}` +62 `<circle cx="${m.ring.cx}" cy="${m.ring.cy}" r="${m.ring.r}"/>` +63 `<ellipse cx="${m.meridian.cx}" cy="${m.meridian.cy}" rx="${m.meridian.rx}" ry="${m.meridian.ry}" stroke-width="${thin}" opacity="0.5"/>` +64 `<path d="${m.tropic}" stroke-width="${thin}" opacity="0.5"/><path d="${m.equator}"/><path d="${m.legs}"/></svg>`65 );66}67