# 디자인 시스템 — "Issue 01" > AI 에이전트 네트워크 연구실 (ANL) 랜딩 페이지의 **정본(canonical) 디자인 레퍼런스**입니다. > 코드베이스를 다시 읽지 않고도 디자인 시스템을 이해할 수 있도록 작성했습니다. > 토큰의 실제 정의는 `tailwind.config.ts` 와 `app/globals.css`(`:root`)에, > 폰트 로딩은 `app/layout.tsx` 에 있습니다. --- ## 1. 한 줄 요약 (One-liner) **연구실 웹사이트를 "한 권의 인쇄 잡지(Issue 01)"로 다룬다.** 따뜻한 종이(ivory) 위에 가까운-검정 잉크(ink), 두 개의 편집 강조색 (vermillion = Agent Engineering / Orchestration, cobalt = MCP / A2A Protocol), 큰 세리프 디스플레이 타이포, 그리고 절제된 스크롤 등장 모션으로 구성된 **고대비 매거진 스프레드** 시스템입니다. 소수의 토큰에서 사이트 전체가 파생되며, 토큰 하나를 바꾸면 전 페이지에 전파됩니다. --- ## 2. 디자인 원칙 (Principles) | 원칙 | 설명 | | --- | --- | | **편집 레이아웃 (Editorial layout)** | 모든 면을 잡지 스프레드처럼 다룬다. 섹션은 `01 / 05` 넘버링과 헤어라인 괘선으로 구획하고, 큰 디스플레이 숫자가 카드를 앵커한다. | | **큰 고대비 타이포 (Big high-contrast type)** | `ink`(#0A0A0A) on `ivory`(#F5F1E8)의 강한 대비. 히어로는 `clamp()` 기반 거대 세리프(Fraunces)로 잡지 표지 스케일을 만든다. | | **의도적 여백 (Intentional whitespace)** | 종이처럼 숨 쉬게 둔다. 섹션 패딩은 `py-16 sm:py-24`, 그리드는 `gap-px bg-line` 헤어라인으로 분리해 장식 테두리를 최소화한다. | | **움직임은 절제 (Restrained motion)** | 콘텐츠는 한 번 부드럽게 자리잡고 멈춘다. 튀거나 반복되지 않으며, 단일 이징으로 페이지 전체가 일관된 호흡을 갖는다. | | **콘텐츠 우선 (Content first)** | 모든 편집 데이터는 각 페이지 상단 `// CUSTOMIZATION HOOK` 배열에 모은다. 모션·장식은 정보를 전달하는 수단이지 그 자체가 목적이 아니다. | | **한/영 혼식 (KO/EN bilingual setting)** | 한국어 세리프(Noto Serif KR) 헤드라인과 영어 디스플레이 세리프(Fraunces)를 짝지어, 국문 제목 + 영문 디스플레이 단어의 매거진 바이링구얼 조판을 만든다. | | **의미 있는 색 (Semantic accent pairing)** | 두 강조색은 장식이 아니라 연구 영역을 인코딩한다 — **vermillion = Agent Eng / Orchestration (홀수)**, **cobalt = MCP / A2A Protocol (짝수)**. | --- ## 3. 컬러 토큰 (Color Tokens) `tailwind.config.ts` 의 `theme.extend.colors` 에 정의되고, 동일 값이 `app/globals.css` 의 `:root` CSS 변수로 미러링됩니다. **두 곳을 함께** 바꾸세요 (순수 CSS 그레인·그라디언트가 CSS 변수를 재사용하기 때문). | Tailwind 토큰 | Hex | CSS 변수 | 역할 / 사용 규칙 | | --- | --- | --- | --- | | `ink` (DEFAULT) | `#0A0A0A` | `--ink` | 본문 텍스트, 괘선·테두리, 다크 면(填) | | `ink-soft` | `#262420` | — | 2차 본문 텍스트 | | `ink-mute` | `#6B655B` | — | 3차 텍스트, 숫자, 캡션 | | `ivory` | `#F5F1E8` | `--ivory` | 페이지 기본 배경(따뜻한 종이) | | `paper` | `#ECE6D6` | `--paper` | 인셋/호버용 어두운 종이 | | `line` | `#D8D1C0` | `--line` | 헤어라인 그리드 간격(`gap-px bg-line`) | | `vermillion` (DEFAULT) | `#D9342B` | `--vermillion` | **Agent Eng / Orchestration 강조(Hot)** · 홀수 항목 | | `vermillion-dark` | `#A8231C` | — | vermillion 의 호버/심도(dim) 변형 | | `cobalt` (DEFAULT) | `#1B3A8A` | `--cobalt` | **MCP / A2A Protocol 강조(Cool)** · 짝수 항목 | | `cobalt-dark` | `#122863` | — | cobalt 의 호버/심도(dim) 변형 | | `brand`, `accent` | (별칭) | — | **레거시 호환 전용.** 신규 코드에서 사용 금지 | ### 개념적 역할 매핑 (Naming note) 설계 논의에서 쓰던 개념 명칭과 실제 코드 토큰의 매핑입니다. **코드에서는 항상 실제 토큰명을 사용하세요.** ``` 개념 명칭 → 실제 코드 토큰 ───────────────────────────────────────── accent.ink → ink (주 잉크/텍스트) 종이(paper) 계열 → ivory, paper, line accent.ko (국문) → headline-ko 클래스가 font-serif-ko 로 처리(색은 ink) accent.ko-warm(강조) → vermillion (Hot, Agent Eng / Orchestration) accent.glow / dim → vermillion-dark / cobalt-dark (각 강조색의 dark 변형) Protocol/A2A 강조 → cobalt (Cool, MCP / A2A Protocol) ``` > 별도의 `accent.glow`(발광)나 중립 회색 팔레트는 현재 정의돼 있지 않습니다. 강조가 > 필요하면 vermillion/cobalt 를, 심도가 필요하면 그 `-dark` 변형을 쓰세요. ### 헤어라인 그리드 관용구 ``` gap-px bg-line → 부모 그리드의 1px 간격이 line 색으로 드러남 └ 각 자식에 bg-ivory → 인쇄 괘선 효과(추가 border 불필요) ``` --- ## 4. 타이포 시스템 (Typography) `app/layout.tsx` 에서 `next/font/google` 로 로드해 CSS 변수로 노출하고, `tailwind.config.ts` 의 `fontFamily` 에서 패밀리로 묶습니다. | 패밀리 클래스 | CSS 변수 | 서체 | 용도 | | --- | --- | --- | --- | | `font-display` | `--font-display` | **Fraunces** (영문 세리프, 300–600 + italic) | 페이지 히어로/매거진 제목, 이탤릭 강조어, 숫자 | | `font-serif-ko` | `--font-serif-ko` | **Noto Serif KR** (400/600/700) | 한국어 디스플레이/헤드라인 | | `font-sans` | `--font-sans` | **Pretendard**(선호) → `Inter`(웹 폴백) | 본문, 키커, UI | | *(mono)* | — | **미설정** | 코드/수치/라벨용 모노스페이스는 현재 미연동 — §10 FAQ 참고 | > 본문 sans 스택은 `tailwind.config.ts` 에서 `Pretendard → var(--font-sans)(Inter) > → ui-sans-serif → system-ui → Apple SD Gothic Neo → Malgun Gothic` 순입니다. > Pretendard 는 Google Fonts 로 제공되지 않아 Inter 가 웹 폴백으로 로드됩니다. ### 컴포넌트 타입 클래스 (`app/globals.css`, `@layer components`) ad-hoc 유틸리티 스택보다 아래 시맨틱 클래스를 우선 사용하세요. | 클래스 | 결과 | | --- | --- | | `.display` | 매거진 표지급 영문 세리프, `text-display`, 가는 굵기, optical sizing | | `.headline-ko` | 한국어 세리프 헤드라인, semibold, 좁은 자간 | | `.kicker` | 소문자 대문자화 아이브로우, `tracking-caps`(0.28em), 뮤트 | | `.corner-label` | tabular-nums 스프레드 넘버링("01 / 05") | | `.pull-quote` | 큰 이탤릭 세리프 인용구 | | `.container-content` | `max-w-content`(78rem) 중앙 정렬 + 반응형 패딩 | | `.rule` / `.spread-card` / `.draw-underline` | 헤어라인 괘선 / 인셋 카드 / 호버 밑줄 드로우 | ### 스케일 (clamp 기반) 뷰포트에 따라 브레이크포인트 없이 유체적으로 커집니다. | 단계 | 토큰/클래스 | 값 (clamp) | 용도 | | --- | --- | --- | --- | | 1 — Hero | `text-display` (`.display`) | `clamp(3rem, 10vw, 9rem)` / lh 0.92 | 커버 디스플레이 | | 2 — Sub-hero | `text-display-sm` | `clamp(2.25rem, 6vw, 4.5rem)` / lh 0.98 | 보조 디스플레이 | | 3 — Page H1 | (PageHeader 내) | `clamp` 없이 `text-4xl → lg:text-6xl` | 페이지 마스트헤드 한글 제목 | | 4 — Section H | `text-2xl ~ text-3xl` | 고정 | 섹션/카드 헤드라인 | | 5 — Lead | `text-base ~ text-lg` | 고정 | 리드 문단 | | 6 — Body | `text-sm` | 고정 | 본문 | | 7 — Caption | `text-xs` | 고정 | 캡션/메타 | | 8 — Kicker | `.kicker` (`text-[0.7rem]`) | 고정 | 아이브로우/라벨 | > 인트로 히어로는 인라인으로 `text-[clamp(2.5rem,8vw,5.5rem)]` 를 쓰기도 합니다. > 새 거대 제목은 가급적 `text-display` / `text-display-sm` 토큰을 재사용하세요. --- ## 5. 모션 원칙 (Motion) 모션은 **조용하고 편집적**입니다. 콘텐츠가 자리잡을 뿐, 튀지 않습니다. 타이밍 토큰은 `app/globals.css`(`--reveal-*`)와 `tailwind.config.ts`(`animation`)에 있습니다. ### 토큰 | 종류 | 토큰 | 값 | 비고 | | --- | --- | --- | --- | | Duration · reveal | `--reveal-duration` | `820ms` | 스크롤 등장 기본 | | Duration · medium | (Tailwind `duration-500`) | `500ms` | 호버 색 전환 | | Duration · marquee | `animate-marquee` 외 | `32s` / `45s`(reverse) / `60s`(slow) | 러닝 티커 | | Easing · editorial-out | `--reveal-ease` | `cubic-bezier(0.16, 1, 0.3, 1)` | 부드러운 감속(전역 단일 이징) | | Keyframes | `stream-dash` / `node-pulse` | `6s` / `4s` | HeroComposition SVG | ### 원칙 1. **스크롤 등장, 단 한 번.** `.reveal` 이 뷰포트 진입 시 fade + translate 후 유지된다(`Reveal` 컴포넌트, IntersectionObserver, 라이브러리 없음). 2. **단일 이징.** 전부 `editorial-out` 으로 ~820ms — 페이지 전체가 한 호흡. 3. **방향이 레이아웃을 인코딩.** 좌측 컬럼은 왼쪽에서, 우측은 오른쪽에서, 히어로 아트는 scale 로 들어온다. 4. **마퀴는 앰비언트.** 32–60s 연속 루프는 "관찰 대상"이 아니라 마스트헤드 배경. ### 사용처 가이드 | 모션을 **쓸 때** | 모션을 **안 쓸 때** | | --- | --- | | 섹션/카드가 처음 뷰포트에 들어올 때 `Reveal` | 동일 뷰 안에서 반복 토글되는 상태 | | 통계 수치 등장(`Counter`) | 본문 가독성에 영향을 주는 텍스트 | | 키워드/서지 마스트헤드(`Marquee`, 페이지당 1개) | 정보 전달을 모션에 의존(접근성 위배) | | 호버 색 전환(`duration-500`) | `prefers-reduced-motion: reduce` 사용자(자동 비활성화) | --- ## 6. 공유 컴포넌트 카탈로그 `components/` 의 신규 컴포넌트. 각 파일에 `CUSTOMIZATION HOOK` 주석이 있습니다. ### `Reveal` — 스크롤 등장 모션 래퍼 스크롤 진입 시 자식을 fade + translate. 모든 모션의 기본 프리미티브. ```tsx Reveal(props: { children: ReactNode; variant?: "up" | "left" | "right" | "scale"; // 진입 방향, 기본 "up" delay?: number; // 스태거(ms), transition-delay 로 적용 as?: ElementType; // 렌더 태그, 기본 "div" className?: string; }) ``` ```tsx
``` ### `SectionLabel` — 매거진 섹션 넘버링 페이지 *내부* 섹션을 `01 / 05` 넘버링 + 아이브로우로 표시. ```tsx SectionLabel(props: { index: number; total?: number; // 기본 5 label: string; // 영문 아이브로우 className?: string; }) ``` ```tsx ``` ### `Counter` — 0 → 값 카운트업 숫자 스크롤 진입 시 0에서 `value` 까지 easeOutExpo 로 틱업(rAF, 라이브러리 없음). `prefers-reduced-motion` 이면 즉시 최종값. ```tsx Counter(props: { value: number; duration?: number; // 기본 1600ms prefix?: string; suffix?: string; className?: string; }) ``` ```tsx ``` ### `Marquee` — 키워드 러닝 티커 키워드/서지/기구명을 연속 스크롤하는 마스트헤드 배너. 트랙을 2배로 렌더해 `-50%` 이동이 매끈하게 루프. `aria-hidden`(장식). ```tsx Marquee(props: { items: string[]; reverse?: boolean; // 진행 방향 반전 className?: string; }) ``` ```tsx ``` ### `HeroComposition` — 추상 편집 히어로 (인트로 전용) 인라인 SVG 일러스트(에셋 없음). **A2A = 에이전트 메시 노드 성좌(vermillion, `node-pulse`)**, **QUIC = 다중화 스트림 흐름(cobalt, `stream-dash`)**. 색은 CSS 변수 상속, 기하만 내부 정의. ```tsx HeroComposition(props: { className?: string }) ``` ```tsx ``` > **함께 쓰는 관용구:** 모든 페이지는 페이지 섹션 헤더 > → (선택) `Marquee` 밴드 1개 → 통계 밴드(`Counter` ×3, `gap-px bg-line sm:grid-cols-3`) > → `Reveal`+`SectionLabel` 로 여는 콘텐츠 섹션. 행 내 스태거는 `delay={i * 80}`~`120`. --- ## 7. 페이지별 디자인 차이 (잡지 비유) | 라우트 | index | 잡지에서의 역할 | 특징 | | --- | --- | --- | --- | | `/` | 1 | **커버 스프레드** | 거대 한/영 히어로 + `HeroComposition` 아트, Research Project PageView(3대 과제), 4대 핵심 분야 조판 그리드 | | `/members` | 2 | **기고자 페이지(Contributors)** | 인원 통계 카운터, PI 피처 스프레드, 박/석/학부 그룹을 헤어라인 그리드 + 교차 강조색으로 | | `/publications` | 3 | **목차/서지(Index & Bibliography)** | 서지 `Marquee`, 파생 통계, 번호 매긴 편집 목록(저널=vermillion, 학회=cobalt) | | `/lectures` | 4 | **강의 카탈로그(Course catalog)** | 45개 학기 학부·대학원 개설 교과목 이력 | | `/standardization` | 5 | **표준화 바이라인(Standards byline)** | 기구 `Marquee`, 통계, 기관별 2열 기여 그리드, 기관마다 강조색 배정 | --- ## 8. 새 페이지 추가 체크리스트 1. **내비게이션 배지 & 마스트헤드:** `Header.tsx` 활성 배지(`bg-vermillion text-white dark:text-ivory`, `aria-current="page"`)와 페이지 타이틀 섹션으로 시작. 2. **콘텐츠 분리:** 편집 데이터 전부를 파일 상단 `// CUSTOMIZATION HOOK` 배열에 둔다. 3. **레이아웃:** 헤어라인 그리드 관용구(`gap-px bg-line` + 자식 `bg-ivory`)로 구성, 섹션은 `border-b border-ink` 로 구획. 4. **모션:** 등장 가치가 있는 블록을 `Reveal` 로 감싸고, §2 페어링 규칙대로 vermillion/cobalt 를 교차 배정. 5. **토큰만 사용:** 디자인 토큰(`ink`/`ivory`/`paper`/`line`/`vermillion`/`cobalt`)과 컴포넌트 타입 클래스만 사용 — 임의 회색·팔레트 밖 hex 금지. --- ## 9. 접근성 (a11y) 고려사항 - **대비비 (Contrast).** 본문은 `ink`(#0A0A0A) on `ivory`(#F5F1E8)로 WCAG AAA 수준의 강한 대비. 보조 텍스트는 `ink-soft`/`ink-mute` 까지만 낮추고, 작은 본문에 `ink-mute` 단독 사용은 지양. 강조색 위 텍스트는 항상 `ivory`(흰 종이색)로 뒤집어 대비 확보. - **모션 줄임 (Reduced motion).** `@media (prefers-reduced-motion: reduce)` 에서 `.reveal`·마퀴·`[class*="animate-"]`·smooth scroll 을 전부 비활성화. `Counter` 도 즉시 최종값으로 표기. **모션에 정보를 싣지 말 것**(원칙 §5). - **키보드 포커스 (Keyboard focus).** 링크/버튼은 시맨틱 요소(``, `