- Update all appkit slash commands to use local docs/ directory - Add .gitignore to exclude docs/ folder - Update example files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
9.7 KiB
9.7 KiB
description
| description |
|---|
| 목업 생성 - HTML 기반 라이브 프로토타입 UI 생성 |
User Input
$ARGUMENTS
User input should be a screen name (e.g., "home", "chat", "booking") or "all" for all screens.
Overview
논리적 사고 5단계 워크플로우:
1. /appkit.new → 아이디어 스케치 (어떤 서비스인지?)
2. /appkit.mvp → MVP 범위 정하기 + 랜딩페이지
3. /appkit.ui → 화면설계서 (라우터, 화면, 컴포넌트)
4. /appkit.policy → 정책설계서 (비즈니스 규칙)
5. /appkit.visualize → 목업 생성 (HTML 프로토타입) ← YOU ARE HERE
출력 디렉토리: docs/mockup
Purpose
router.md와 policy 문서를 기반으로 실제 동작하는 HTML 목업을 생성합니다. 브라우저에서 바로 확인할 수 있는 라이브 프로토타입입니다.
Pre-requisite
/appkit.ui로 화면설계가 되어 있어야 함router.md파일이 존재해야 함
Usage
/appkit.visualize home # 홈 화면 목업
/appkit.visualize chat # 채팅 화면 목업
/appkit.visualize all # 전체 화면 목업
/appkit.visualize "예약 상세" # 특정 화면 목업
Execution Flow
1. 기존 기획 읽기
Read files from docs/:
router.md- 라우터 및 UI 목록main.md- 메인 구조policies/*.md- 정책 문서
2. 목업 생성 대화
Step 1: 화면 확인
## 목업 생성
router.md에서 확인된 화면 목록:
| 화면 | 경로 | 생성 여부 |
|-----|------|----------|
| home | `/` | ⬜ |
| chat-list | `/chat` | ⬜ |
| chat-room | `/chat/:id` | ⬜ |
| shop | `/shop` | ⬜ |
어떤 화면을 생성할까요?
Step 2: 사용자 선택
- 특정 화면명 → 해당 화면만 생성
- "all", "전체" → 모든 화면 생성
3. HTML 목업 파일 생성
3-1. 기본 구조
File: docs/mockup/[screen].html
템플릿: .specify/templates/mockup-base.html 참조
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>[화면명] - [서비스명]</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="app">
<!-- Header -->
<header class="header" id="[screen]-header">
<!-- 헤더 컴포넌트 -->
</header>
<!-- Main Content -->
<main class="content" id="[screen]-content">
<!-- 메인 콘텐츠 -->
</main>
<!-- Tab Bar -->
<nav class="tab-bar">
<!-- 탭바 -->
</nav>
</div>
<!-- Overlay -->
<div class="overlay" id="overlay" onclick="closeAll()"></div>
<!-- Modals/Popups -->
<!-- 모달/팝업 -->
<script src="scripts.js"></script>
<script>
// 화면별 스크립트
</script>
</body>
</html>
3-2. 공통 스타일시트 (styles.css)
/* Reset & Base */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Colors */
--bg-primary: #0D0D1A;
--bg-secondary: #1A1A2E;
--bg-card: #252538;
--text-primary: #FFFFFF;
--text-secondary: #9999AA;
--accent-primary: #6C5CE7;
--accent-secondary: #45B7D1;
--accent-pink: #FF6B9D;
--accent-gold: #FFD93D;
--danger: #FF6B6B;
--success: #4ECB71;
/* Sizing */
--header-height: 56px;
--tab-height: 64px;
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
}
body {
font-family: 'Noto Sans KR', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
overflow-x: hidden;
}
/* App Container */
.app {
max-width: 430px;
margin: 0 auto;
min-height: 100vh;
position: relative;
background: var(--bg-primary);
}
/* Header */
.header {
position: fixed;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 430px;
background: var(--bg-primary);
padding: 12px 16px;
z-index: 100;
}
/* Tab Bar */
.tab-bar {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 430px;
background: var(--bg-primary);
display: flex;
justify-content: space-around;
padding: 8px 0;
padding-bottom: calc(8px + var(--safe-area-bottom));
border-top: 1px solid #2D2D3A;
z-index: 100;
}
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 16px;
color: var(--text-secondary);
text-decoration: none;
position: relative;
}
.tab-item.active {
color: var(--text-primary);
}
.tab-icon {
font-size: 20px;
margin-bottom: 4px;
}
.tab-label {
font-size: 10px;
}
.tab-badge {
position: absolute;
top: 0;
right: 8px;
background: var(--danger);
color: white;
font-size: 10px;
padding: 2px 6px;
border-radius: 10px;
min-width: 16px;
text-align: center;
}
/* Content Area */
.content {
padding-top: var(--header-height);
padding-bottom: calc(var(--tab-height) + var(--safe-area-bottom));
min-height: 100vh;
}
/* Cards */
.card {
background: var(--bg-card);
border-radius: 16px;
padding: 16px;
margin: 12px 16px;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
border: none;
}
.btn-primary {
background: var(--accent-primary);
color: white;
}
.btn-secondary {
background: var(--bg-card);
color: var(--text-primary);
}
/* Modal */
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.9);
background: var(--bg-secondary);
border-radius: 20px;
padding: 24px;
text-align: center;
z-index: 1001;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
max-width: 320px;
width: 90%;
}
.modal.active {
opacity: 1;
visibility: visible;
transform: translate(-50%, -50%) scale(1);
}
/* Overlay */
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.7);
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
}
.overlay.active {
opacity: 1;
visibility: visible;
}
/* Toast */
.toast {
position: fixed;
bottom: calc(var(--tab-height) + 20px + var(--safe-area-bottom));
left: 50%;
transform: translateX(-50%) translateY(100px);
background: var(--bg-card);
padding: 12px 24px;
border-radius: 12px;
opacity: 0;
transition: all 0.3s;
z-index: 1002;
}
.toast.active {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
3-3. 공통 스크립트 (scripts.js)
// Modal Functions
function showModal(id) {
document.getElementById('overlay').classList.add('active');
document.getElementById(id).classList.add('active');
}
function closeAll() {
document.getElementById('overlay').classList.remove('active');
document.querySelectorAll('.modal, .bottom-sheet').forEach(el => {
el.classList.remove('active');
});
}
// Toast Function
function showToast(message, duration = 2000) {
const toast = document.getElementById('toast');
if (toast) {
toast.textContent = message;
toast.classList.add('active');
setTimeout(() => {
toast.classList.remove('active');
}, duration);
}
}
// Tab Badge Update
function updateBadge(tabId, count) {
const badge = document.querySelector(`#${tabId} .tab-badge`);
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? 'block' : 'none';
}
}
4. 화면별 목업 생성
각 화면에 대해 router.md의 컴포넌트 정보를 기반으로 HTML 생성:
- 헤더 영역:
[screen]-header컴포넌트들 - 메인 콘텐츠:
[screen]-content내 컴포넌트들 - 탭바: 전역 탭바 (현재 탭 active 표시)
- 팝업/모달:
popup-*,bs-*컴포넌트들
5. 완료 리포트
✅ 목업 생성 완료!
📁 생성된 파일:
- mockup/home.html
- mockup/chat.html
- mockup/styles.css
- mockup/scripts.js
🌐 브라우저에서 확인:
file:///Users/.../ui/works/mockup/home.html
📊 생성 요약:
- HTML 파일: N개
- 컴포넌트: N개
- 모달/팝업: N개
📝 참고:
- 각 화면은 상호 링크되어 있습니다
- 팝업은 JavaScript로 동작합니다
- 정책 문서와 연결된 UI 요소에는 정책 ID 주석이 포함되어 있습니다
📍 현재 위치: Step 5/5 완료!
목업 생성 원칙
Do's
- ✅ router.md 기반: 모든 컴포넌트 ID와 구조 준수
- ✅ 정책 연결: UI에 적용되는 정책 ID 주석 추가
- ✅ 상호 링크: 화면 간 네비게이션 동작
- ✅ 상태 표시: Empty, Loading, Error 상태 시뮬레이션
- ✅ 반응형: 모바일 퍼스트 (max-width: 430px)
Don'ts
- ❌ 과도한 디자인: 와이어프레임 수준 유지
- ❌ 하드코딩: 데이터는 의미있는 샘플로
- ❌ 누락: router.md에 정의된 컴포넌트 누락 금지
정책 연결 주석 예시
<!-- [MN-001] 채팅권 잔액 표시 -->
<div class="token-badge">
<span>💬</span>
<span>127</span>
</div>
<!-- [GM-002] 예약 가능 시간: 24시간 전까지만 -->
<button class="btn-primary" disabled>
예약 마감
</button>
Example
$ /appkit.visualize home
🎨 목업 생성: home
router.md에서 컴포넌트 확인:
- home-header (Header)
- home-stats (Card)
- home-quick-status (Display)
- tab-bar (TabBar)
- popup-new-message (Modal)
- popup-life-stage (Modal)
정책 적용:
- [MN-001] 채팅권 잔액 표시 → header
- [GM-001] 모드 전환 → header
✅ home.html 생성 완료
✅ styles.css 업데이트
✅ scripts.js 업데이트
🌐 file:///Users/.../ui/works/mockup/home.html