import * as React from "react" import { Typography } from "@/components/ui/typography" import { cn } from "@/lib/utils" /* * 섹션 머리. * * eyebrow 는 선택이고, 기본적으로 쓰지 않는다. 작은 색 라벨을 섹션마다 반복하면 * 레퍼런스에서는 영문 대문자라 조판 요소로 기능하지만, 한글은 대문자가 없어서 * 그냥 "작은 글씨"가 되고 일곱 번 반복되면 벽지가 된다. * 위계는 라벨이 아니라 헤드라인 크기(본문의 3.5~4배)와 2톤 대비로 만든다. * * ── 세로 리듬 ──────────────────────────────────────────────────────── * 머리 안팎의 간격은 전부 이 파일이 소유한다. 섹션 파일에서 각자 mb-12 / mb-16 / * mb-20 / mb-24 를 붙이던 걸 걷어낸 결과다. 같은 관계(머리 → 본문)에 값이 다섯 개 * 있으면 스크롤할 때 섹션마다 호흡이 달라지고, 그게 "정돈이 안 됐다"는 인상의 * 실제 원인이었다. * * 규칙은 하나다 — 간격이 위계를 만든다. 가까울수록 한 덩어리로 읽힌다. * 값이 흔들리면 "어디까지가 한 덩어리인가"가 섹션마다 달라져 보인다. * * eyebrow → title 16px 라벨은 제목에 붙는다 * title → description 20 / 32px 큰 활자는 더 벌린다. 64px 제목 아래 20px 은 붙어 보인다 * 머리 → 본문 56 / 80px 섹션 안에서 가장 큰 끊김 * * 섹션 파일에서 이 관계들에 mb-· mt- 유틸리티를 직접 쓰지 않는다. 필요하면 아래 상수를 * import 해서 쓰고, 새 값이 필요하다고 느껴지면 여기에 이름을 붙여서 추가한다. * 눈대중으로 한 칸씩 조정하기 시작하면 다시 지금 상태로 돌아온다. */ /** 머리 → 본문. 머리를 직접 조판하는 섹션(무대 데모)도 이 상수를 쓴다. */ export const HEADING_GAP = "mb-14 md:mb-20" /** 탭·토글 행 → 본문. 머리 간격보다 좁아서 컨트롤이 위가 아니라 본문 쪽에 묶인다. */ export const CONTROL_GAP = "mb-12" /** 리드 문단 → 버튼 행 (히어로·최종 CTA). */ export const CTA_GAP = "mt-12" /** display 티어 제목 → 리드 문단. 히어로는 h1 이라 SectionHeading 을 못 쓰고 이 상수를 직접 쓴다. */ export const DISPLAY_LEAD_GAP = "mt-8" /* * 제목 조판 티어. 티어가 바뀌면 리드 문단까지의 간격도 같이 움직인다 — * Typography 주석의 "크기·굵기·조임은 같이 움직인다"와 같은 이유로, 간격도 딸려간다. */ const SIZES = { /** 섹션 표준 h2 (34 / 64px) */ title: { variant: "title", descriptionGap: "mt-5" }, /** 히어로·최종 CTA 급 디스플레이 (38~74px) */ display: { variant: "stageDisplay", descriptionGap: DISPLAY_LEAD_GAP }, } as const type SectionHeadingProps = { eyebrow?: string title: React.ReactNode description?: React.ReactNode align?: "left" | "center" /** 다크 무대 섹션(bg="stage")에서는 "stage" 를 준다. Typography 변형이 먹색을 물고 있어서 필요하다. */ tone?: "light" | "stage" /** 제목 조판 티어. display 는 히어로·최종 CTA 전용. */ size?: keyof typeof SIZES /** 머리 아래 표준 간격을 끈다. 뒤에 컨트롤 행이 붙는 섹션에서만 쓴다. */ gap?: "heading" | "none" className?: string /** 제목 크기 미세조정 (예: 최종 CTA 의 축소 디스플레이) */ titleClassName?: string /** 리드 문단 폭 제한·색 조정 (예: "max-w-2xl"). 세로 간격은 여기서 주지 않는다. */ descriptionClassName?: string } function SectionHeading({ eyebrow, title, description, align = "left", tone = "light", size = "title", gap = "heading", className, titleClassName, descriptionClassName, }: SectionHeadingProps) { const onStage = tone === "stage" const { variant, descriptionGap } = SIZES[size] return (
{/* eyebrow 가 없을 때 빈 span 을 렌더하면 mb + 30px 행간만큼 유령 여백이 생긴다. 지금은 모든 섹션이 eyebrow 를 넘기지만, 하나만 빼도 그 섹션만 제목이 내려앉는다. */} {eyebrow && ( {eyebrow} )} {title} {description && ( {description} )}
) } export { SectionHeading }