generated from ttipo/project-base
feat(frontend): 전역 셸·로그인·세션 처리와 웹메일 화면
확정된 디자인(docs/design/webmail)을 기준으로 만든 웹메일 화면이다. - 앞으로 붙일 사내 모듈(캘린더·파일·ERP 등)을 위한 전역 앱 레일과 셸, Pretendard 자체 호스팅, 바다 청록 브랜드 토큰과 콘텐츠 열 토큰 - 로그인과 세션 처리: 401 이면 한 번 갱신한 뒤 다시 보내고, 실패하면 세션 만료 대화상자를 띄운다. 로그아웃은 다른 탭에도 알리고, 로그아웃 뒤에 늦게 온 갱신 결과는 버린다 - 메일함(분할·목록 보기), 읽기(외부 HTML 은 sandbox iframe 으로만 그린다), 쓰기(발송 키 규칙, 첨부 형식·크기·합계·개수 사전 검사, 답장·전달 초안 길이 맞춤) - 오류는 코드로만 분기하고, 문구는 생성된 에러 사전에서 가져온다 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 전역 기본 스타일 — 요소 기본값과 공용 유틸만 둔다.
|
||||
* 값은 tokens.css 의 변수만 쓴다. 화면별 모양은 각 컴포넌트의 <style scoped> 에 둔다.
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font-family-base);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-base);
|
||||
color: var(--color-text);
|
||||
background: var(--color-surface);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-brand);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-brand-strong);
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: var(--focus-ring-width) solid var(--color-brand);
|
||||
outline-offset: var(--focus-ring-offset);
|
||||
}
|
||||
|
||||
/* 화면에는 숨기고 스크린리더에는 읽힌다 (안 읽음·준비 중 같은 상태 전달용) */
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: var(--space-1);
|
||||
height: var(--space-1);
|
||||
margin: calc(var(--space-1) * -1);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 디자인 토큰 — 색 · 간격 · 글자 크기는 이 파일 하나에만 둔다.
|
||||
* 기준: docs/design/webmail (확정본) — 브랜드 "바다 청록" 바탕 #0B3640 · 강조 #0E7C86
|
||||
* 컴포넌트는 <style scoped> 에서 var(--…) 로만 쓰고, 치수는 rem(16px = 1rem)으로 적는다.
|
||||
*
|
||||
* 화면 폭 기준 (CSS 변수는 미디어 쿼리에 쓸 수 없어 값으로 적는다):
|
||||
* 70rem(1120px) 이상 — 레일 · 메일함 · 목록 · 본문이 한 줄에 놓이고 칸마다 따로 스크롤된다.
|
||||
* 그보다 좁으면 칸이 아래로 접히고 화면 전체가 스크롤된다.
|
||||
* 목록 보기 표(MessageTable) — 68.6875rem(1099px) 이하에서 크기 칸 숨김, 37.4375rem(599px) 이하에서 칸 축소.
|
||||
*/
|
||||
:root {
|
||||
/* ── 글꼴 ─────────────────────────────── */
|
||||
--font-family-base:
|
||||
'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo',
|
||||
'Malgun Gothic', system-ui, sans-serif;
|
||||
|
||||
--font-size-caption: 0.6875rem; /* 11px — 앱 레일 이름 */
|
||||
--font-size-small: 0.75rem; /* 12px — 시각·개수·보조 설명 */
|
||||
--font-size-label: 0.78125rem; /* 12.5px — 입력 이름·안내 */
|
||||
--font-size-base: 0.8125rem; /* 13px — 기본 */
|
||||
--font-size-emphasis: 0.84375rem; /* 13.5px — 주요 버튼·보낸 사람 */
|
||||
--font-size-body: 0.875rem; /* 14px — 본문·입력 */
|
||||
--font-size-title-sm: 0.9375rem; /* 15px — 영역 제목 */
|
||||
--font-size-title: 1rem; /* 16px — 상단 바 모듈 이름 */
|
||||
--font-size-heading: 1.25rem; /* 20px — 메일 제목 */
|
||||
--font-size-heading-lg: 1.375rem; /* 22px — 로그인 제목 */
|
||||
--font-size-display: 1.875rem; /* 30px — 로그인 소개 문구 */
|
||||
|
||||
--line-height-base: 1.5;
|
||||
--line-height-body: 1.75;
|
||||
--line-height-tight: 1.3;
|
||||
--line-height-heading: 1.4;
|
||||
|
||||
--letter-spacing-tight: -0.01em;
|
||||
--letter-spacing-tighter: -0.015em;
|
||||
--letter-spacing-heading: -0.02em;
|
||||
--letter-spacing-display: -0.025em;
|
||||
|
||||
/* ── 색: 브랜드 ─────────────────────────── */
|
||||
--color-brand-surface: #0b3640; /* 레일 · 로그인 패널 · 성공 알림 바탕 */
|
||||
--color-brand-surface-active: #155a64; /* 레일 현재 모듈 */
|
||||
--color-brand: #0e7c86; /* 강조 버튼 · 링크 · 진행률 */
|
||||
--color-brand-strong: #0a5f67; /* 강조 글자 · 눌린 상태 */
|
||||
--color-brand-subtle: #e3f2f3; /* 선택한 행 · 현재 메일함 · 아바타 */
|
||||
--color-on-brand: #ffffff;
|
||||
--color-on-brand-text: #e6eff0;
|
||||
--color-on-brand-muted: #a3c2c6;
|
||||
--color-on-brand-link: #9ed6db;
|
||||
--color-on-brand-success: #9fe0b5;
|
||||
|
||||
/* ── 색: 글자 ───────────────────────────── */
|
||||
--color-text: #171c26;
|
||||
--color-text-body: #1f2733; /* 메일 본문 */
|
||||
--color-text-strong: #344054; /* 입력 이름 · 도구 버튼 */
|
||||
--color-text-secondary: #4a5363;
|
||||
--color-text-muted: #667085;
|
||||
--color-icon-muted: #98a2b3;
|
||||
|
||||
/* ── 색: 면 · 선 ────────────────────────── */
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-panel: #f8f9fb; /* 메일함 패널 */
|
||||
--color-surface-muted: #f2f4f7; /* 받는 사람 토큰 · 파일 아이콘 */
|
||||
--color-surface-sunken: #fbfbfc; /* 목록 표 머리 줄 */
|
||||
--color-row-hover: #f3f8f8; /* 목록 표 행 hover */
|
||||
--color-border: #e6e8ec;
|
||||
--color-border-subtle: #eef0f3;
|
||||
--color-border-strong: #d0d5dd; /* 보기 전환 세그먼트 테두리 */
|
||||
--color-border-input: #8a94a3;
|
||||
--color-skeleton: #e4e7ec; /* 불러오는 중 막대 · 진행률 바탕 */
|
||||
|
||||
/* ── 색: 상태 ───────────────────────────── */
|
||||
--color-danger: #b42318;
|
||||
--color-danger-surface: #fef3f2;
|
||||
--color-danger-border: #fbd7d3;
|
||||
--color-warning-surface: #fff7e6;
|
||||
--color-warning-text: #7a5200;
|
||||
--color-star: #b7791f;
|
||||
|
||||
/* ── 간격 (이름의 숫자 = px) ─────────────── */
|
||||
--space-1: 0.0625rem;
|
||||
--space-2: 0.125rem;
|
||||
--space-3: 0.1875rem;
|
||||
--space-4: 0.25rem;
|
||||
--space-6: 0.375rem;
|
||||
--space-8: 0.5rem;
|
||||
--space-9: 0.5625rem;
|
||||
--space-10: 0.625rem;
|
||||
--space-12: 0.75rem;
|
||||
--space-14: 0.875rem;
|
||||
--space-16: 1rem;
|
||||
--space-18: 1.125rem;
|
||||
--space-20: 1.25rem;
|
||||
--space-22: 1.375rem;
|
||||
--space-24: 1.5rem;
|
||||
--space-28: 1.75rem;
|
||||
--space-36: 2.25rem;
|
||||
--space-40: 2.5rem;
|
||||
--space-44: 2.75rem;
|
||||
--space-48: 3rem;
|
||||
--space-56: 3.5rem;
|
||||
--space-80: 5rem;
|
||||
|
||||
/* ── 모서리 ─────────────────────────────── */
|
||||
--radius-xs: 0.25rem; /* 받는 사람 토큰 */
|
||||
--radius-sm: 0.375rem;
|
||||
--radius-item: 0.4375rem; /* 메일함 항목 */
|
||||
--radius-md: 0.5rem; /* 버튼 · 입력 */
|
||||
--radius-lg: 0.625rem; /* 레일 항목 · 알림 */
|
||||
--radius-xl: 0.75rem; /* 대화상자 */
|
||||
--radius-round: 50%;
|
||||
|
||||
/* ── 선 · 그림자 ────────────────────────── */
|
||||
--border-width: 0.0625rem;
|
||||
--focus-ring-width: 0.125rem;
|
||||
--focus-ring-offset: 0.125rem;
|
||||
--shadow-toast: 0 0.625rem 1.75rem rgba(16, 24, 28, 0.12);
|
||||
--shadow-toast-strong: 0 0.625rem 1.75rem rgba(16, 24, 28, 0.22);
|
||||
--shadow-dialog: 0 0.875rem 2.25rem rgba(16, 24, 28, 0.14);
|
||||
--color-backdrop: rgba(11, 54, 64, 0.32);
|
||||
|
||||
/* ── 크기 ───────────────────────────────── */
|
||||
--size-touch: 2.75rem; /* 44px — 누를 수 있는 최소 영역 */
|
||||
/*
|
||||
* 밀도를 위한 작은 조작 영역 — 클릭 영역 44px 기준의 의도된 예외 (디자인 README "디자인 원칙", 사용자 확정).
|
||||
* 목록 표의 별 버튼(폭 32px · 행 높이를 채움)과 "분할 | 목록" 전환(바깥 36px)에만 쓴다.
|
||||
*/
|
||||
--size-compact-control: 2rem; /* 32px */
|
||||
--size-segment-item: 1.875rem; /* 30px — 전환 버튼 한 칸 높이 (테두리·안쪽 여백 포함 36px) */
|
||||
--size-icon-button: 2.5rem; /* 40px — 새로고침 · 이전/다음 버튼 폭 (높이는 44px) */
|
||||
--size-rail: 4.5rem; /* 72px — 앱 레일 폭 */
|
||||
--size-rail-item: 3.75rem; /* 60px */
|
||||
--size-row-min: 2.5rem; /* 40px — 메일함 항목 */
|
||||
--size-header-min: 3.25rem; /* 52px — 목록 · 본문 머리 */
|
||||
--size-topbar-min: 3.5rem; /* 56px — 상단 바 */
|
||||
|
||||
/* ── 메일 목록 행 ───────────────────────── */
|
||||
--size-list-row: 3.125rem; /* 50px — 분할 보기 2줄 행 */
|
||||
--size-table-head: 2rem; /* 32px — 목록 보기 표 머리 줄 */
|
||||
--size-table-row: 2.375rem; /* 38px — 목록 보기 표 행 */
|
||||
--table-gap: 0.75rem; /* 12px — 표 칸 사이 */
|
||||
--table-col-star: 2rem; /* 32px */
|
||||
--table-col-who: 10.5rem; /* 168px */
|
||||
--table-col-who-narrow: 7rem; /* 112px — 600px 미만 */
|
||||
--table-col-date: 5.25rem; /* 84px */
|
||||
--table-col-date-narrow: 4.5rem; /* 72px — 600px 미만 */
|
||||
--table-col-size: 4rem; /* 64px */
|
||||
--table-min-width: 25rem; /* 400px — 이보다 좁으면 가로 스크롤 */
|
||||
|
||||
/*
|
||||
* ── 콘텐츠 열 (읽기 · 쓰기 공통) ─────────────
|
||||
* 분할 보기 읽기 · 목록 보기 읽기 · 쓰기 세 곳이 이 값만 쓴다 (디자인 README "콘텐츠 열").
|
||||
*/
|
||||
--content-max-width: 50rem; /* 800px */
|
||||
--content-inline: 2rem; /* 32px — 좌우 여백. 제목·입력·툴바 첫 아이콘이 모두 이 선에서 시작한다 */
|
||||
--content-block-start: 1.25rem; /* 20px */
|
||||
--content-block-end: 2.25rem; /* 36px */
|
||||
--content-measure: 42.5rem; /* 680px — 본문 글줄 최대 폭 */
|
||||
--toolbar-button-inline: 0.625rem; /* 10px — 툴바 버튼 안쪽 좌우 여백 */
|
||||
/* 22px — 툴바 안쪽 여백. 첫 버튼의 안쪽 여백(10px)을 더하면 아이콘이 32px 선에 온다 */
|
||||
--content-toolbar-start: calc(var(--content-inline) - var(--toolbar-button-inline));
|
||||
/* 744px — 쓰기 본문 입력칸 = 글줄 680 + 좌우 여백 */
|
||||
--compose-body-max-width: calc(var(--content-measure) + var(--content-inline) * 2);
|
||||
}
|
||||
Reference in New Issue
Block a user