spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import type { ReactNode } from 'react';23/** Editorial section: kicker, serif title, optional description and right-aligned actions. */4export function Section({5 id,6 kicker,7 title,8 description,9 actions,10 children,11 className = '',12 level = 2,13}: {14 id?: string;15 kicker?: string;16 title: ReactNode;17 description?: ReactNode;18 actions?: ReactNode;19 children: ReactNode;20 className?: string;21 level?: 2 | 3;22}) {23 const H = level === 3 ? 'h3' : 'h2';24 return (25 <section id={id} aria-labelledby={id ? `${id}-title` : undefined} className={`ci-rule pt-5 ${className}`}>26 <div className="mb-3 flex flex-wrap items-end justify-between gap-2">27 <div className="min-w-0">28 {kicker ? <p className="ci-kicker mb-1">{kicker}</p> : null}29 <H id={id ? `${id}-title` : undefined} className={level === 3 ? 'text-lg' : 'text-xl sm:text-2xl'}>30 {title}31 </H>32 {description ? <p className="mt-1 max-w-3xl text-[13.5px] text-ink-2">{description}</p> : null}33 </div>34 {actions ? <div className="flex shrink-0 items-center gap-2 text-[13px]">{actions}</div> : null}35 </div>36 {children}37 </section>38 );39}4041export function PageHeader({ kicker, title, lede, children, className = '' }: { kicker?: string; title: ReactNode; lede?: ReactNode; children?: ReactNode; className?: string }) {42 return (43 <header className={`pb-4 pt-6 sm:pt-8 ${className}`}>44 {kicker ? <p className="ci-kicker mb-2">{kicker}</p> : null}45 <h1 className="text-3xl leading-tight sm:text-4xl">{title}</h1>46 {lede ? <p className="mt-2 max-w-3xl text-[15px] leading-relaxed text-ink-2">{lede}</p> : null}47 {children}48 </header>49 );50}5152/** Definition list in two columns, dense. */53export function KV({ items, className = '' }: { items: Array<{ k: ReactNode; v: ReactNode }>; className?: string }) {54 const rows = items.filter((i) => i.v !== null && i.v !== undefined && i.v !== '');55 if (rows.length === 0) return null;56 return (57 <dl className={`grid grid-cols-[minmax(110px,auto)_1fr] gap-x-4 gap-y-1.5 text-[13.5px] ${className}`}>58 {rows.map((r, i) => (59 <div key={i} className="contents">60 <dt className="text-ink-3">{r.k}</dt>61 <dd className="min-w-0 break-words text-ink">{r.v}</dd>62 </div>63 ))}64 </dl>65 );66}6768export function Note({ children, tone = 'neutral' }: { children: ReactNode; tone?: 'neutral' | 'warn' }) {69 return <p className={`border-l-2 pl-3 text-[12.5px] leading-relaxed ${tone === 'warn' ? 'border-warn text-warn' : 'border-rule-strong text-ink-3'}`}>{children}</p>;70}71