SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
3.2 KB · 81 lines tsx
Raw Blame History
1import Link from 'next/link';2import type { ReactNode } from 'react';3import { cn } from '@/lib/cn';4import { routes } from '@/lib/site';56/** Small building blocks shared by the satellite / launch pages (local variants — shared ui/* is read-only). */78export function DL({ children, className }: { children: ReactNode; className?: string }) {9  return <dl className={cn('divide-y divide-[color:var(--rule)] text-sm', className)}>{children}</dl>;10}1112export function Row({ label, value, mono = true, hint }: { label: ReactNode; value: ReactNode; mono?: boolean; hint?: ReactNode }) {13  return (14    <div className="grid grid-cols-[minmax(0,42%)_minmax(0,1fr)] items-baseline gap-3 py-2">15      <dt className="text-ink-3">16        {label}17        {hint && <span className="ml-1 text-2xs text-ink-3/70">{hint}</span>}18      </dt>19      <dd className={cn('min-w-0 break-words text-right text-ink', mono && 'mono tnum')}>{value}</dd>20    </div>21  );22}2324export function Derived({ className }: { className?: string }) {25  return (26    <Link href={routes.methodology()} className={cn('inline-flex items-center rounded border border-dashed border-rule-strong px-1.5 py-px text-[10px] uppercase tracking-wider text-ink-3 hover:text-accent', className)} title="Derived by SatelliteIndex — see methodology">27      derived28    </Link>29  );30}3132export function Note({ children, className }: { children: ReactNode; className?: string }) {33  return <p className={cn('rounded-md border border-dashed border-rule px-3 py-2.5 text-xs leading-relaxed text-ink-3', className)}>{children}</p>;34}3536export function Chip({ href, children, active = false, count, className }: { href: string; children: ReactNode; active?: boolean; count?: ReactNode; className?: string }) {37  return (38    <Link39      href={href}40      className={cn(41        'inline-flex min-h-9 items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs transition-colors',42        active ? 'border-accent/40 bg-accent-soft text-accent' : 'border-rule bg-plane text-ink-2 hover:border-rule-strong hover:text-ink',43        className,44      )}45    >46      <span className="truncate">{children}</span>47      {count !== undefined && <span className="tnum text-[11px] text-ink-3">{count}</span>}48    </Link>49  );50}5152export function Head({ eyebrow, title, action, children }: { eyebrow?: string; title: ReactNode; action?: { href: string; label: string }; children?: ReactNode }) {53  return (54    <div className="mb-4 flex items-end justify-between gap-4 border-b border-rule pb-3">55      <div>56        {eyebrow && <p className="eyebrow">{eyebrow}</p>}57        <h2 className="mt-0.5 text-lg font-semibold tracking-tight md:text-xl">{title}</h2>58        {children}59      </div>60      {action && (61        <Link href={action.href} className="shrink-0 py-1 text-sm text-accent hover:underline">62          {action.label} →63        </Link>64      )}65    </div>66  );67}6869/** Wraps a page section; `id` allows deep links. */70export function Block({ id, children, className }: { id: string; children: ReactNode; className?: string }) {71  return (72    <section id={id} className={cn('scroll-mt-20 py-6 md:py-8', className)}>73      {children}74    </section>75  );76}7778export function Empty({ children }: { children: ReactNode }) {79  return <p className="text-sm text-ink-3">{children}</p>;80}81