크라우니 디자인 표준 v1.0
정본 출처: kps.crowny.org 디자인 계약(Design Contract) 작성일: 2026-06-27 적용 대상: 모든 crowny.org 서비스 (cowork 우선 적용)
왜 이 문서가 필요한가
지금 crowny 서비스들은 저마다 다른 색을 쓴다. cowork는 보라·파랑, finance는 골드, kps는 초콜릿+골드다. 처음 방문한 사람은 같은 회사 사이트인지 알기 어렵다.
이 문서는 모든 crowny 서비스가 함께 따를 하나의 디자인 언어를 정한다. kps.crowny.org를 기준으로 삼고, 중학생도 이해할 수 있는 쉬운 표현을 원칙으로 한다.
1. 색 (Color)
1-1. 기본 팔레트 (원시 색상)
이 색들은 그 자체로는 직접 쓰지 않는다. 아래 "이름표 색상"에서 가져다 쓴다.
css:root {
/* 초콜릿 계열 — 무게감, 신뢰 */
--choco: #3B2A1D; /* 진한 초콜릿 */
--choco-2: #2C1E13; /* 더 진한 초콜릿 */
--choco-deep: #1A130D; /* 가장 진한 초콜릿 (다크모드 배경) */
/* 크림 계열 — 부드러움, 따뜻함 */
--cream-50: #FBF7EE; /* 가장 밝은 크림 (카드 배경) */
--cream-100: #F4EFE2; /* 기본 배경 */
--cream-200: #ECE5D5; /* 약간 어두운 배경 */
--cream-300: #E2D8C4; /* 구분선, 경계 */
/* 골드 계열 — 포인트, 액션 */
--gold: #B58A4A; /* 기본 골드 */
--gold-dim: #8F6E3A; /* 어두운 골드 (hover 시) */
--gold-soft: #D8C29A; /* 밝은 골드 (다크모드 강조) */
--gold-wash: #EFE4CD; /* 연한 골드 (배경 강조) */
--gold-bright: #C9A35A; /* 생생한 골드 */
/* 잉크 계열 — 글씨 */
--ink: #2B2018; /* 기본 글씨 색 */
--ink-soft: #5C4C3C; /* 보조 글씨 색 */
--muted: #978872; /* 흐린 글씨 색 */
/* 상태 색 */
--success: #4E8C5A; /* 성공, 완료 */
--warning: #C08842; /* 경고 */
--danger: #B5524A; /* 오류, 삭제 */
}
1-2. 이름표 색상 (Semantic Tokens) — 실제로 사용하는 색
css:root {
/* 배경 */
--bg: var(--cream-100); /* 페이지 배경 */
--bg-soft: var(--cream-200); /* 약간 강조된 배경 */
--surface: var(--cream-50); /* 카드 배경 */
--surface-2: var(--cream-200); /* 중첩 카드 배경 */
/* 글씨 */
--text: var(--ink); /* 제목, 본문 */
--text-soft: var(--ink-soft); /* 보조 설명 */
--text-muted: var(--muted); /* 날짜, 태그 등 흐린 글씨 */
/* 강조 (액션, 링크) */
--accent: var(--gold);
--accent-dim: var(--gold-dim); /* 링크 기본 색 */
--accent-soft: rgba(181,138,74,.12); /* 배지 배경 */
--accent-wash: var(--gold-wash);
--on-accent: #FBF7EE; /* 골드 버튼 위 글씨 */
/* 경계선 */
--border: rgba(43,32,24,.12);
--border-strong:rgba(43,32,24,.20);
}
1-3. 다크 모드 (Dark Mode)
두 가지 방식을 모두 지원한다.
방식 A: 시스템 설정 자동 감지 (OS가 다크 모드이면 자동 적용)
css@media (prefers-color-scheme: dark) {
:root {
--bg: #1A130D;
--bg-soft: #221810;
--surface: #241A12;
--surface-2: #2E2117;
--text: #F4EFE2;
--text-soft: #D8C9B6;
--text-muted: #A89A82;
--accent: #D8C29A;
--accent-dim: #C9A35A;
--accent-soft: rgba(216,194,154,.14);
--accent-wash: #2E2117;
--border: rgba(244,239,226,.12);
--border-strong:rgba(244,239,226,.22);
--on-accent: #1A130D;
--shadow-sm: 0 1px 2px rgba(0,0,0,.3),0 2px 8px rgba(0,0,0,.28);
--shadow-md: 0 8px 22px -8px rgba(0,0,0,.5),0 2px 8px rgba(0,0,0,.3);
--shadow-lg: 0 36px 80px -30px rgba(0,0,0,.7),0 10px 26px -14px rgba(0,0,0,.45);
--shadow-hi: inset 0 1px 0 rgba(255,255,255,.06);
}
}
방식 B: 수동 토글 버튼으로 전환 (data-theme="dark" 또는 "light")
css[data-theme="dark"] {
--bg:#1A130D; --bg-soft:#221810; --surface:#241A12; --surface-2:#2E2117;
--text:#F4EFE2; --text-soft:#D8C9B6; --text-muted:#A89A82;
--accent:#D8C29A; --accent-dim:#C9A35A;
--accent-soft:rgba(216,194,154,.14); --accent-wash:#2E2117;
--border:rgba(244,239,226,.12); --border-strong:rgba(244,239,226,.22);
--on-accent:#1A130D;
}
[data-theme="light"] {
--bg:var(--cream-100); --surface:var(--cream-50);
--text:var(--ink); --accent:var(--gold);
--border:rgba(43,32,24,.12); --on-accent:#FBF7EE;
}
2. 글씨체 (Typography)
2-1. 폰트 패밀리
html<!-- head에 반드시 포함 -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet">
css:root {
--sans: 'Pretendard', system-ui, -apple-system, 'Apple SD Gothic Neo',
'Segoe UI', 'Noto Sans KR', sans-serif;
--serif: 'Noto Serif KR', 'Nanum Myeongjo', serif;
--mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
| 변수 | 용도 |
|---|---|
--sans | 본문, UI 버튼, 레이블 |
--serif | 제목(h1~h3), 브랜드명 |
--mono | 코드 블록, 명령줄 |
font-family 목록에만 있으면 해당 폰트가 없는 컴퓨터에선 적용되지 않는다.2-2. 글씨 크기 체계
css/* 제목 계열 (serif) */
h1 { font-size: clamp(28px, 5vw, 48px); font-weight: 700; line-height: 1.18; }
h2 { font-size: clamp(22px, 3.5vw, 32px); font-weight: 600; line-height: 1.25; }
h3 { font-size: clamp(17px, 2.5vw, 22px); font-weight: 600; line-height: 1.3; }
/* 본문 계열 (sans) */
.text-lg { font-size: 17px; line-height: 1.75; }
.text-md { font-size: 15px; line-height: 1.7; } /* 기본 */
.text-sm { font-size: 13px; line-height: 1.65; }
.text-xs { font-size: 11px; line-height: 1.6; }
3. 간격 (Spacing)
8px 리듬 기반. 이 숫자들만 사용한다. 중간 숫자는 쓰지 않는다.
css:root {
--s-1: 4px; /* 아주 작은 내부 여백 */
--s-2: 8px; /* 작은 간격 */
--s-3: 12px; /* 뱃지 내부 여백 */
--s-4: 16px; /* 기본 내부 여백 */
--s-5: 24px; /* 카드 내부 여백, 섹션 여백 */
--s-6: 32px; /* 섹션 사이 */
--s-7: 48px; /* 큰 섹션 사이 */
--s-8: 64px; /* 모바일 섹션 */
--s-9: 96px; /* 데스크탑 섹션 */
--s-10:128px; /* 히어로 섹션 */
}
4. 모서리 둥글기 (Border Radius)
css:root {
--r-xs: 8px; /* 작은 뱃지 */
--r-sm: 12px; /* 카드 (소형) */
--r-md: 18px; /* 카드 (기본) */
--r-lg: 28px; /* 대형 카드, 모달 */
--r-xl: 40px; /* 큰 배너 */
--r-pill:999px; /* 버튼, 태그, 인풋 */
}
5. 그림자 (Shadow)
모두 초콜릿 톤의 따뜻한 그림자다. 파란 그림자는 사용하지 않는다.
css:root {
/* 라이트 모드 */
--shadow-sm: 0 1px 2px rgba(59,42,29,.06), 0 2px 6px rgba(59,42,29,.05);
--shadow-md: 0 6px 16px -6px rgba(59,42,29,.14), 0 2px 6px rgba(59,42,29,.06);
--shadow-lg: 0 30px 70px -28px rgba(59,42,29,.30), 0 8px 20px -12px rgba(59,42,29,.14);
--shadow-hi: inset 0 1px 0 rgba(255,255,255,.55); /* 하이라이트 (버튼 안쪽) */
--glow: 0 0 28px rgba(181,138,74,.22), 0 0 1px rgba(181,138,74,.4);
}
6. 모션 (Motion)
css:root {
--ease: cubic-bezier(.22,.61,.36,1); /* 기본 */
--ease-emph: cubic-bezier(.2,0,0,1); /* 강조 */
--ease-spring: cubic-bezier(.34,1.56,.64,1); /* 탄성 */
--dur-1: 160ms; /* 빠른 반응 (hover) */
--dur-2: 240ms; /* 기본 전환 */
--dur-3: 360ms; /* 느린 전환 (팝업) */
}
/* 화면에 들어올 때 자연스럽게 나타나는 애니메이션 */
.reveal {
opacity: 0;
transform: translateY(22px);
transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.reveal.in { opacity: 1; transform: none; }
/* 움직임에 민감한 사용자 배려 */
@media (prefers-reduced-motion: reduce) {
.reveal { opacity: 1 !important; transform: none !important; transition: none; }
html { scroll-behavior: auto; }
}
7. 레이아웃 컨테이너
css:root {
--maxw: 1080px; /* 기본 최대 너비 */
--maxw-narrow: 720px; /* 좁은 레이아웃 (글 읽기용) */
}
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s-5); }
.wrap-narrow { max-width: var(--maxw-narrow); margin: 0 auto; padding: 0 var(--s-5); }
section { padding: var(--s-9) 0; }
@media (max-width: 760px) { section { padding: var(--s-8) 0; } }
8. 컴포넌트 (Components)
8-1. 버튼 (Button)
css/* 기본 버튼 (테두리형) */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
padding: 11px 20px;
border-radius: var(--r-pill);
cursor: pointer;
border: .5px solid var(--border);
background: var(--surface);
color: var(--text-soft);
transition: all var(--dur-1) var(--ease);
min-height: 44px; /* 터치 영역 최소 크기 — 접근성 */
text-decoration: none;
}
.btn:hover {
border-color: var(--accent);
color: var(--accent);
transform: translateY(-1px);
}
/* 주요 버튼 (골드 채우기) */
.btn--primary {
background: var(--accent);
color: var(--on-accent);
border-color: var(--accent);
box-shadow: var(--shadow-hi);
}
.btn--primary:hover {
background: var(--gold-dim);
color: var(--on-accent);
}
/* 위험 버튼 (빨강) */
.btn--danger {
background: var(--danger);
color: #fff;
border-color: var(--danger);
}
/* 크기 변형 */
.btn--sm { font-size: 12px; padding: 7px 14px; min-height: 36px; }
.btn--lg { font-size: 16px; padding: 14px 28px; min-height: 52px; }
쉬운 라벨 원칙:
- "부트스트랩" → "자동 시작 설치"
- "동기화" → "다른 컴퓨터로 복사"
- "블록체인 커밋" → "변경 내용 저장"
8-2. 카드 (Card)
css/* 기본 카드 */
.card {
background: var(--surface);
border: .5px solid var(--border);
border-radius: var(--r-md);
padding: var(--s-5);
box-shadow: var(--shadow-sm);
transition:
border-color var(--dur-2) var(--ease),
transform var(--dur-2) var(--ease),
box-shadow var(--dur-2) var(--ease);
}
.card:hover {
border-color: var(--accent);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
/* 강조 카드 (히어로 안 등) */
.card--featured {
border-color: var(--accent);
background: linear-gradient(135deg, var(--surface), var(--accent-wash));
}
/* 작은 카드 */
.card--sm {
border-radius: var(--r-sm);
padding: var(--s-4);
}
8-3. 입력창 (Input)
css.input {
width: 100%;
padding: 12px 16px;
background: var(--surface);
border: .5px solid var(--border);
border-radius: var(--r-pill);
color: var(--text);
font-family: var(--sans);
font-size: 14px;
transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
min-height: 44px; /* 터치 접근성 */
}
.input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.input::placeholder { color: var(--text-muted); }
/* 라벨 */
.input-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text-soft);
margin-bottom: var(--s-2);
}
8-4. 네비게이션 (Nav)
css/* 고정 네비게이션 */
#nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 50;
transition:
background var(--dur-2) var(--ease),
border-color var(--dur-2) var(--ease),
box-shadow var(--dur-2) var(--ease);
background: transparent;
border-bottom: .5px solid transparent;
}
/* 스크롤하면 불투명 배경 */
#nav.scrolled {
background: color-mix(in srgb, var(--bg) 86%, transparent);
backdrop-filter: saturate(150%) blur(12px);
-webkit-backdrop-filter: saturate(150%) blur(12px);
border-bottom-color: var(--border);
box-shadow: var(--shadow-sm);
}
.nav-wrap {
max-width: var(--maxw);
margin: 0 auto;
padding: 10px var(--s-5);
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--s-4);
}
/* 브랜드 로고 영역 */
.nav-brand {
display: inline-flex;
align-items: center;
gap: 10px;
color: var(--text);
text-decoration: none;
}
.nav-mark {
width: 32px; height: 32px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, var(--gold), var(--gold-soft));
color: var(--on-accent);
font-family: var(--serif);
font-weight: 700;
box-shadow: var(--shadow-hi);
}
.nav-name {
font-family: var(--serif);
font-weight: 600;
font-size: 16px;
}
/* 네비 링크 */
.nav-link {
font-size: 13.5px;
font-weight: 500;
color: var(--text-soft);
padding: 7px 12px;
border-radius: var(--r-pill);
transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
text-decoration: none;
}
.nav-link:hover { color: var(--text); background: var(--accent-soft); }
/* 테마 토글 버튼 */
.theme-toggle {
width: 38px; height: 38px;
border-radius: var(--r-pill);
border: .5px solid var(--border);
background: var(--surface);
color: var(--text-soft);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.theme-toggle:hover { border-color: var(--accent); color: var(--accent); }
/* 아이콘 전환 */
.theme-toggle .ic-moon { display: none; }
[data-theme="dark"] .theme-toggle .ic-sun { display: none; }
[data-theme="dark"] .theme-toggle .ic-moon { display: block; }
/* 모바일: 링크 숨기기 */
@media (max-width: 680px) {
.nav-link:not(.nav-link--cta) { display: none; }
.nav-name { display: none; }
}
8-5. 뱃지 (Badge)
css/* 기본 뱃지 (골드) */
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11.5px;
font-weight: 700;
letter-spacing: .03em;
padding: 4px 11px;
border-radius: var(--r-pill);
background: var(--accent-soft);
color: var(--accent-dim);
font-family: var(--sans);
}
.badge .dot {
width: 6px; height: 6px;
border-radius: 50%;
background: var(--accent);
box-shadow: var(--glow);
}
/* 상태 뱃지 */
.badge--success { background: rgba(78,140,90,.14); color: #3a7a4a; }
.badge--warning { background: rgba(192,136,66,.14); color: #8f6e3a; }
.badge--danger { background: rgba(181,82,74,.14); color: #8a3a36; }
.badge--muted { background: var(--bg-soft); color: var(--text-muted); }
/* 등급 뱃지 (cowork 전용) */
.tier-badge--4 { background: rgba(78,140,90,.14); color: #3a7a4a; } /* 완전싱크 */
.tier-badge--3 { background: rgba(181,138,74,.14); color: var(--gold-dim); } /* 정기싱크 */
.tier-badge--2 { background: rgba(192,136,66,.14); color: #8f6e3a; } /* 선택싱크 */
.tier-badge--1 { background: rgba(181,82,74,.14); color: #8a3a36; } /* 로컬보관 */
8-6. 탭 (Tab)
css/* 탭 컨테이너 */
.tabs {
display: flex;
gap: 2px;
padding: var(--s-2);
background: var(--bg-soft);
border-radius: var(--r-md);
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
/* 탭 버튼 */
.tab {
flex: 0 0 auto;
padding: 8px 16px;
border-radius: var(--r-sm);
font-family: var(--sans);
font-size: 13px;
font-weight: 600;
color: var(--text-muted);
cursor: pointer;
white-space: nowrap;
transition: all var(--dur-1) var(--ease);
border: none;
background: none;
}
.tab:hover { color: var(--text-soft); background: var(--accent-soft); }
.tab.on { background: var(--accent); color: var(--on-accent); }
/* 모바일: 하단 고정 탭 바 */
@media (max-width: 640px) {
.tabs {
position: fixed;
bottom: 0; left: 0; right: 0;
z-index: 50;
border-top: .5px solid var(--border);
border-radius: 0;
padding: 6px 4px;
justify-content: space-around;
background: color-mix(in srgb, var(--bg) 95%, transparent);
backdrop-filter: blur(12px);
}
body { padding-bottom: 56px; }
}
8-7. 코드 블록 (Code Block)
css.code-block {
background: var(--choco-deep);
border: .5px solid var(--border);
border-radius: var(--r-sm);
padding: var(--s-4);
font-family: var(--mono);
font-size: 13px;
color: var(--gold-soft);
white-space: pre-wrap;
word-break: break-all;
position: relative;
line-height: 1.6;
}
[data-theme="light"] .code-block {
background: var(--choco-2);
color: var(--gold-wash);
}
/* 복사 버튼 */
.code-block .copy-btn {
position: absolute;
top: 8px; right: 8px;
}
8-8. 히어로 섹션 (Hero)
처음 화면에서 사이트를 소개하는 큰 영역이다.
css.hero {
position: relative;
min-height: 70vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: var(--s-9) var(--s-5) var(--s-8);
overflow: hidden;
background-color: var(--bg);
}
/* 배경 빛 번짐 효과 */
.hero::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 72% 56% at 50% 38%, var(--accent-wash) 0%, transparent 70%),
radial-gradient(ellipse 40% 30% at 50% 50%, var(--gold-soft) 0%, transparent 60%);
opacity: .38;
pointer-events: none;
z-index: 0;
}
/* 배경 점 패턴 (4상 균형 3진 상징) */
.hero::after {
content: '';
position: absolute;
inset: 0;
background-image:
radial-gradient(circle 1.2px at 0 0, var(--accent-dim) 100%, transparent 100%),
radial-gradient(circle 0.8px at 0 0, var(--gold-dim) 100%, transparent 100%);
background-size: 36px 36px, 72px 72px;
background-position: 0 0, 18px 18px;
opacity: .13;
pointer-events: none;
z-index: 0;
}
.hero-inner {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--s-5);
max-width: var(--maxw-narrow);
}
9. 접근성 (Accessibility)
최소 터치 영역
버튼, 링크, 입력창은 최소 44×44px을 확보한다. 손가락으로 누를 때 잘못 누르는 것을 줄인다.css.btn, .tab, .input, .nav-link { min-height: 44px; }
포커스 표시
키보드로 탭 이동할 때 현재 위치를 알 수 있게 한다.css:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: var(--r-xs);
}
/* 마우스 클릭 시에는 포커스 링 숨기기 */
:focus:not(:focus-visible) { outline: none; }
색 대비
- 배경
#F4EFE2위 글씨#2B2018: 대비 비율 12.1:1 (WCAG AA 통과) - 골드
#B58A4A위 글씨#FBF7EE: 대비 비율 4.6:1 (WCAG AA 통과)
스크린 리더 지원
html<!-- 아이콘만 있는 버튼에는 반드시 aria-label 추가 -->
<button class="theme-toggle" aria-label="다크 모드 전환">
<svg ...></svg>
</button>
<!-- 상태 표시에는 role 추가 -->
<div class="badge badge--success" role="status" aria-live="polite">완료</div>
10. 반응형 (Responsive)
기준점 (Breakpoint)
| 기준점 | 크기 | 대상 |
|---|---|---|
| 모바일 | ~ 640px | 스마트폰 |
| 태블릿 | 641px ~ 880px | 태블릿, 소형 노트북 |
| 데스크탑 | 881px ~ | 일반 PC, 윈도우 브라우저 |
css/* 모바일 우선 설계 (작은 화면에서 기본값 정의, 큰 화면에서 확장) */
.svc-grid {
display: grid;
grid-template-columns: 1fr; /* 모바일: 1열 */
gap: var(--s-3);
}
@media (min-width: 480px) {
.svc-grid { grid-template-columns: repeat(2, 1fr); } /* 태블릿: 2열 */
}
@media (min-width: 880px) {
.svc-grid { grid-template-columns: repeat(3, 1fr); } /* 데스크탑: 3열 */
}
윈도우 브라우저 주의사항
- Clipboard API:
navigator.clipboard.writeText()는 HTTPS에서만 작동한다.
javascript// 올바른 복사 함수 — HTTP 폴백 포함
function copyText(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text)
.then(() => showCopied())
.catch(() => fallbackCopy(text));
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const el = document.createElement('textarea');
el.value = text;
el.style.cssText = 'position:fixed;opacity:0;top:-100px';
document.body.appendChild(el);
el.focus(); el.select();
try { document.execCommand('copy'); showCopied(); }
catch (e) { alert('클립보드에 복사하려면 Ctrl+C를 눌러주세요.'); }
document.body.removeChild(el);
}
function showCopied() { /* "복사됨!" 피드백 표시 */ }
- 한글 파일 다운로드:
Content-Disposition헤더는 RFC 5987 방식으로 인코딩해야 한다.
javascript// Node.js 서버에서
const encoded = encodeURIComponent(fileName);
res.setHeader('Content-Disposition',
`attachment; filename="fallback.zip"; filename*=UTF-8''${encoded}`);
- Blob URL 누수:
URL.createObjectURL()후 반드시URL.revokeObjectURL()을 호출한다.
javascriptconst url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = fileName;
document.body.appendChild(a); a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 1000); // 1초 후 해제
11. 쉬운 라벨 원칙 (Plain Language)
중학생이 이해할 수 있는 말을 쓴다. 전문 용어는 필요할 때만, 반드시 짧은 설명을 달아야 한다.
| 기존 표현 | 바꿀 표현 |
|---|---|
| 티어드 블록체인 세팅 이관 | 등급별 설정 파일 동기화 |
| 부트스트랩 | 자동 설치 시작 |
| 블록체인 노드 기여 커밋 | 내 작업 변경 내용 저장 |
| 블록체인 싱크 체인 | 변경 기록 확인 |
| 오너 피드 | 관리자 알림판 |
| croworker | 함께 작업하는 사람 |
| 싱크 | 동기화 (설명 없이는 "복사해 맞추기") |
| 티어1 ~ 티어4 | 1단계 ~ 4단계 (처음엔 "접근 등급" 설명 필요) |
| 게스트 | 방문자 (로그인 없이) |
| 아이디 | 아이디 (OK — 모두 아는 말) |
12. cowork 적용 가이드
12-1. portal.html / index.html 교체할 CSS 변수 블록
기존의 보라 계열 변수를 초콜릿+골드 계열로 모두 교체한다.
css/* ── 기존 (삭제) ── */
:root {
--bg:#0a0a0f; --sf:#12121a; --sf2:#181824; --bd:#1e1e2e;
--ac:#7c5bf5; --ac2:#5b8bf5;
--tx:#e0e0e8; --dim:#666680;
--gn:#3ddc84; --og:#f5a623; --rd:#f55b5b;
}
/* ── 새 크라우니 표준 (교체) ── */
:root {
/* 팔레트 */
--choco:#3B2A1D; --choco-2:#2C1E13; --choco-deep:#1A130D;
--cream-50:#FBF7EE; --cream-100:#F4EFE2; --cream-200:#ECE5D5;
--gold:#B58A4A; --gold-dim:#8F6E3A; --gold-soft:#D8C29A;
--gold-wash:#EFE4CD; --gold-bright:#C9A35A;
--ink:#2B2018; --ink-soft:#5C4C3C; --muted:#978872;
/* 이름표 (라이트 모드 기본) */
--bg:var(--cream-100); --bg-soft:var(--cream-200);
--surface:var(--cream-50); --surface-2:var(--cream-200);
--text:var(--ink); --text-soft:var(--ink-soft); --text-muted:var(--muted);
--accent:var(--gold); --accent-dim:var(--gold-dim);
--accent-soft:rgba(181,138,74,.12); --accent-wash:var(--gold-wash);
--on-accent:#FBF7EE;
--border:rgba(43,32,24,.12); --border-strong:rgba(43,32,24,.20);
/* 상태 */
--success:#4E8C5A; --warning:#C08842; --danger:#B5524A;
/* 그림자 */
--shadow-sm:0 1px 2px rgba(59,42,29,.06),0 2px 6px rgba(59,42,29,.05);
--shadow-md:0 6px 16px -6px rgba(59,42,29,.14),0 2px 6px rgba(59,42,29,.06);
--shadow-hi:inset 0 1px 0 rgba(255,255,255,.55);
--glow:0 0 28px rgba(181,138,74,.22),0 0 1px rgba(181,138,74,.4);
/* 반경 */
--r-xs:8px; --r-sm:12px; --r-md:18px; --r-lg:28px; --r-pill:999px;
/* 모션 */
--ease:cubic-bezier(.22,.61,.36,1); --dur-1:160ms; --dur-2:240ms;
/* 간격 */
--s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px;
/* 폰트 */
--sans:'Pretendard',system-ui,-apple-system,'Apple SD Gothic Neo','Segoe UI',sans-serif;
--serif:'Noto Serif KR',serif;
--mono:'JetBrains Mono',ui-monospace,monospace;
}
/* 다크 모드 */
@media (prefers-color-scheme:dark) { :root {
--bg:#1A130D; --bg-soft:#221810; --surface:#241A12; --surface-2:#2E2117;
--text:#F4EFE2; --text-soft:#D8C9B6; --text-muted:#A89A82;
--accent:#D8C29A; --accent-dim:#C9A35A;
--accent-soft:rgba(216,194,154,.14); --accent-wash:#2E2117;
--border:rgba(244,239,226,.12); --border-strong:rgba(244,239,226,.22);
--on-accent:#1A130D;
--shadow-sm:0 1px 2px rgba(0,0,0,.3),0 2px 8px rgba(0,0,0,.28);
--shadow-md:0 8px 22px -8px rgba(0,0,0,.5),0 2px 8px rgba(0,0,0,.3);
--shadow-hi:inset 0 1px 0 rgba(255,255,255,.06);
}}
[data-theme="dark"] {
--bg:#1A130D; --bg-soft:#221810; --surface:#241A12; --surface-2:#2E2117;
--text:#F4EFE2; --text-soft:#D8C9B6; --text-muted:#A89A82;
--accent:#D8C29A; --accent-dim:#C9A35A;
--accent-soft:rgba(216,194,154,.14); --accent-wash:#2E2117;
--border:rgba(244,239,226,.12); --border-strong:rgba(244,239,226,.22);
--on-accent:#1A130D;
}
12-2. head에 추가할 폰트 링크
html<!-- 기존 head에 추가 -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css">
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@400;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet">
<meta name="theme-color" content="#F4EFE2" media="(prefers-color-scheme:light)">
<meta name="theme-color" content="#1A130D" media="(prefers-color-scheme:dark)">
12-3. portal.html 수정 우선순위 (감사 결과 반영)
[높음] http:// 하드코딩 수정 (portal.html:263-266)
javascript// 기존 (문제) — http 하드코딩
function renderBoot(host) {
const mac = `curl -s http://${host}/setup | bash -s -- ${tok} 4`;
const win = `irm http://${host}/setup.ps1 | iex ; cowork-setup ${tok} 4`;
...
}
// 수정 — protocol 자동 감지
function renderBoot(host) {
const proto = (host.includes(':') || location.protocol === 'http:') ? 'http' : 'https';
const baseUrl = `${proto}://${host.replace(/:\d+$/, '')}`;
const mac = `curl -s ${baseUrl}/setup | bash -s -- ${tok} 4`;
const win = `irm ${baseUrl}/setup.ps1 | iex ; cowork-setup ${tok} 4`;
...
}
[높음] index.html 하드코딩 URL 수정 (index.html:199,202)
html<!-- 기존 (문제) -->
<code>curl -o cowork-cli.sh cowork.crowny.org:9720/cli</code>
<!-- 수정 -->
<code>curl -o cowork-cli.sh https://cowork.crowny.org/cli</code>
[높음] Clipboard 폴백 추가 (portal.html:370)
javascript// 기존 (문제) — 오류 처리 없음
function copyEl(id) {
const t = h(id).textContent.replace('복사','').trim();
navigator.clipboard.writeText(t);
}
// 수정 — HTTP 폴백 + 피드백 포함
function copyEl(id) {
const t = h(id).textContent.replace('복사','').trim();
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(t)
.then(() => showCopyFeedback(id))
.catch(() => fallbackCopy(t, id));
} else {
fallbackCopy(t, id);
}
}
function fallbackCopy(text, id) {
const el = document.createElement('textarea');
el.value = text;
el.style.cssText = 'position:fixed;opacity:0;top:-100px;left:-100px';
document.body.appendChild(el);
el.focus(); el.select();
try { document.execCommand('copy'); showCopyFeedback(id); }
catch(e) { alert('복사 단축키: Ctrl+C (Windows) / Cmd+C (Mac)'); }
document.body.removeChild(el);
}
function showCopyFeedback(id) {
const btn = h(id).querySelector('.cp');
if (!btn) return;
const orig = btn.textContent;
btn.textContent = '복사됨!';
btn.style.background = 'var(--success)';
setTimeout(() => { btn.textContent = orig; btn.style.background = ''; }, 1500);
}
[중간] Blob URL 누수 수정 (portal.html:253-255)
javascript// 기존 (문제) — revokeObjectURL 없음
function pull(tier) {
...fetch(...).then(b => b.blob()).then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = '...'; a.click();
// URL 해제 없음 → 누수
});
}
// 수정
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = getFileName(res) || 'bundle.tar.gz';
document.body.appendChild(a); a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 1000);
})
// 파일명 파싱 (RFC 5987 지원)
function getFileName(res) {
const cd = res.headers.get('content-disposition') || '';
const utf8 = cd.match(/filename\*=UTF-8''([^;]+)/);
if (utf8) return decodeURIComponent(utf8[1]);
const plain = cd.match(/filename="?([^";]+)"?/);
return plain ? plain[1] : null;
}
12-4. 테마 토글 버튼 추가
portal.html과 index.html 헤더에 다크/라이트 전환 버튼을 추가한다.
html<!-- 헤더 오른쪽에 추가 -->
<button class="theme-toggle" onclick="toggleTheme()" aria-label="다크/라이트 모드 전환">
<svg class="ic-sun" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/>
<line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
<line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
</svg>
<svg class="ic-moon" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<script>
function toggleTheme() {
const root = document.documentElement;
const cur = root.getAttribute('data-theme') ||
(matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light');
const next = cur === 'dark' ? 'light' : 'dark';
root.setAttribute('data-theme', next);
localStorage.setItem('crowny-theme', next);
}
// 저장된 테마 복원
(function(){
const saved = localStorage.getItem('crowny-theme');
if (saved) document.documentElement.setAttribute('data-theme', saved);
})();
</script>
12-5. 히어로 섹션 추가 (index.html)
처음 방문한 사람이 사이트가 무엇인지 바로 알 수 있도록 히어로 섹션을 추가한다.
html<!-- 탭 메뉴 위에 삽입 -->
<section class="hero" id="hero">
<div class="hero-inner">
<span class="badge">
<span class="dot"></span>
크라우니 협업 도구
</span>
<h1 style="font-family:var(--serif); font-size:clamp(26px,5vw,42px); color:var(--text);">
여러 컴퓨터에서<br>같은 도구로 같이 일한다
</h1>
<p style="color:var(--text-soft); font-size:16px; max-width:48ch; line-height:1.75;">
한선씨 컴파일러, 크라우니 스킬, 설정 파일을<br>
로그인 하나로 다른 컴퓨터에 그대로 옮긴다.
</p>
<div style="display:flex; gap:12px; flex-wrap:wrap; justify-content:center;">
<a href="/portal" class="btn btn--primary">로그인해서 시작하기</a>
<a href="/안내" class="btn">자세히 알아보기</a>
</div>
</div>
</section>
12-6. 전문 용어 → 쉬운 말로 교체
[portal.html footer] "티어드 블록체인 세팅 이관"
→ "등급별 설정 파일 동기화"
[portal.html 카드 제목] "부트스트랩"
→ "새 컴퓨터에 자동 설치하기"
[portal.html] "블록체인 노드 & 기여 커밋"
→ "작업 변경 내용 저장하기"
[portal.html] "블록체인 싱크 체인"
→ "변경 기록 확인"
[portal.html] "오너 피드"
→ "관리자 알림판"
[index.html] "croworker"
→ "함께 일하는 사람"
13. 체크리스트 (적용 완료 확인용)
새 페이지를 만들거나 기존 페이지를 고칠 때 아래 항목을 확인한다.
색/폰트
-
--ac:#7c5bf5같은 보라 계열 변수 없음 - Pretendard CDN
<link>있음 - Noto Serif KR CDN
<link>있음 - JetBrains Mono CDN
<link>있음 - 제목에
var(--serif)사용
기능
- HTTPS에서 clipboard API 작동 확인
- HTTP(로컬)에서 clipboard 폴백 확인
- Blob URL
revokeObjectURL호출 있음 - 한글 파일명 다운로드 RFC 5987 인코딩
접근성
- 모든 버튼/링크 최소 44×44px
- 아이콘 버튼에
aria-label있음 - 포커스 링 보임 (
:focus-visible) - 텍스트 색 대비 4.5:1 이상
반응형
- 모바일(640px 이하) 화면 확인
- 탭이 모바일에서 하단 고정 확인
- 가로 스크롤 없음
콘텐츠
- 전문 용어에 쉬운 설명 달림
- 첫 화면에서 사이트 목적 파악 가능
- 다크/라이트 모드 토글 버튼 있음
14. 관련 파일
| 파일 | 위치 | 역할 |
|---|---|---|
| KPS 정본 CSS | /Users/ef/crowny-services/public/kps/index.html (L30~220) | 디자인 토큰 원본 |
| 기존 디자인 라이브러리 | /Users/ef/crowny-design/크라우니디자인.한선 | 한선씨 버전 |
| 기존 CSS | /Users/ef/crowny-design/크라우니표준.css | 정적 CSS |
| cowork portal | /Users/ef/crowny-cowork/public/portal.html | 수정 대상 |
| cowork index | /Users/ef/crowny-cowork/public/index.html | 수정 대상 |
| UX 감사 | /Users/ef/CrownyDoc/projects/2026-06-27-cowork-감사-UX.md | 문제 목록 |
| 백엔드 감사 | /Users/ef/CrownyDoc/projects/2026-06-27-cowork-감사-백엔드.md | 문제 목록 |
| 한선씨 감사 | /Users/ef/CrownyDoc/projects/2026-06-27-cowork-감사-한선씨셀코어.md | 문제 목록 |
이 문서는 크라우니 디자인 표준 v1.0 정본이다. 변경 시 이 파일을 먼저 수정하고 관련 서비스에 반영한다.