Skip to main content
ENKO

레이아웃

레이아웃은 Senco 화면의 구조와, 화면 크기에 따라 콘텐츠가 다시 배치되는 방식을 정한다.

Senco는 글자 그대로 모바일 우선이다. 휴대폰 레이아웃이 곧 디자인이고, 데스크톱 레이아웃은 같은 레이아웃에 공간을 더 준 것뿐이다. 이 순서를 지키기 때문에 두 레이아웃이 서로 다른 제품처럼 갈라지지 않는다. 송금 플로우가 1440px 모니터에서도 360px 휴대폰과 같은 모양인 것도 그래서다.

그리드 시스템

브레이크포인트에 맞춰 바뀌는 열 기반 반응형 그리드다. 콘텐츠는 다시 배치될 뿐, 작게 줄어들지 않는다.

BreakpointWidthColumnsGutterMargin
Mobile< 600px416px16px
Tablet600–1023px816px24px
Desktop1024–1439px1224px32px
Wide≥ 1440px1224pxauto (최대 1400px)

브레이크포인트 토큰: --ds-bp-mobile (600px), --ds-bp-tablet (1024px), --ds-bp-desktop (1440px).

줄이지 말고 다시 배치한다

화면이 작아지면 콘텐츠가 차지하는 열 수가 바뀔 뿐, 크기가 작아지지는 않는다. 16px 라벨은 어느 브레이크포인트에서나 16px이다. 바뀌는 건 카드 세 개가 나란히 있던 행이 한 열로 쌓인다는 점이다.

크기를 줄이기 시작하면, 글자를 가장 크게 봐야 하는 사용자의 기기에 13px 텍스트가 뜨게 된다.

브레이크포인트별 열 범위

ContentMobile (4)Tablet (8)Desktop (12)
전체 폭 섹션4812
세 개가 한 행인 카드4 (세로로 쌓음)4 (한 행에 둘)4 (한 행에 셋)
송금 플로우 열46, 가운데 정렬4, 가운데 정렬
대시보드 본문 / 사이드4 / 4, 세로로 쌓음8 / 8, 세로로 쌓음8 / 4
설정 내비 / 패널4 (탭을 위에)8 (탭을 위에)3 / 9

송금 플로우는 화면이 넓어질수록 오히려 좁아진다. 4열 중 4열을 쓰다가 12열 중 4열을 쓴다. 의도한 것이고, 모바일 우선 원칙이 가장 잘 드러나는 부분이다. 공간이 늘어나도 폼은 넓어지지 않고, 같은 폼 주변의 여백만 넓어진다.

브레이크포인트는 레이아웃에만 쓴다

컴포넌트는 뷰포트 크기를 보지 않는다. 주어진 컨테이너를 채울 뿐이고, 컨테이너 폭은 레이아웃이 정한다. @media (max-width: 600px)에 반응하는 카드는 데스크톱 사이드바에 넣는 순간 깨진다.

좁아지면 보조 줄을 숨기는 거래 행처럼 컴포넌트가 정말 자기 폭에 반응해야 할 때는, 미디어 쿼리가 아니라 컨테이너 쿼리를 쓴다.

Responsive grid — columns, gutters, margins
1024–1439px
32px
24px
Breakpoint
1024–1439px
Columns
12
Gutter
24px
space-5
Margin
32px
space-6
Container
Fluid
Column spanning
span 12 — header
span 3
span 9 — hero
span 3
span 3
span 3
span 12 — footer
Sidebar (3) + content area (9) with 3-column card grid.

컨테이너

TypeBehaviorUsage
Fluid사용 가능한 폭을 채운다전체 폭 섹션
Constrained최대 폭을 두고 콘텐츠를 가운데 정렬일반 콘텐츠 페이지
Full-bleed화면 끝에서 끝까지 채우고 페이지 패딩을 무시히어로 배너, 배경

Constrained 폭은 sizing 토큰(--ds-container-xs부터 --ds-container-max까지)으로 정한다. Sizing 참고.

컨테이너 고르기

ScreenContainerWhy
Transfer flowsm (480px)한 열로 순서대로 진행한다. 넓으면 순서를 건너뛰며 훑어보게 된다
Settingslg (960px)데스크톱에서는 내비와 패널 두 열
Dashboardlg (960px)본문 열과 사이드 레일
문서·콘텐츠md (640px)한 줄 길이를 45–75자로 유지한다
마케팅 히어로Full-bleed배경이 콘텐츠 열 밖까지 이어진다

