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%
1.4 KB · 46 lines tsx
Raw Blame History
1import type { ReactNode } from 'react';2import { cn } from '@/lib/cn';34/**5 * Editorial section: eyebrow/heading + subtitle on the left, optional actions on the right, a 1 px rule above.6 * Use instead of cards. `id` makes it linkable from TopicNav / TOC.7 */8export function Section({9  id,10  title,11  subtitle,12  eyebrow,13  actions,14  children,15  className,16  level = 2,17  tight = false,18}: {19  id?: string;20  title: ReactNode;21  subtitle?: ReactNode;22  eyebrow?: ReactNode;23  actions?: ReactNode;24  children: ReactNode;25  className?: string;26  level?: 2 | 3;27  tight?: boolean;28}) {29  const H = level === 2 ? 'h2' : 'h3';30  return (31    <section id={id} className={cn('hairline min-w-0 scroll-mt-28', tight ? 'pt-4 pb-6' : 'pt-6 pb-10 md:pt-8 md:pb-14', className)} aria-labelledby={id ? `${id}-h` : undefined}>32      <div className="mb-4 flex flex-wrap items-end justify-between gap-x-6 gap-y-2 md:mb-6">33        <div className="min-w-0">34          {eyebrow ? <div className="eyebrow mb-1">{eyebrow}</div> : null}35          <H id={id ? `${id}-h` : undefined} className={cn('display text-ink', level === 2 ? 'text-2xl md:text-3xl' : 'text-xl md:text-2xl')}>36            {title}37          </H>38          {subtitle ? <p className="mt-1 max-w-prose text-sm text-ink-2 md:text-base">{subtitle}</p> : null}39        </div>40        {actions ? <div className="flex shrink-0 flex-wrap items-center gap-2 text-sm">{actions}</div> : null}41      </div>42      {children}43    </section>44  );45}46