Skip to content

Commit 217727e

Browse files
박은우박은우
authored andcommitted
refactor(blog): align responsive reading layout
1 parent 79056bd commit 217727e

17 files changed

Lines changed: 605 additions & 144 deletions

app/layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { Metadata, Viewport } from 'next';
22
import type { ReactNode } from 'react';
33
import '@/styles/globals.css';
4+
import '@/styles/viewport/content.css';
5+
import '@/styles/viewport/mobile.css';
6+
import '@/styles/viewport/tablet.css';
47
import '@/styles/tossface.css';
58

69
import AppProviders from '@/site/providers/AppProviders';

blog/ui/mdx/components.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,27 @@ export function getMDXComponents(components: MDXComponents): MDXComponents {
5252
return {
5353
h1: createHeading(
5454
'h1',
55-
'mt-16 mb-8 text-3xl font-bold text-[var(--color-text-primary)]'
55+
'mt-16 mb-8 font-semibold text-[var(--color-text-primary)]'
5656
),
5757
h2: createHeading(
5858
'h2',
59-
'mt-12 mb-6 text-2xl font-bold text-[var(--color-text-primary)]'
59+
'mt-12 mb-6 font-semibold text-[var(--color-text-primary)]'
6060
),
6161
h3: createHeading(
6262
'h3',
63-
'mt-8 mb-4 text-xl font-bold text-[var(--color-text-primary)]'
63+
'mt-8 mb-4 font-semibold text-[var(--color-text-primary)]'
6464
),
6565
h4: createHeading(
6666
'h4',
67-
'mt-8 mb-4 text-lg font-bold text-[var(--color-text-primary)]'
67+
'mt-8 mb-4 font-semibold text-[var(--color-text-primary)]'
6868
),
6969
h5: createHeading(
7070
'h5',
71-
'mt-8 mb-4 text-base font-bold text-[var(--color-text-primary)]'
71+
'mt-8 mb-4 font-semibold text-[var(--color-text-primary)]'
7272
),
7373
h6: createHeading(
7474
'h6',
75-
'mt-8 mb-4 text-base font-bold text-[var(--color-text-primary)]'
75+
'mt-8 mb-4 font-semibold text-[var(--color-text-primary)]'
7676
),
7777
p: (props) => <p {...props}>{props.children}</p>,
7878
img: (props) => {

blog/ui/pages/BlogPostPage.tsx

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
TableOfContents,
1616
GiscusComments,
1717
SeriesNavigation,
18-
ViewCounter,
1918
} from '@/blog/ui/components';
2019
import PostViewTracker from '@/infra/analytics/components/PostViewTracker';
2120
import DwellTimeTracker from '@/infra/analytics/components/DwellTimeTracker';
@@ -154,39 +153,23 @@ export default async function BlogPostPage({
154153
<DwellTimeTracker slug={post.slug} />
155154
<ScrollDepthTracker slug={post.slug} />
156155

157-
<article className="py-10">
156+
<article className="ark-article">
158157
<Container size="md">
159158
{/* Header */}
160-
<header className="mb-10">
161-
<span className="mb-4 inline-block rounded-[var(--radius-selection)] bg-[var(--color-bg-secondary)] px-3 py-1 text-sm font-medium text-[var(--color-text-secondary)]">
159+
<header className="ark-article-header">
160+
<span className="mb-4 inline-block rounded-[var(--radius-selection)] bg-[var(--color-bg-secondary)] px-3 py-1 text-xs font-medium text-[var(--color-text-secondary)]">
162161
{post.category}
163162
</span>
164-
<h1 className="text-3xl font-bold leading-tight text-[var(--color-text-primary)] md:text-4xl">
165-
{post.title}
166-
</h1>
167-
<div className="mt-6 flex flex-wrap items-center gap-4 text-sm text-[var(--color-text-tertiary)]">
163+
<h1 className="ark-article-title">{post.title}</h1>
164+
<div className="ark-article-meta">
168165
<time>{formattedDate}</time>
169166
{readingTimeLabel && (
170167
<>
171168
<span className="h-1 w-1 rounded-full bg-[var(--color-divider)]" />
172169
<span>{readingTimeLabel}</span>
173170
</>
174171
)}
175-
<span className="h-1 w-1 rounded-full bg-[var(--color-divider)]" />
176-
<ViewCounter slug={post.slug} />
177172
</div>
178-
{post.tags && (
179-
<div className="mt-4 flex flex-wrap items-center gap-2">
180-
{post.tags.map((tag) => (
181-
<span
182-
key={tag}
183-
className="rounded-[var(--radius-selection)] bg-[var(--color-bg-secondary)] px-2.5 py-1.5 text-xs font-medium text-[var(--color-text-tertiary)]"
184-
>
185-
#{tag}
186-
</span>
187-
))}
188-
</div>
189-
)}
190173
</header>
191174

192175
{/* Series Navigation */}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# 0050. 모바일 컴팩트 탐색과 글 상세 진입 스케일을 사용한다
2+
3+
Date: 2026-07-23
4+
Status: Accepted
5+
6+
## 배경
7+
8+
현재 shell은 모바일에서도 `1fr 4fr` grid를 사용해 로고와 탐색을 왼쪽
9+
열에 고정한다. 작은 화면에서 이 열이 본문 폭을 지속적으로 차지해 글을
10+
읽을 수 있는 공간이 줄어든다.
11+
12+
또한 홈의 14px 소개 문장과 글 상세의 제목·17px 본문 사이의 대비가 커서,
13+
사용자가 글에 진입할 때 본문이 갑자기 커진 것처럼 느낄 수 있다.
14+
15+
## 결정
16+
17+
- 데스크톱의 `2fr 6fr 4fr` shell과 sticky identity rail은 유지한다.
18+
- 모바일은 단일 콘텐츠 열로 전환하고, `identity → 주요 탐색 → content`
19+
순서의 상단 영역을 사용한다.
20+
- 모바일 identity 영역은 로고와 GitHub, Email, RSS를 가로로 배치한다.
21+
주요 탐색도 별도의 가로 행으로 항상 노출한다.
22+
- 홈 소개 문장에는 16px `--text-entry` 토큰을 사용해 기존 14px보다 본문에
23+
가까운 진입 크기를 제공한다. JetBrains Mono와 24px line-height는
24+
유지한다.
25+
- 글 상세 제목은 모바일 32px, 데스크톱 40px으로 두고, 본문 내부 제목은
26+
28px, 24px, 20px 순서로 글 제목과 중복되지 않게 한다. 메타데이터와
27+
본문 사이의 간격은 의미 토큰으로 관리하고, 본문은 17px과 1.72
28+
line-height를 유지한다.
29+
- 글 상세의 시리즈 탐색, 댓글, 목차, 읽기 진행도와 MDX pipeline은
30+
변경하지 않는다.
31+
32+
## 결과
33+
34+
- 모바일 글 본문이 좌측 레일에 의해 축소되지 않는다.
35+
- 로고와 주요 이동 경로는 글을 읽기 전 상단에서 계속 발견할 수 있다.
36+
- 콘텐츠 읽기 baseline은 유지하면서 글 진입 시 제목과 본문의 시각적
37+
점프가 줄어든다.
38+
39+
## 검토한 대안
40+
41+
- 모바일 좌측 레일을 유지하고 폭만 줄인다: 본문 폭 문제를 근본적으로
42+
해결하지 못하므로 채택하지 않는다.
43+
- 모바일에서 탐색을 접기 UI로 숨긴다: 본문 폭은 확보되지만 핵심 경로의
44+
발견 가능성이 낮아지므로 채택하지 않는다.
45+
- 본문을 16px로 줄인다: 진입 대비는 낮아지지만 현재의 긴 글 가독성
46+
기준을 훼손하므로 채택하지 않는다.
47+
48+
## 검증
49+
50+
- AppShell 및 스타일 테스트로 링크 구조, 단일 열 규칙, 읽기 토큰을
51+
검증한다.
52+
- Playwright mobile 프로젝트에서 상단 탐색, article 본문 폭, 표 가로
53+
스크롤을 검증한다.
54+
- Playwright desktop 프로젝트에서 기존 2-6-4 좌표와 17px 본문 기준을
55+
검증한다.
56+
57+
## Related History
58+
59+
- [ADR 0037](0037-adopt-zero-log-grid-site-shell.md): 2-6-4 shell과
60+
기존 모바일 1-4 grid
61+
- [ADR 0038](0038-use-jetbrains-mono-for-home-statement.md): 홈 소개
62+
문장의 JetBrains Mono 사용
63+
- [ADR 0040](0040-keep-external-links-in-sticky-identity-rail.md):
64+
identity rail 외부 링크 배치

docs/adr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ ADR은 AI 협업 가이드와 별개의 문서다. 사람이 결정했든 AI가
6767
| [0047](0047-isolate-publication-policy.md) | Accepted | 공개 정책을 blog 도메인 모듈로 분리한다 |
6868
| [0048](0048-centralize-blog-publication-rules.md) | Accepted | 블로그 발행 규칙을 단일 policy 모듈로 확장한다 |
6969
| [0049](0049-share-publication-policy-data-with-audit.md) | Accepted | 발행 정책 데이터를 content audit과 공유한다 |
70+
| [0050](0050-use-compact-mobile-navigation-and-article-entry-scale.md) | Accepted | 모바일 컴팩트 탐색과 글 상세 진입 스케일을 사용한다 |
7071

7172
## 작성 조건
7273

site/shell/AppShell/AppShell.test.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@ describe('AppShell', () => {
1717

1818
const primaryNavigation = within(screen.getByLabelText('Ark 주요 탐색'));
1919

20-
expect(primaryNavigation.getByRole('link', { name: 'Archive' })).toHaveAttribute(
21-
'href',
22-
'/archive'
23-
);
24-
expect(primaryNavigation.getByRole('link', { name: 'Resume' })).toHaveAttribute(
25-
'href',
26-
'/resume'
27-
);
20+
expect(
21+
primaryNavigation.getByRole('link', { name: 'Archive' })
22+
).toHaveAttribute('href', '/archive');
23+
expect(
24+
primaryNavigation.getByRole('link', { name: 'Resume' })
25+
).toHaveAttribute('href', '/resume');
2826
expect(
2927
primaryNavigation.queryByRole('link', { name: 'Tech' })
3028
).not.toBeInTheDocument();
@@ -33,6 +31,9 @@ describe('AppShell', () => {
3331
screen.getByLabelText('Ark 외부 링크')
3432
);
3533
expect(container.querySelector('footer')).toBeNull();
34+
expect(
35+
container.querySelector('[data-page-layout="home"]')
36+
).toBeInTheDocument();
3637
});
3738

3839
it('treats existing post routes as part of Archive', () => {
@@ -43,8 +44,9 @@ describe('AppShell', () => {
4344
</AppShell>
4445
);
4546

46-
expect(
47-
screen.getByRole('link', { name: 'Archive' })
48-
).toHaveAttribute('aria-current', 'page');
47+
expect(screen.getByRole('link', { name: 'Archive' })).toHaveAttribute(
48+
'aria-current',
49+
'page'
50+
);
4951
});
5052
});

site/shell/AppShell/AppShell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export default function AppShell({ children }: AppShellProps) {
7272
<div
7373
data-page-scroll-container
7474
data-page-scroll-mode="document"
75+
data-page-layout={pathname === '/' ? 'home' : 'content'}
7576
className="ark-site-grid"
7677
>
7778
<aside className="ark-site-identity">
@@ -106,7 +107,6 @@ export default function AppShell({ children }: AppShellProps) {
106107
})}
107108
</nav>
108109
</div>
109-
110110
</div>
111111
);
112112
}

0 commit comments

Comments
 (0)