콘텐츠는 폭을 제한한 Full-bleed

Full-bleed 섹션에서 끝까지 채우는 건 배경뿐이다. 텍스트는 여전히 Constrained 컨테이너 안에 둔다. 배경이 아무리 좋아도 1400px 폭으로 이어지는 헤드라인은 읽을 수 없다.

Do / Don't
Do

Content centered in a constrained container with margins. Comfortable reading.

Don't

Content stretches edge-to-edge. Line lengths become unreadable on wide screens.

레이아웃 기본 요소

PrimitivePurpose
Container콘텐츠 폭을 제한하고 좌우 패딩을 준다
Grid콘텐츠를 반응형 열로 배치한다
Stack같은 간격 하나로 세로로 나열한다
Row같은 간격 하나로 가로로 나열한다. 기본적으로 줄바꿈된다

Stack과 Row를 쓰는 이유는 간격을 한곳에서 관리하기 위해서다. Stack으로 만든 화면은 그룹마다 간격 값이 딱 하나이고 자식 요소에는 margin이 없다. Spacing의 규칙을 리뷰에 맡기지 않고 구조로 지키게 하는 셈이다.

Header와 Footer는 템플릿 단위의 영역이다. Templates > Basic page 참고.

안전 영역

모바일 레이아웃은 env(safe-area-inset-*)를 지킨다. 하단 탭 바와 고정 액션 바는 모두 홈 인디케이터 위에 둔다. inset을 적용하지 않으면 주요 송금 액션이 제스처 바 밑으로 들어가는데, 거기서는 탭의 3분의 1 정도를 OS가 가져가 버린다.

반응형 원칙

  • 모바일 우선. 기본 스타일은 모바일 기준이고, 브레이크포인트에서 확장한다
  • 내비게이션은 모바일에서는 하단 탭, 데스크톱에서는 사이드바로 바뀐다
  • 모바일에서 터치 타깃은 최소 48px을 지킨다
  • 차지하는 열 수는 브레이크포인트마다 달라진다 (예: 데스크톱에서 3열이던 카드가 모바일에서는 한 열로 쌓인다)
  • 320px 폭과 200% 확대에서도 가로 스크롤 없이 콘텐츠가 다시 배치된다 (WCAG 1.4.10)
  • 레이아웃은 rem과 비율 단위로 잡는다. 그래야 OS 글자 크기를 키웠을 때 잘리지 않고 다시 배치된다

권장과 금지

DoDon't
Let the transfer flow stay 480px on a wide screenStretch a single-column form to fill 1200px
Reflow a three-card row into a stackScale three cards down to fit side by side on mobile
Use a container query for component-level responsePut a viewport media query inside a component
Apply safe-area insets to fixed bottom chromePin an action bar to bottom: 0 and lose it under the home indicator
Keep gaps on the parent via gapAdd margins to grid children to fake a gutter

디자인과 접근성

  • 시맨틱 랜드마크를 쓴다 (header, main, footer, nav)
  • 그리드가 스크린 리더의 읽기 순서를 깨면 안 된다. 화면상 순서와 DOM 순서가 같아야 한다
  • 두 순서가 어긋나게 되는 곳에는 order와 row-reverse를 쓰지 않는다
  • 컨테이너 안에 키보드 포커스를 가두지 않는다
  • <main>은 페이지마다 하나만 두고, 건너뛰기 링크가 여기를 가리키게 한다
  • 같은 랜드마크가 반복되면 라벨을 붙인다 (<nav>가 둘이면 서로 다른 aria-label이 필요하다)

알려진 공백

ItemStatus
Stack / Row 기본 요소여기서 문서화했지만 컴포넌트로 구현해 출시하지는 않음
컨테이너 쿼리권장 방식으로 문서화했지만 아직 쓰는 컴포넌트가 없음
그리드 열 토큰열 수와 거터는 문서화했지만 --ds- 변수로 정의하지 않음
Safe-area 처리프로토타입 화면에는 적용했지만 레이아웃 기본 요소로 정리하지 않음