Layout
레이아웃 유형 · 밀도 · 그리드 · 중단점 · 화면 영역
레이아웃은 제품의 콘텐츠를 구조화하고 화면마다 같은 경험을 주는 뼈대다. 화면 밀도와 기기 폭에 맞춰 늘고 줄 수 있게 그리드와 중단점을 둔다 — 당근 SEED 의 레이아웃을 따른다.
이 값들은 아직 두 웹 · 앱과 groo-home 에 들어가지 않았다. 앱마다 PR 을 따로 내며, 그 전까지 제품 코드의 값(Desk 웹 중단점 736 · 좌우 여백 28 · 사이드바 256, HR 웹 Tailwind 기본 중단점 · 사이드바 288 등)이 이 페이지와 다른 게 정상이다.
Layout Types
사용자의 목적과 콘텐츠의 복잡도에 따라 두 유형을 쓴다.
- Dashboard Layout — 데이터를 많이 다루고 관리 기능이 중심인 화면. groo 의 두 웹(Desk 가계부 · HR 관리)이 여기에 든다.
- Contents Layout — 정보를 전하는 것이 목적인 서비스 페이지. groo-home 같은 소개 · 안내 페이지가 여기에 든다.
Dashboard Layout — 밀도
화면이 담는 정보량으로 밀도를 하나 고르고, 밀도가 콘텐츠의 최대 폭을 정한다. 폭을 제한하면 가운데 정렬한다.
- low — 간단한 설정 창이나 대시보드 요약
- medium (기본) — 일반적인 관리 도구와 데이터 목록
- high — 많은 데이터의 시각화와 편집 도구
| 밀도 | 정렬 | 칸 | 칸 사이 | 좌우 여백 | 768 – 1279 | 1280 이상 | 쓰는 화면 |
|---|---|---|---|---|---|---|---|
| low | 가운데(Centered) | 8 | 24 | 32 | 720 | 720 | 간단한 설정 창 · 대시보드 요약 |
| medium (기본) | 가운데(Centered) | 12 | 24 | 32 | 720 | 1040 | 일반 관리 도구 · 데이터 목록 |
| high | 가변(Fluid) | 전체 폭 | — | 32 | 제한 없음 | 제한 없음 | 대량 데이터 시각화 · 편집 도구 |
| 토큰 | 값 | 뜻 |
|---|---|---|
layout-max-low | 720px | low 밀도의 최대 폭 · medium 밀도의 768 – 1279 최대 폭 |
layout-max-medium | 1040px | medium 밀도의 1280 이상 최대 폭 |
768 미만은 밀도와 상관없이 칸 없이 화면 폭 전체를 쓴다. high 는 최소 폭도 두지 않는다 — 태블릿 세로(768)에서 가로 스크롤이 생기지 않게. 어느 화면이 어느 밀도인지는 앱에 적용할 때 화면 목록을 만들어 정한다.
Contents Layout
12칸으로 시스템의 짜임을 맞춘다.
- 가운데 정렬 — 읽기 좋게
breakpoint-lg(1280) 이상에서 가운데에 고정한다. - 최대 폭 — 콘텐츠에 몰입하도록 기본 1040px 을 쓰고, 검색 결과처럼 넓게 훑어야 하는 페이지는 1280px 까지 넓힌다.
| 토큰 | 값 | 뜻 |
|---|---|---|
layout-max-content | 1040px | 콘텐츠 레이아웃 기본 최대 폭 — 읽기에 몰입하게 |
layout-max-content-wide | 1280px | 넓게 훑어보는 페이지(검색 결과 · 목록)의 최대 폭 |
Grid anatomy
그리드를 이루는 요소다.
- Columns — 콘텐츠가 놓이는 칸. 밀도 · 화면 폭에 따라 수가 정해진다.
- Gutters — 칸과 칸 사이. 768 이상은 24px, 768 미만은 16px. 카드 사이도 같은 값을 쓴다.
- Margins — 콘텐츠와 화면 끝 사이. 768 이상은 32px, 768 미만은
spacing-global-gutter(24px).
| 토큰 | 값 | 뜻 |
|---|---|---|
layout-margin | 32px | 웹 페이지 좌우 여백 · 768 이상 |
layout-gutter | 24px | 웹 칸 · 카드 사이 · 768 이상 |
layout-gutter-narrow | 16px | 칸 사이 · 768 미만(v103) — 위아래로 쌓은 카드 사이는 8(2026-10-08) |
Responsive Behavior
화면 크기가 바뀔 때 두 가지 방식으로 대응한다.
- Fluid (가변형) — 중단점 사이에서 칸 폭이 브라우저 폭에 비례해 늘고 준다.
- Fixed & Centered (고정 · 가운데 정렬) — 최대 폭(예: 1040px)에 이르면 더 늘지 않고 화면 가운데에 고정된다.
한 화면에서 두 방식을 섞어 쓸 수 있다 — 예를 들어 표는 Fluid, 그 위 요약 카드 줄은 Fixed & Centered.
Breakpoint
모바일 우선이다. 작은 화면에서 시작해 화면이 커지면 레이아웃이 넓어진다.
| 구간 | 폭 | 칸 | 칸 사이 | 좌우 여백 |
|---|---|---|---|---|
| 기본(base) | 0 – 479 | 한 줄 | 16 · layout-gutter-narrow | 24 · spacing-global-gutter |
| sm | 480 – 767 | 한 줄 | 16 · layout-gutter-narrow | 24 · spacing-global-gutter |
| md | 768 – 1279 | 밀도대로(8 · 12) | 24 · layout-gutter | 32 · layout-margin |
| lg | 1280 – 1439 | 밀도대로(8 · 12) | 24 · layout-gutter | 32 · layout-margin |
| xl | 1440 이상 | 밀도대로(8 · 12) | 24 · layout-gutter | 32 · layout-margin |
| 토큰 | 값 | 구간 |
|---|---|---|
breakpoint-sm | 480px | 480 – 767 · 큰 폰 · 작은 태블릿 |
breakpoint-md | 768px | 768 – 1279 · 사이드 내비게이션이 보이기 시작한다 |
breakpoint-lg | 1280px | 1280 – 1439 · 데스크톱 |
breakpoint-xl | 1440px | 1440 이상 · 넓은 데스크톱 |
- 사이드 내비게이션은
breakpoint-md이상에서 보인다. 그 아래에서는 Desk 웹은 앱과 같은 하단 탭 바(Bottom Navigation), HR 은 상단 바의 ☰ 로 여는 왼쪽 주 메뉴(Side Panel)다 — 아래 화면 틀. - 화면 틀을 JS 로 바꾸는 기준도 같은 값이다. 스타일(
md:)과 화면 틀이 다른 폭에서 바뀌면 그 사이에서 모바일 틀 안에 태블릿 스타일이 켜진다.
Column span and offset
콘텐츠가 몇 칸을 차지하고 어디서 시작하는지를 칸 수로 정한다.
- Column span — 콘텐츠가 차지하는 칸 수. 중요한 콘텐츠에 칸을 더 줘서 위계를 만든다.
- Column offset — 콘텐츠 앞에 비워 두는 칸 수. 콘텐츠를 가운데에 두거나 좌우 균형을 맞출 때 쓴다.
Layout Regions
화면의 역할을 나누는 영역이다.
- Header (GNB) — 사이드바 오른쪽 · 본문 위의 머리(Top Navigation 의 데스크톱 머리 — Desk). 화면의 주 동작과 아이콘 버튼을 두고, 화면 제목은 본문 맨 위 제목이 맡는다. 아래에 선을 긋지 않는다. HR 은 머리를 두지 않는다.
- Side Navigation — 서비스의 주 메뉴(Side Navigation).
breakpoint-lg이상은 펼치고 그 아래는 아이콘만으로 접힌다.breakpoint-md미만에서는 Desk 하단 탭 바 · HR ☰ 주 메뉴가 맡는다. - Main Content — 사용자가 실제로 다루는 콘텐츠. 레이아웃 유형에 따라 그리드가 적용된다.
- Aside — 오른쪽에 나타나는 보조 영역. 고른 항목의 상세 · 설정처럼 부가 작업에 쓴다.
| 토큰 | 값 | 뜻 |
|---|---|---|
layout-sidebar | 240px | 사이드 내비게이션 폭 |
layout-sidebar-collapsed | 56px | 접은 사이드 내비게이션(아이콘만) |
화면 틀
화면을 감싸는 틀 — 머리(상단 바) · 주 메뉴(하단 탭 바 · 사이드바) · 본문 — 은 두 문턱에서 바뀐다. 부품의 값은 Top Navigation · Bottom Navigation · Side Navigation · Side Panel 에 있다. 가려질 수 있는 화면 가장자리는 Safe Area 를 따른다.
문턱 — 768 · 1280
| 자리 | 0 – 767 | 768 – 1279 | 1280 이상 |
|---|---|---|---|
| 머리 | 상단 바 56 — 탭 첫 화면은 큰 제목, 그 아래는 ← + 제목 | Desk 데스크톱 머리 56 · HR 은 머리 없음 | 같다 |
| 주 메뉴 | Desk 하단 탭 바 · HR ☰ → 왼쪽 옆 패널 | 사이드바 접힘 56(손으로 펼침) | 사이드바 펼침 240(손으로 접음 · 기억) |
| 화면 제목 | 상단 바의 제목 | 본문 맨 위 h1 | 같다 |
| 폼 · 상세 · 고르기 | Bottom Sheet | Bottom Sheet | Dialog · Popover |
| 동작 목록(들어간 화면 머리 ⋮ · 표 줄 ⋮ — 목록 줄에는 ⋮ 없음) | Menu Sheet | Menu Sheet | Menu |
| 접힌 사이드바의 하위 | — | 옆 펼침 메뉴(줄 44 — Menu Sheet 로 바꾸지 않는다, 2026-10-08) | 같다(손으로 접었을 때) |
| 긴 목록 | 끝없이 불러오기 | 상황에 따라 — 아래 "목록 넘기기" | 같다 |
| 데이터 표 | Table Pagination(표와 함께 가로 스크롤) | Table Pagination | Table Pagination |
| 화면의 주 동작 | 탭 바 가운데 + · 탭 바 없는 화면은 떠 있는 버튼 | 머리의 버튼 | 같다 |
| 본문 폭 | 화면 폭 | 밀도 표(720) | 밀도 표(720 · 1040) |
- Pagination 의 칸 수는 480(
breakpoint-sm)에서 9 ↔ 7 이다. - 768 – 1279 는 사이드바 틀인데 겹치는 표면은 시트다 — 터치 태블릿과 작은 노트북이 섞인 폭이라 손가락에 맞는 쪽을 둔다(시트 · 대화상자 2026-10-02 결정의 1280).
머리 · 화면 제목
- 머리(상단 바)는 높이 56 — 폰 · 데스크톱 모든 화면이 같다. 스크롤해도 선 · 그림자를 긋지 않는다(Elevation).
- 폰은 상단 바의 제목이 화면 제목(
h1)이다 — 탭 첫 화면은 22 / 30(text-t8), 그 아래 화면은 18 / 24(text-t6), 둘 다 700 · 왼쪽. - 데스크톱은 머리에 제목을 두지 않는다 — 본문 맨 위
h1이 화면 제목이다.text-screen-title(26 / 35 · 700), 머리 아래spacing-nav-to-title(20). 모든 화면에 하나다(홈 · 캘린더도). 제목 줄은 한 줄 — 제목(설명은 그 아래)이 왼쪽, 그 화면의 동작 버튼(Button small ·neutralOutline— 회색 바닥 위, 2026-10-16 사용자 결정 174-3 A)이 오른쪽이다(2026-10-16 사용자 결정 — 구현 세션 질문 Q-018-2 A). - HR 은 데스크톱 머리를 두지 않는다 — 접기 버튼은 사이드바 머리로, 테마는 사이드바 바닥 계정 메뉴의 "화면 테마"(기기 설정 · 라이트 · 다크)로 갔고(2026-10-23 사용자 결정 H-A3 A · H-A4 B), 위치는 사이드바의 지금 항목이 알린다(빵부스러기는 걷었다).
화면을 옮길 때
| 일 | 스크롤 | 초점 |
|---|---|---|
| 다른 화면으로(링크 · 사이드바 항목) | 맨 위 | 새 화면의 제목(h1) |
| 뒤로 가기 · ← | 그 화면에 있던 자리 | 그 화면의 제목 |
| 하단 탭 옮기기 | 그 탭의 마지막 화면 · 자리(탭마다 기억 — 웹도, 목록이 그려진 뒤 돌려놓는다 — 2026-10-16 Q-018-4 A) | 그 탭 화면의 제목 |
| 화면 맨 위 1차 탭 옮기기(가계부 · 자산 · 통계 · 예산) | 그 탭에 있던 자리(탭마다 기억 — Tabs) | 고른 탭 |
| 지금 탭을 다시 누름 | 맨 위 · 그 탭의 첫 화면 | 그대로 |
| 쪽 넘기기(Pagination) | 목록 맨 위 | 누른 칸 — "N페이지" 를 알린다 |
- 문서 제목은 화면마다 "{화면 제목} - 그루 담다" · "{화면 제목} - 그루 이음" 이다.
- ← 는 들어온 화면으로(History)가 기본이고, 앞 화면이 없으면 상위 화면으로 — 주소를 고쳐 쓴다. 설정처럼 "어디서 왔든 같은 곳" 이 나은 화면은 화면마다 상위 화면(Hierarchy)을 고를 수 있다 — 고르는 표는
specs/components/top-navigation.md의 "← 는 어디로"(2026-10-22 사용자 결정 4B). ✕ 는 모달 · 독립 흐름을 닫을 때만.
랜드마크
- 머리는
header—main밖에 둔다. - 주 메뉴는 이름 있는
nav— 하단 탭 바 · 사이드바 · 옆 패널의 주 메뉴 모두 "주 메뉴". - 본문은
main하나. - 화면 맨 앞에 "본문 바로가기" 링크 — 키보드 초점이 오면 보이고, 누르면
main으로 간다. - 앱은 랜드마크가 없다(Flutter 의
main·navigation역할은 웹 엔진만 쓴다). 그래서 상단 바의 제목을 머리말로 두고(Semantics(header: true, headingLevel: 1)— iOS 는header를, 안드로이드는headingLevel을 읽는다) 목록 제목(List Header) · 카드 머리는 단계 2 로 단다. 상단 바의 버튼마다 이름을 단다. 주 메뉴는 이름 있는 묶음(Semantics(container: true, label)— "주 메뉴", 번역 문구navMainMenu· en "Main menu")이다. 본문 바로가기는 두지 않는다 — 화면 읽기는 제목 단위로 건너뛰고, 상단 바의 누를 것은 셋 이하다(2026-10-16 사용자 결정 — 틀 앱 비교 페이지 02-004). - 이름은 모두 번역 문구다 — 코드에 한국어 상수를 두지 않는다(International Design). 알림 벨의 이름은 새 알림이 있으면 "알림, 새 알림 있음"(en "Notifications, new notifications"), 없으면 "알림"이다(Notification Badge — 점은
aria-hidden· 낭독 제외, 02-003).
구성과 일부 설명은 당근 SEED Design 의 Foundations 문서(Apache-2.0)를 바탕으로 했다. 값과 그림은 groo 것이다.