SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
3.5 KB · 67 lines tsx
Raw Blame History
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