개발 미리보기
UI 컴포넌트 프리뷰
일반 화면에서 사용하는 Cotton 컴포넌트를 실제 테마와 간격으로 확인하는 개발용 화면입니다. 각 샘플의 변형과 의미론적 HTML을 함께 확인할 수 있습니다.
날짜와 시간
일반·사용 기한·다른 연도·감사 기록의 표시 규칙
일반 · 10월 28일 오후 3:05
사용 기한 · 10월 28일 오후 3:05까지
다른 연도 · 2025년 10월 21일 오후 2:30
감사 기록 · 2026년 10월 28일 오후 3:05:01
소비자 디자인 시스템
탑툰챗 기준의 중립 다크 표면과 주요 강조색 #FFC349를 공유하며 탐색·몰입·정보 처리의 규칙을 구분합니다.
글자 역할
크기·줄높이·굵기를 역할로 함께 적용합니다. 상세 이름은 화면 크기에 맞춰 변합니다.
text-consumer-hero · 32/40 → 48/52새벽에 다시 만난 이야기
text-consumer-page · 24/32 → 28/36내가 이어가는 이야기
text-consumer-section · 18/28오늘의 추천
text-consumer-card · 16/24비가 멎을 때까지
text-consumer-body · 16/24장면을 고르면 새로운 이야기를 시작할 수 있어요.
text-consumer-description · 14/22긴 소개와 도움말은 충분한 크기로 읽을 수 있게 표시합니다. 내용이 길어지면 줄바꿈하며 모바일에서도 의미를 생략하지 않습니다.
text-consumer-label · 14/20메시지 입력
text-consumer-meta · 12/18text-consumer-tag · 12/16 · 500최근 검색어 · #로맨스
탑툰 기준 화면별 역할 · 표시용 글꼴은 font-consumer-display(S-Core Dream)
font-consumer-display text-consumer-rail-title · 18/18 → 640px 22/22인기작
font-consumer-display text-consumer-banner-title · 20/25font-consumer-display text-consumer-card · 16/24비가 멎을 때까지
text-consumer-card-meta · 11/15.125 · 600font-consumer-display text-consumer-scenario · 14/21 · 600처음 만난 날
font-consumer-display text-consumer-cta · 16/24대화 시작하기
text-consumer-subpage · 18/28공지사항
text-consumer-tab · 14/20 · 500실시간 · 주간 · 월간
text-consumer-sidebar · 14/20 · 700text-consumer-nav-label · 10/15탐색
text-consumer-widget · 13/19.5 · 700text-consumer-caption · 11/16.5 · 500#일상 · 1/5
text-consumer-badge · 10/10 · 900NEW
글자 역할 사용 예
<h1 class="text-consumer-hero text-consumer-heading">이야기 이름</h1>
<p class="text-consumer-description text-consumer-muted">소개와 도움말</p>
색상 역할과 대비
불투명 표면의 글자·배경 조합입니다. 이미지 위 글자는 어두운 배경막과 함께 별도로 확인합니다.
화면 배경 · 기본 본문
패널 · 제목
패널 · 보조 설명
패널 · 짧은 보조 정보
입력 표면 · 입력값
주요 행동 · #FFC349 위 글자
선택한 칩 · 보조 버튼
호버 · 선택한 사이드바 행
획득 금액 · +100
사용 금액 · -100
선택 표면 · 강조 글자
안내 · 정보
완료 · 성공
주의 · 제한 안내
오류 · 복구 안내
화면 배경 · 부제·설명
화면 배경 · 개수·시각
카드 · 보조 설명
카드 · 날짜·분류
카드 · 링크·모드
카드 · 답변 대기
카드 · 답변 완료·수집 완료
화면 배경 · 입력 오류
색상 역할 사용 예
<div class="bg-consumer-surface text-consumer-text border border-consumer-border">정보 패널</div>
<a class="text-consumer-link">강조 링크</a>
<!-- 레퍼런스 bg-secondary → bg-consumer-secondary, text-muted-foreground → text-consumer-muted -->
<button class="bg-consumer-primary text-consumer-on-primary">대화 시작하기</button>
consumer-hot(SALE·실시간·할인율)과 consumer-destructive는 #E63740 계열이라 작은 본문 글자의 4.5:1 대비를 만족하지 않습니다. 장식·큰 글자·아이콘 표시에만 쓰고, 오류 문구는 consumer-error를 사용합니다.
간격·크기·표면·움직임
모바일의 독립 제어는 최소 44px로 표시하고 고정 입력은 키보드와 safe area를 고려합니다.
간격 척도 · 4 / 8 / 12 / 16 / 24 / 32px
라벨 8px · 묶음 16px · 패널 16→24px · 섹션 32px
제어 44px · 큰 CTA 48px · 기존 반경 12 / 14 / 16 / 22px
버튼의 색상·그림자·2px 이동은 호버 진입과 이탈에서 함께 150ms로 전환합니다. 버튼 크기는 전환하지 않습니다. 팝오버는 진입 180ms / 퇴장 120ms를 사용합니다. 기기에서 모션 감소를 선택하면 이동과 반복 효과를 멈춥니다. 상태의 성공 여부는 서버 응답으로 확인합니다.
탑툰 기준 반경 · 기본 10px 파생
사이드바 행·시나리오 선택 10px · 카드·CTA·재화 pill 14px · 탐색 이미지·AI 말풍선 18px · 채팅 입력 24px. 레퍼런스의 rounded-xl은 Tailwind 기본값이 아닌 rounded-consumer-xl(14px)로 옮깁니다.
간격과 표면 사용 예
<section class="rounded-consumer-panel bg-consumer-surface p-consumer-panel space-y-consumer-section">…</section>
<a class="rounded-consumer-xl bg-consumer-primary text-consumer-on-primary">대화 시작하기</a>
아이콘과 로고
아이콘은 단일 스프라이트를 c-icon으로, 로고는 교체 가능한 브랜드 자산을 c-brand-logo로만 참조합니다.
로고는 자사 로고 DalVo Story, 내비게이션 아이콘(홈·탐색·채팅·MY·출석)은 Lucide 계열 자사 도형이며 apps/core/static/core/brand/의 파일만 바꿔 교체합니다.
아이콘과 로고 사용 예
<c-icon name="chevron-right" size="16" />
<c-icon name="bell" label="알림" />
<c-brand-logo class="h-7" />
공통 입력
실제 input의 폼·라벨·키보드 동작을 유지하며 검색 진입점과 표면 규격을 공유합니다.
아이콘·보조 문구는 슬롯으로 지정합니다.
입력한 값을 확인해 주세요.
공통 입력 사용 예
<label for="story-query">검색어</label>
<c-input id="story-query" name="q" x-model="query" required aria-describedby="query-help">
<c-slot name="prefix">장식 아이콘</c-slot>
</c-input>
<p id="query-help">캐릭터 이름이나 이야기로 검색하세요.</p>
<c-input id="invalid-query" invalid aria-describedby="query-error" />
복수 선택
네이티브 체크박스로 키보드 선택과 실제 폼 값을 유지합니다.
복수 선택 사용 예
<fieldset>
<legend>태그 조합</legend>
<c-checkbox name="tag" value="romance">#로맨스</c-checkbox>
<c-checkbox name="tag" value="daily" checked>#일상</c-checkbox>
</fieldset>
정보 처리 화면의 세로 간격
작은 도움말 8px · 안내와 행동 12px · 독립 카드 16px · 구역 전환 24px
사용 가능한 양피지 800개
보유 총량과 만료 예정은 잔액 상세에서 확인해 주세요.
안내와 별도 행동을 같은 간격 기준으로 구분해요.
현재 상태를 확인해 주세요
세로 간격 조합 예시
<div class="grid gap-4"><p class="m-0">안내</p><div class="grid gap-3"><c-notice>현재 상태</c-notice><c-button class="justify-self-start">다시 확인</c-button></div></div>
수집 등급
수집 등급은 어드민과 같은 이름으로 표시하고 카드에는 대화 모드를 표시하지 않습니다.
예시 코드
<c-collection-grade :grade="item.grade_key" :label="item.grade_label" />
수집 미디어의 소유·잠금·실패
보호 URL 주입도 실제 소비자 미디어 표현을 사용합니다. 실패 시 원본으로 우회하지 않습니다.
테스트 방의 보호 URL 사용법
{% include 'components/_collection_media.html' with item=row.item locked=row.locked locked_preview_url=row.locked_preview_url media_url=row.media_url only %}
발견 카드의 해금 상세는 공통 시트 안에서 최신 잔액을 확인하고 바로 지정 해금합니다. 열기·취소·잔액 부족은 차감하지 않습니다. 실제 요청은 격리 테스트 방에서 확인합니다.
발견 장면 모달 해금 사용 예
<c-discovered-key-unlock :item="appearance.item" :dialog_key="appearance.item_id" :conversation_id="conversation.pk" />
문맥형 버튼과 태그
문맥형 버튼 크기
검색·배너 링크와 태그 필터도 같은 버튼 계약을 사용합니다.
배너 링크 예시
<c-button variant="inline" size="inline">배너 링크<c-slot name="right_icon"><svg class="nav-icon" aria-hidden="true"><use href="#icon-arrow"></use></svg></c-slot></c-button>
태그 버튼 예시
<c-button variant="ghost" size="tag">태그</c-button>
크기 단계
크기와 형태
xs · sm · md · lg · icon
xssmmdlgiconcontrol버튼 크기 예시
<c-button size="xs">Extra small</c-button>
<c-button size="sm">Small</c-button>
<c-button>Medium</c-button>
<c-button size="lg">Large</c-button>
<c-button size="icon" aria-label="더 보기">⋯</c-button>
<c-button type="submit" size="control">검색</c-button>
전체 너비
폼 하단이나 모바일 주요 액션에 사용합니다.
전체 너비 버튼 예시
<c-button variant="primary" full_width>전체 너비 버튼</c-button>
c-chip-tabs · c-chip
필터 칩
선택은 주요 강조색이 아닌 중립 반전으로 표시합니다
링크형 · size="sm"(공지 분류)
링크형 칩 예시
<c-chip-tabs label="공지 분류">
<c-chip href="?category=all" selected>전체</c-chip>
<c-chip href="?category=notice">공지</c-chip>
</c-chip-tabs>
버튼형 · size="md"(즐겨찾기 종류)
버튼형 칩 예시
<c-chip size="md" count="2" x-bind:data-selected="kind === 'character'" x-bind:aria-pressed="kind === 'character'" @click="kind = 'character'">캐릭터</c-chip>
c-dropdown
드롭다운
클릭·바깥 영역·Escape로 닫히며, 아래 공간이 부족하면 위쪽으로 펼쳐집니다.
작업 메뉴
메뉴 항목을 클릭하면 드롭다운이 닫힙니다.
기본 메뉴 예시
<c-dropdown label="작업 메뉴">
<c-button href="/developer-tools/components/" variant="ghost" size="sm" full_width role="menuitem">컴포넌트 보기</c-button>
<c-button type="button" variant="ghost" size="sm" full_width role="menuitem">임시 작업</c-button>
</c-dropdown>
오른쪽 정렬
DaisyUI의 end 정렬에 대응하는 변형입니다.
오른쪽 정렬 메뉴 예시
<c-dropdown label="계정 메뉴" align="end" variant="ghost" size="sm">
<c-button href="/account/" variant="ghost" size="sm" full_width role="menuitem">계정 설정</c-button>
<c-button type="button" variant="ghost" size="sm" full_width role="menuitem">로그아웃</c-button>
</c-dropdown>
아이콘 트리거
아이콘 버튼과 말풍선 꼬리는 필요한 드롭다운에서만 사용합니다.
아이콘 드롭다운 예시
<c-dropdown label="테마 선택" trigger_type="icon" align="end">
<c-slot name="trigger_icon"><span aria-hidden="true">🌙</span></c-slot>
<button type="button" role="menuitemradio" aria-checked="true">🌙 다크 ✓</button>
<button type="button" role="menuitemradio" aria-checked="false">☀️ 라이트</button>
</c-dropdown>
폼 선택 입력
선택값 · 필수 입력 오류 · 비활성 항목 · 입력 잠금 · 초기화
오른쪽 경계의 메뉴도 패널 안에 표시합니다.
방문 기록에서 복원된 폼 값도 선택 문구에 반영하며 복원만으로 제출하지 않습니다.
테스트 사용자를 선택해 주세요.
폼 선택 입력 예시
<label for="filter">목록 필터</label>
<c-dropdown selection name="filter" id="filter" label="목록 필터" required x-model="filter">
<option value="" selected>선택해 주세요</option>
<option value="all">전체</option>
<option value="hidden" disabled>숨김 · 선택 불가</option>
</c-dropdown>
설명 줄 예시
<c-dropdown selection id="mode" name="mode" label="대화 모드">
<option value="half" data-description="대사와 장면 묘사를 균형 있게">반달 · 120양피지</option>
</c-dropdown>
조건부 선택지 예시
<option value="extra" hidden disabled x-bind:hidden="!available" x-bind:disabled="!available">추가 선택지</option>
서버 오류 연결 예시
<c-dropdown selection id="user" name="user" label="테스트 사용자" required aria-invalid="true" aria-describedby="user-errors">
<option value="" selected>선택해 주세요</option>
</c-dropdown>
<p id="user-errors" role="alert">테스트 사용자를 선택해 주세요.</p>
가격과 설명이 있는 선택지
짧은 선택 버튼 · 가격 정렬 · 선택 표시 · 유료 전용 · 노트 추가 비용
가격·잔액은 표시 예시입니다. 제목과 가격을 먼저 읽고, 설명과 잔여 횟수를 아래에서 확인합니다. 긴 정보는 줄바꿈하고 좁은 화면에서는 메뉴 폭을 제한합니다.
상세 선택지 사용 예시
<c-dropdown selection name="mode" label="대화 모드" menu_class="w-[22rem]">
<option value="half" data-title="반달" data-trailing="120양피지" data-trigger-label="반달 · 120양피지" data-description="대사와 장면 묘사를 균형 있게 이어가는 대화" data-meta="513턴 가능">반달 · 120양피지 · 513턴 가능</option>
</c-dropdown>
엔딩 카드
획득 카드의 대표 장면, 미발견 카드의 무료 힌트와 힌트 없음 안내를 같은 부품으로 표시합니다.
달빛 아래의 약속
미발견 엔딩
미발견 엔딩
엔딩 카드 사용 예
<c-ending-card :card="ending_card" :is_new="is_new" />
EndingCardPresentation은 서버에서 확인한 소유 상태로 만듭니다. 미획득 값에는 이름·미디어 URL·감상 URL을 넣지 않으며, 힌트가 없으면 대화 진행 안내를 표시합니다.
음소거 영상
재생 조건은 호출자의 Alpine 상태로 전달하고, 영상별 취소·오류 처리는 공통 부품이 관리합니다. 샘플은 외부 영상을 요청하지 않습니다.
정지 · poster 유지
실제 미디어 오류 · 대체 이미지
음소거 영상 사용 예
<c-muted-video src="영상 URL" poster="대체 이미지 URL"
active="index === 0" paused="isVideoPaused()" repeat="true"
revision="selection" visible="index === 0 && !reduced" />
일시적인 재생 취소는 한 번 자동 재시도하고 실제 오류는 영상만 숨깁니다. 같은 프레임에 대체 이미지를 함께 배치합니다.
active·paused·repeat·revision·visible은 호출자 Alpine 표현식입니다.
작품 상세 대표 영상 사용 예
<c-muted-video :src="대표_영상_URL" :poster="대표_이미지_URL" repeat="true"
disablepictureinpicture disableremoteplayback tabindex="-1" class="pointer-events-none absolute inset-0" />
상세 대표 영상은 사용자 조작 없이 음소거 자동 반복 재생하며, 같은 영역의 대표 이미지를 로딩·실패 대체 화면으로 유지합니다.
작품 카드 미리보기
게시 소개 아래에 작품 태그와 캐릭터별 사진·이름·태그를 보여 줍니다. 사진이 없거나 실패하면 이름 첫 글자가 보입니다. 태그 없는 인물은 이름만 표시하며 터치는 상세로 바로 이동합니다.
비가 멎을 때까지
오랜만에 다시 만난 두 사람과 새로운 이야기를 시작해 보세요.
비가 멎을 때까지
작품 태그
등장인물
-
이서희
-
백진아
이미지가 없는 이야기
대표 미디어가 없어도 작품 소개를 확인할 수 있어요.
이미지가 없는 이야기
홈 작품 카드 사용 예
<c-content-preview-card :href="작품_상세_주소" :title="게시_제목" :description="게시_소개"
:preview_url="공개_작품_정보창_GET_주소" :image="공개_대표_이미지" :video_url="게시_영상_720_URL" image_sizes="실제_표시_폭"
:tags="작품_활성_태그" :characters="공개_게시_인물별_사진과_태그" />
preview_url을 주면 첫 열기에만 정보를 받고, 실패 시 다시 시도·작품 상세 이동을 제공합니다. 정적 샘플은 주소 없이 인라인 내용을 씁니다.
tags는 key·label, characters는 character_id·name·tags·portrait(선택 ResponsiveImage)를 가진 표시 값입니다. 비어 있는 절은 생략하고 인물이 여러 장면에 등장해도 한 번만 표시합니다.
영상은 마우스를 올린 카드에서만 무음 반복 재생하고, 주소가 비어 있으면 정지 이미지를 유지합니다. 정보창만 호버·키보드 포커스로 열리며 Escape로 닫습니다.
콘텐츠 카드와 섹션
홈·탐색에서 같은 3:4 카드와 제목 위계를 사용합니다. 긴 이름과 미디어 없음도 같은 크기를 유지합니다.
첫 카드의 할인 리본은 배치 샘플입니다. 실제 할인 대상과 기간은 운영 계약이 연결될 때만 표시합니다.
지금 인기 있는 이야기
운영팀 추천
이번 주 운영팀이 고른 이야기를 한 줄로 소개합니다. 길면 말줄임으로 정리합니다.
레일 제목 예시
<c-section-heading title="지금 인기 있는 이야기" variant="rail" more_href="..." title_id="popular-title" />
<c-section-heading title="운영팀 추천" description="이번 주 운영팀이 고른 이야기" variant="rail" more_href="..." />
캐릭터 소개
컬렉션
상세 섹션 제목 예시
<c-section-heading variant="detail" title="캐릭터 소개" title_id="introduction-title" />
<c-section-heading variant="detail" title="컬렉션" more_href="..." more_label="모두 보기" />
랭킹 카드 · rank
랭킹 카드 예시
<c-content-card href="..." title="새벽의 약속" rank="1">
<c-slot name="media"><img src="..." alt=""></c-slot>
<c-slot name="metadata"><span>#로맨스</span><span>#일상</span></c-slot>
</c-content-card>
텍스트 카드 · compact
썸네일 없이 제목·소개·메타데이터만 둡니다. 지금은 랭킹 목록 4위 이하 행만 씁니다(랭킹 개편 때 rank 변형으로 옮깁니다).
텍스트 카드 예시
<c-content-card layout="compact" href="..." title="달" description="작은 서점에서 시작하는 대화">
<c-slot name="metadata">답변 1,204회</c-slot>
</c-content-card>
상세 · 동시에 유효한 배지 전체
신규 MULTI EP+카드는 NEW → MULTI → EP+ 중 최대 두 개를 표시합니다. 상세는 모두 표시하며 SALE은 별도 위치를 사용합니다.
시나리오 선택 · 새로 게시한 장면
비 오는 저녁 N정확히 7일 경과 · 신규와 추가 배지 만료
MULTI빈 배지 상태
유효한 상태가 없으면 장식이나 빈 칸을 만들지 않습니다. 랭킹의 NEW는 신규 순위 진입을 뜻하는 별도 상태입니다.
상태 배지 · c-badge tone
처리 상태·보조 pill · tone="waiting"·"answered"·"neutral"
문의·내역 카드(secondary 50%) 위에 놓는 작은 상태 글자입니다. 두 테마에서 4.5:1을 넘도록 라이트 amber·positive는 한 단계 진한 값을 씁니다.
홈 히어로 배너 분류 · tone="banner"
이미지 위에 놓이므로 두 테마에서 같은 색(검정 85% 바탕·#F5F5F5 글자)을 씁니다.
상태 배지 예시
<c-badge tone="notice">공지</c-badge>
<c-badge tone="dday">D-3</c-badge>
<c-badge tone="waiting">답변 대기</c-badge>
<c-badge tone="neutral">3개 세션</c-badge>
<c-badge tone="banner">인기 작품</c-badge>
<c-badge>운영자</c-badge>
콘텐츠 카드와 섹션 사용 예
<c-section-heading title="오늘의 추천" description="나에게 맞는 이야기를 골라 보세요." />
<c-content-card href="/characters/story" title="이야기 이름" description="짧은 소개">
<c-slot name="media"><img src="/static/story.jpg" alt=""></c-slot>
<c-slot name="badges"><c-content-badges :badges="content.content_badges" /></c-slot>
</c-content-card>
배지 사용 예
ContentBadgeCatalog.attach(contents, as_of=request_clock)
카드: content.content_badges (NEW → MULTI → EP+ 최대 두 개)
상세: content.badge_state().for_surface(as_of=request_clock, surface="detail")
선택: content.badged_scenarios(as_of=request_clock)의 scenario.badges
<c-content-badges :badges="scenario.badges" />
기본 select option에는 “ · N”을 붙여 JavaScript 없이도 새 장면을 구분합니다.
상세 부품
시작 시나리오 행 · 바(variant="bar") · 단일 장면 정적 · 여러 장면 메뉴
시작 시나리오 행 · 히어로 안(variant="inline") · 정적 · 메뉴
히어로 행동 원형 · 공유 · 링크 복사 · 즐겨찾기 해제/등록
컬렉션 등급 막대·범례(--consumer-grade-*) · 장소 칩
- 기본 9/37
- P 3/24
- E 2/29
- R 1/29
등장 캐릭터 카드(c-cast-card) · 마우스 오버·탭·Enter로 상세 팝오버 · 한 줄 소개만
등장 캐릭터 2
이서희
뻔뻔한 메스카키 일진녀
무인도에 표류한 뒤에도 주도권을 놓지 않으려는 고집쟁이입니다.
성격 게이지
- 친밀도
-
12 - 자신감
-
85
백진아
은근히 순진한 일진녀
성격 게이지 막대 · 낮음 · 중간 · 가득
상세 시작 묶음 사용 예
변형: variant="bar"는 768px 미만 하단 탭 위 고정 바, "inline"은 768px 이상 히어로 안 묶음입니다.
장면이 하나면(scenario_choices.is_selectable 거짓) 정적 행, 여러 개면 공통 드롭다운의 trigger 슬롯 행입니다.
<c-detail-start variant="bar">
<c-detail-start-row variant="bar" :title="selected_scenario.title" :progress="collection_preview.progress" />
<c-dropdown label="시나리오 선택" placement="auto" class="block w-full mb-2" menu_class="w-full">
<c-slot name="trigger"><c-detail-start-row variant="bar" mode="menu" :title="selected_scenario.title" /></c-slot>
<c-scenario-menu-item href="?scenario=..." :title="choice.scenario.title" :selected="choice.selected" :progress="choice.progress" hx-get="?scenario=..." hx-target="closest .content-detail-page" hx-select=".content-detail-page" hx-swap="outerHTML" hx-push-url="true" />
</c-dropdown>
<c-button href="..." size="lg" full_width data-detail-start-cta>대화 시작하기</c-button>
</c-detail-start>
상세 즐겨찾기와 성격 게이지 사용 예
{% include "characters/_content_favorite_action.html" with content=content is_favorited=is_content_favorited source="detail" csrf_token=csrf_token only %}
등장인물 2명 이상(등장 캐릭터 절): <c-section-heading variant="detail" title="등장 캐릭터" count="2" /> + grid grid-cols-1 gap-2 sm:grid-cols-2 안에 <c-cast-card :revision="revision" />
상세 성격 섹션(consumer-secondary 카드, 등장인물 1명의 "성격" 절): <c-public-profile :revision="revision" />
이미 카드 안(작품 등장인물, 경계선으로만 구분): <c-public-profile :revision="revision" nested />
이미지 인트로 재생
원본 비율·표시 시간·탭 이탈·건너뛰기는 영상과 같은 소비자 재생기를 사용합니다. 샘플은 외부 미디어를 요청하지 않습니다.
첫 만남이 시작됩니다.
혼합 인트로 사용 예
intro_scenes_payload의 각 장면에 kind, url과 이미지의 image_duration_ms를 전달합니다.
characters/_intro_video_player.html과 scenarioIntro를 함께 사용하고 visibilitychange, pagehide, pageshow로 정지·재개합니다.
대사·서술·메시지 입력
작게·기본·크게 설정을 실제 대화 컴포넌트에 적용한 예입니다. 설정은 대사와 서술 크기에 반영하고 메시지 입력 크기는 유지합니다.
작게 · 대사 13px / 서술 12px
기본 · 대사 15px / 서술 14px
크게 · 대사 17px / 서술 16px
작품 화자 메시지
단일 화자와 작품 화자 메시지 사용 예
<article class="message-row character-message">
<c-character-message name="하린" :segments="segments" />
</article>
<article class="message-row character-message">
<c-character-message :bubbles="presentation.bubbles" :structured="presentation.structured" />
</article>
도입·일반 답변·구조화 답변 모두 대사마다 프로필과 이름을 표시합니다. 서술과 대사 속 *지문*은 말풍선 밖의 text-consumer-narrative 문단으로 나눕니다.
c-responsive-image
반응형 이미지
업로드 때 만든 폭별 렌디션 중 화면에 맞는 가장 작은 파일을 브라우저가 고릅니다.
렌디션 후보
선택된 후보:
대체 원본(백필 전·애니메이션)
srcset·sizes 없이 기존 원본 하나만 씁니다.
그릴 이미지 없음
img를 출력하지 않아 호출자의 빈 자리 표시가 남습니다.
레일 카드 사용 예
<c-responsive-image :asset="asset" sizes="(min-width: 768px) 200px, 150px" alt="" class="h-full w-full object-cover" />
채팅 부품
추천 답변 · 문장 선택과 바로 보내기
추천이 도착하기 전에는 카드 공간을 접습니다. 아래 버튼으로 결과 표시 전후를 비교할 수 있어요. 모바일은 세로 목록이며, 문장은 초안에 넣고 화살표는 바로 보냅니다. 샘플에서는 전송 없이 선택한 동작만 표시합니다.
추천 카드 사용 예
<c-chat-reply-card>
<c-plain-button class="chat-reply-copy" data-reply-fill>추천 문장</c-plain-button>
<c-plain-button class="chat-reply-send" data-reply-send aria-label="추천 답변 바로 보내기"><span><c-icon name="arrow" size="16" class="-rotate-90" /></span></c-plain-button>
</c-chat-reply-card>
메시지 보내기 · 접수 대기
누른 직후 내 메시지가 대화에 나타나고 입력창은 다음 문장을 쓸 수 있게 비워집니다. 확정 전까지 보내는 중 상태를 유지하며 실패한 원문은 다시 보낼 수 있습니다.
입력 안내 · 모바일
768px 미만에서는 일반 사용법을 숨기고 전송할 수 없는 이유만 남깁니다.
Shift+Enter로 줄바꿈할 수 있어요. 완료된 답변이 없어 다음 메시지를 보낼 수 없습니다. 작성 중인 입력은 보존됩니다.
반응형 입력 안내 사용 예
일반 안내: class="composer-hint hidden md:block"
전송 차단 사유: class="composer-hint" data-composer-blocked-hint
차단 안내의 키보드 설명: class="hidden md:inline"
답변 상태 · 요약과 상세
최신 답변만 펼친 채, 과거 답변은 접힌 채 보입니다. 접힌 줄은 이번 변화 칩을 먼저 보이고 변화가 없으면 값을 요약합니다. 펼치면 공통 값은 첫 행, 인물은 각자 한 행에 값 칩을 두고, 문자열처럼 스스로 설명하는 값은 라벨을 빼며 범위 5칸 이하 정수는 점 칸, 숫자를 숨긴 단계 값은 단계 이름 태그와 사각 단계 막대로 그립니다. 순서대로 최신·과거, 제목 있는 공통 블록과 2인 인물 블록(긴 이름), 단계와 숫자 공개, 단계만 공개(단계 변화) 예시입니다.
현재 상태 갱신 중 · 이전 확정값
현재 상태 호감 +3신뢰 -2
이 답변 당시 상태 호감 +3신뢰 -2
현재 상태 QA 해솔 포만 +1
현재 상태 관심 +15
현재 상태 관심 무관심 → 호기심
생성 중에는 이전 확정값을 유지하고 본문 끝까지만 따라갑니다. 새 글줄은 180ms 동안 펼치고, 추천을 기다릴 때는 빈 공간을 두지 않고, 실제 추천이 도착하면 마지막 본문 표시 뒤 패널을 엽니다. 긴 문장은 카드 안에서 스크롤해 읽습니다. 모션 감소에서는 즉시 표시합니다.
추천 대기 중에는 후보 공간을 접고 본문을 계속 읽습니다.
답변 상태 사용 예
{% include "chat/_answer_game_state.html" with presentation=answer.game_state %}답변 도구 · 아이콘
<c-button variant="ghost" size="icon" aria-label="다시 생성" title="다시 생성"><c-icon name="refresh-cw" size="18" /></c-button>
입력 위 행 · 모드 pill · 칩(추천 켜짐) · 설정
상황 입력 · 안내 중에도 입력 유지
상황을 누른 직후 별표 사이에 입력할 수 있어요. 안내의 확인·Esc·바깥 클릭과 다시 보지 않기도 실제 도구로 확인합니다. 내용은 전송되지 않아요.
입력 바(50px) · "+" 36px · 전송 48px
말풍선 · 서술 · 장면 도구 · UI 숨김
모바일 장면 · 화면 채움과 원본 보기
좁은 채팅은 장면으로 화면을 채웁니다. 전체 구도가 필요하면 원본 보기로 전환하며, 영상 재생과 대화 위치는 유지합니다. 아래 샘플은 1024px 미만에서 표시 차이를 확인할 수 있습니다.
장면 맞춤 사용 예
상위 Alpine 상태: sceneOriginal: false
장면을 감싼 요소: x-bind:data-scene-original="sceneOriginal || null"
공통 도구: <c-chat-scene-fit />
좁은 화면의 .chat-scene-pane은 --chat-scene-fit 변수를 도입과 일반 미디어에 공유합니다.
독립 감상·PC·영상 전체 화면에서는 원본 비율을 유지합니다.
도입 뒤 첫 대사 · 글자별 이어쓰기
기존 말풍선·서술을 도입과 같은 속도로 한 글자씩 이어서 공개합니다. 문장 사이에 별도 대기를 두지 않습니다. 영상 반복은 원고 DOM을 바꾸지 않습니다. 위에서 읽는 동안 자동 스크롤하지 않으며 모션 감소 설정에서는 지연·효과 없이 전체를 표시합니다.
첫 대사 순차 공개 사용 예
<c-opening-messages>서버가 렌더링한 첫 대사 .message-row 목록</c-opening-messages>
상위 도입의 complete를 읽으며 입력 잠금·영상 재생은 변경하지 않습니다.
채팅 작성란 위 행·입력 바 사용 예
{% include "chat/_composer_toolbar.html" %} ← 모드 pill(#chat-mode-sheet)·커맨드·상황·추천·설정(#chat-settings-sheet)
<div class="composer-row">
<div class="composer-field">{% include "chat/_persona_trigger.html" %}{% include "chat/_message_input.html" … %}</div>
{% include "components/_send_button.html" … %}
</div>
채팅 시트 사용 예
<c-sheet-dialog id="chat-settings-sheet" :title="settings_sheet_title">…</c-sheet-dialog>
여는 쪽: x-on:click="document.getElementById('chat-settings-sheet')?.showModal()"
사용자 지문·대사 사용 예
{% include "chat/_message_row.html" with message_role="user" message_content="* 고개를 끄덕인다 * 그럼 내일도 같이 갈래?" only %}
지문은 별도 문단, 대사는 금색 말풍선으로 원문 순서대로 표시합니다. 상황만 보낼 수도 있습니다.
상황 입력 안내 사용 예
{% include "chat/_situation_tools.html" with conversation=conversation %}
같은 form의 textarea[name="message"]를 사용합니다. 안내는 body에 표시하는 비모달 레이어입니다. 네이티브 Popover API를 사용하지 않고 입력 포커스와 한글 조합을 유지합니다.
MY·공지 부품
탑툰 Account·Notice·Navigation 세트를 옮긴 부품입니다.
화면 제목 · c-page-header
내 채팅
최근 대화를 이어가요
양피지 내역
화면 제목 예시
<c-page-header title="내 채팅" subtitle="최근 대화를 이어가요" />
<c-page-header title="양피지 내역" back_href="{{ back_url }}" />
재화 pill · c-currency-pill
재화 pill 예시
<c-currency-pill kind="parchment" value="1,200" href="..." />
<c-currency-pill kind="key" value="3" />
재화 카드 · c-asset-card
열쇠
양피지
재화 카드 예시
<c-asset-card title="양피지" value="1,200" tone="amber">
<c-slot name="breakdown"><c-asset-row label="일반" value="1,000" /></c-slot>
</c-asset-card>
공지 카드 · c-notice-card
댓글 공헌도 배지 도입 안내
가을 이벤트로 양피지를 더 받아 가세요. 제목이 길면 한 줄로 줄입니다.
공지 카드 예시
<c-notice-card href="..." title="..." category="notice" category_label="공지" :date="notice.published_at" pinned />
하단 탭 항목 · c-bottom-nav-item
하단 탭 항목 예시
<c-bottom-nav-item href="/" icon="nav-home" active_icon="nav-home-filled" label="홈" active current />
설정·목록 제어
토글·페이지 이동·기간 선택·시트 다이얼로그
토글 · c-switch
토글 예시
<c-switch id="notify" name="notify" checked>알림 받기</c-switch>
페이지 이동 · c-pagination
페이지 이동 예시
<c-pagination :page="page_obj" query="{{ filter_query }}" label="공지 페이지" />
기간 선택 · c-date-range
기간 선택 예시
<c-date-range start_value="2026-09-13" end_value="2026-10-13" hx-get="..." hx-target="#history" />
시트 다이얼로그 · c-sheet-dialog
시트 다이얼로그 예시
<c-button x-on:click="document.getElementById('privacy-sheet').showModal()">열기</c-button>
<c-sheet-dialog id="privacy-sheet" title="개인정보 및 보안">…</c-sheet-dialog>
MY 메뉴·내역 부품
탑툰 MY 메뉴 행(65px)과 coin·ticket-history 카드입니다.
메뉴 묶음 · c-menu-group · c-account-row · c-content-row
계정
메뉴 묶음 예시
<c-menu-group title="계정" title_id="account-group-title">
<c-account-row id="personas-row" href="…" title="페르소나 관리" description="대화 속 '나'를 설정해요">
<c-slot name="icon"><c-icon name="users" size="16" /></c-slot>
</c-account-row>
<c-account-row type="submit" title="로그아웃" /> ← href가 없으면 버튼
</c-menu-group>
내역 카드 · c-history-card
내역 카드 예시
<c-history-card href="{{ event.detail_url }}" tone="negative">
<c-slot name="title">새 대화 답변</c-slot>
<span class="block">확정</span>
<c-slot name="amount">-100 양피지</c-slot>
<c-slot name="meta"><time …>2026.10.18 09:30</time></c-slot>
</c-history-card>
작은 설정 선택창
작은 설정 선택창 예시
<c-dropdown selection name="display_mode" label="답변 표시 방식" variant="ghost" size="sm" trigger_size="pill" align="end"
trigger_class="min-w-24 !rounded-full !bg-consumer-surface-active/70 !text-sm !shadow-none"
menu_class="!rounded-consumer-2xl [&_[aria-selected=true]]:!bg-consumer-surface-active [&_[aria-selected=true]]:!text-consumer-heading">
<option value="stream" selected>스트리밍 표시</option>
<option value="full">완료 후 전체 표시</option>
</c-dropdown>
c-parchment-balance-summary
양피지 잔액 요약
계정 설정 상세형과 헤더 메뉴 축약형
상세형
보유 수량과 일반·보너스 잔액을 표시합니다.
보유 양피지
- 일반
- 1,100
- 보너스
- 25 D-12
상세형 잔액 예시
<c-parchment-balance-summary general_balance="1100" bonus_balance="25" title="보유 양피지" general_label="일반" bonus_label="보너스" bonus_expiry_label="D-12" />
축약형
헤더 잔액 메뉴 안에서 사용하는 배치입니다.
보유 양피지
1,125개
- 무료
- 25개
- 유료
- 1,100개
축약형 잔액 예시
<c-parchment-balance-summary general_balance="1100" bonus_balance="25" title="보유 양피지" general_label="유료" bonus_label="무료" compact />
c-content-row
콘텐츠 행
아이콘·제목·설명과 오른쪽 동작을 조합합니다.
설정 컨트롤 콘텐츠 행 예시
<c-content-row title="테마" description="내게 편한 밝기를 선택합니다.">
<c-slot name="icon"><svg class="nav-icon" aria-hidden="true"><use href="#icon-home"></use></svg></c-slot>
<c-slot name="action"><c-button variant="secondary" size="sm">시스템</c-button></c-slot>
</c-content-row>
공통 장면 감상
도입·엔딩·격리 미리보기가 같은 미디어와 고정 원고 컴포넌트를 사용합니다.
이미지와 여러 문구
오류 장면에서 중단
영상 없는 패널 도입 · 대표 이미지로 전환
도입이 끝났어요. 첫 메시지를 작성할 수 있어요.
도입 말풍선 · 사진 있음과 대체 표시
게시 화자 ID와 일치하는 공통 아바타를 사용합니다. 사진이 없거나 실패하면 첫 글자로 대체합니다.
공통 장면 감상 사용 예
<section x-data="sceneSequencePlayer('intro')" x-init="configure(serverPayload)" @scene-sequence:complete="introFinished = true">
<c-scene-sequence-player />
</section>
serverPayload는 소유·게시 검증을 통과한 서버 값입니다. kind, url, image_duration_ms, text_speed_cps, lines를 전달합니다.
도입 영상은 음소거로 자동 시작하며 영상 제어에서 소리를 켤 수 있습니다. 다음 한 장면은 미리 읽고 같은 버퍼로 전환합니다.
intro와 preview는 한 번 완료하고 ending은 미디어만 반복합니다. 오류는 error를 전달하며 관리 미리보기에서만 edit_url을 전달합니다.
채팅처럼 높이가 정해진 패널에는 relative 컨테이너 안에 <c-scene-sequence-media embedded />를 사용합니다. 대표 미디어는 같은 컨테이너의 absolute inset-0 레이어에서 x-show="complete"로 표시합니다. intro는 완료·생략 후 600ms 동안 사라지며 대표 미디어를 드러냅니다. 모션 감소 설정에서는 즉시 교체합니다. 원고 영역 <c-scene-sequence-lines />의 기본은 테두리 카드이고, 작품 새 대화 도입처럼 대화 영역 안에서는 variant="chat"으로 첫 대사와 같은 말풍선 문법(대사 말풍선·서술 왼쪽 선·사용자 말풍선)을 씁니다. embedded의 재생 컨트롤은 미디어 위에 겹쳐(좁은 화면 위쪽·넓은 화면 아래쪽) 재생 중과 완료 뒤 미디어 크기가 같고, 완료 후에는 상태 안내만 낭독으로 남깁니다.
실제 화면은 visibilitychange에서 syncVisibility(), pagehide에서 pauseForPageHide()를 연결합니다.
작품 새 대화에서는 전체 게시 도입의 마지막 영상 자산을 공용 scene_timeline에 전달해 첫 답장 전 마지막 도입 영상만 반복합니다. 영상이 없는 도입은 기존 시작 장면을 사용합니다. 원고 완료와 입력 해제는 도입 플레이어가 담당하며, 반복 플레이어는 문구를 다시 출력하지 않습니다.
빈 상태·오류·처리 중
상태는 이름과 다음 행동으로 설명하고 입력과 버튼 크기를 유지합니다.
조건에 맞는 이야기가 없어요
메시지를 보내지 못했어요
양피지가 부족해요
답변을 준비하고 있어요
입력한 내용을 저장했어요
상태 안내 사용 예
<c-notice tone="error" title="메시지를 보내지 못했어요">
입력한 내용은 그대로 남아 있어요.
<c-slot name="actions"><c-button variant="secondary">다시 시도</c-button></c-slot>
</c-notice>
밝고 어두운 미디어의 상태 문구
영상의 대표 이미지와 마지막 프레임은 유지하고 상태 문구에는 테마와 관계없이 같은 배경막을 적용합니다.
미디어 상태 안내 사용 예
<div class="relative">
<!-- 실제 img 또는 video를 같은 표면 안에 유지합니다. -->
<c-media-status x-show="loading">불러오는 중…</c-media-status>
<c-media-status x-show="buffering && !loading && !mediaError">잠시 기다려 주세요…</c-media-status>
</div>
현재 미디어의 상태를 x-show로 전달합니다. 이 부품은 미디어를 교체하거나 재생 상태·소유·과금을 변경하지 않습니다.
화면 조합 규칙
색상과 조작 언어를 공유하고 화면 목적에 맞는 배치와 정보 밀도를 선택합니다.
탐색
가용 폭 · 이미지 중심 · 카드와 레일
제목·짧은 설명 → 카드 → 다음 콘텐츠를 확인하는 행동 순서로 구성합니다.
몰입
읽기 공간 · 장면 · 대사와 서술
창밖의 비가 잦아들고 두 사람 사이에 잠깐의 고요가 흘렀다.
대사와 서술을 구분하고 입력과 마지막 메시지가 키보드에 가리지 않도록 배치합니다.
정보 처리
단일 과업 · 최대 672px · 불투명 표면
보유 양피지
- 일반
- 1,100
- 보너스
- 25 D-12
확인할 정보 → 입력·선택 → 결과와 주요 행동 순서로 구성합니다.
시작 장면 선택 사용 예
<div class="flex flex-col items-start gap-consumer-label">
<label for="scene" class="text-consumer-label">시작 장면</label>
<c-dropdown selection id="scene" name="scenario" label="시작 장면">
<option value="first" selected>처음 만난 날</option>
<option value="evening">비 오는 저녁</option>
</c-dropdown>
</div>
탐색 작품 정보
작품 소개 → 작품 태그 → 등장인물. 두 인물의 이름과 대표 태그를 바로 확인합니다.
공통 검색
헤더 진입 → 블러 모달 미리보기 → 별도 결과 화면. 전체 화면 블러와 등장 전환을 겹치지 않고 즉시 표시합니다.
진입점을 누르고 두 글자 이상 입력하면 고정 미리보기가 보입니다. 모바일에서는 전체 화면으로 열립니다. 열린 상태에서 화면을 줄여도 입력과 닫기를 화면 안에 유지합니다. Esc·닫기는 입력 포커스와 배경 스크롤을 복원합니다.
작품, 태그로 검색해 보세요.
검색 결과를 불러오는 중...
검색 결과가 없어요.
미리보기를 불러오지 못했어요.
공통 검색 사용 예
<c-discovery-search />
<!-- 문서당 한 번만 배치합니다. 고정 샘플은 고유 id와 sample을 지정합니다. -->
<c-discovery-search id="search-sample" sample>
<c-slot name="suggestions">고정 태그 예시</c-slot>
<c-slot name="preview">고정 미리보기 예시</c-slot>
</c-discovery-search>
<c-tag href="결과 주소" title="최근 검색어">
최근 검색어
<c-slot name="remove">
<c-tag-remove aria-label="최근 검색어 삭제" @click="removeRecent(item)" />
</c-slot>
</c-tag>
전체 검색 결과
공통 입력·복수 선택·탑툰 검색 카드 격자(768px 미만 2열, 이상 auto-fill 10.5rem)·빈 상태를 조합합니다. 아래 내용은 고정 예시입니다.
전체 검색 결과 사용 예
<c-section-heading level="1" title="검색 결과" />
<c-input id="query" name="q" />
<c-button type="submit">검색</c-button>
<c-tag href="태그 결과 주소" title="일상">#일상</c-tag>
<div class="grid grid-cols-2 gap-3 md:grid-cols-[repeat(auto-fill,minmax(10.5rem,1fr))]">
<c-content-card class="block" href="작품 주소" title="작품 이름" description="소개">…</c-content-card>
</div>
<c-notice title="조건에 맞는 작품이 없어요.">검색어 또는 태그를 바꿔 다시 찾아보세요.</c-notice>
홈 장르 목록
장르 칩(전체 + 고정 8개, md) → 탐색과 같은 작품 카드 격자 → 다음 페이지 표식 순서입니다. 작품만 인기순으로 보여 줍니다. 아래 내용은 고정 예시입니다.
이 장르의 작품을 준비하고 있어요.
실제 홈은 장르 영역에 화면 높이에서 8rem을 뺀 최소 높이를 확보해, 작품이 없거나 적은 장르로 바꿔도 칩의 세로 위치를 유지합니다.
홈 장르 목록 사용 예
<section id="home-genre-shelf" class="min-h-[calc(100dvh-8rem)]">
<c-chip-tabs label="장르">
<c-chip size="md" href="/?genre=fantasy" selected hx-get="/?genre=fantasy" hx-target="#home-genre-shelf" hx-swap="outerHTML" hx-push-url="true" hx-sync="#home-genre-shelf:replace">판타지</c-chip>
</c-chip-tabs>
<div class="grid grid-cols-2 gap-3 md:grid-cols-[repeat(auto-fill,minmax(10.5rem,1fr))]">
…작품 카드…
<div id="home-genre-next" class="col-span-full" hx-get="/?genre=fantasy&page=2" hx-trigger="intersect once" hx-swap="outerHTML" hx-push-url="false">
<c-button variant="secondary" shape="pill" href="/?genre=fantasy&page=2">작품 더 보기</c-button>
</div>
</div>
</section>
재화·보상 상태
서버가 확인한 진행 상태와 금액을 소비자 공통 톤으로 전달합니다. 예시는 UI 표현만 보여 줍니다.
자체 회원등급과 비활성 혜택
내 회원등급
일반누적 완료 전환 0꿀
실버까지 100꿀 남았어요.
내 회원등급
실버누적 완료 전환 110꿀
골드까지 390꿀 남았어요.
내 회원등급
골드누적 완료 전환 500꿀
플래티넘까지 1,500꿀 남았어요.
내 회원등급
플래티넘누적 완료 전환 2,000꿀
최고 등급이에요.
내 회원등급
실버누적 완료 전환 110꿀
골드까지 390꿀 남았어요.
회원 추가 혜택은 준비 중이에요. 누적 완료 전환량은 유지돼요.
전환 내용을 다시 확인해 주세요
회원등급 사용 예
<c-membership-progress :progress="membership_progress" :enabled="benefit_enabled" />
현재 이용 화면 위 전환 모달 시안
꿀을 양피지로 바꾸기
전환 내용을 확인해 주세요
- 1꿀
- 100양피지
- 75,100양피지
양피지가 부족해요
- 120개
- 75개
- 45개
잠금·처리·결과
전환 결과를 확인하고 있어요
전환을 확인하지 못했어요
- 220꿀
전환이 완료됐어요
- 220꿀
- 100양피지
- 75양피지
보상 지급 상태
열쇠를 지급하고 있어요
- 열쇠 1개
- 3개
열쇠 지급이 완료됐어요
- 열쇠 1개
- 4개
빈 상태와 긴 내용
아직 전환 내역이 없어요
처리 결과를 확인하는 중이에요. 화면을 닫아도 이 상태는 이어지며, 이미 가진 양피지는 계속 사용할 수 있어요.
재화 상태 사용 예
<c-economy-state title="전환이 완료됐어요" tone="success" used_label="사용한 꿀" used_value="220꿀" received_label="받은 양피지" received_value="100양피지" balance_label="전환 후 예상 잔액" balance_value="75양피지">
서버가 확정한 완료 안내
<c-slot name="actions"><c-button variant="secondary">닫기</c-button></c-slot>
</c-economy-state>
이 상태는 실제 결제·보상 처리를 실행하지 않습니다. 제품 화면은 서버가 확정한 값을 전달하고 그 결과에 맞는 행동만 제공합니다.
유저 랭킹
상위 순위, TOP30 밖 본인, 현재 이름·최애 숨김과 0칸 지표를 같은 부품으로 표시합니다.
1위 달빛독자
최애 · 달빛 아래의 약속
92.35점
↑ 2
활동
컬렉션
출석
31위 매일의 이야기나
최애 · 공개 가능한 최애 없음
48.20점
NEW
활동
컬렉션
출석
8위 탈퇴·비활성 계정
최애 · 공개 가능한 최애 없음
63.00점
비교 없음
활동
컬렉션
출석
공개 준비 중
유저 랭킹 행 사용 예시
<c-user-ranking-row :row="consumer_row" :steps="bar_steps" own />
<c-ranking-contribution label="출석" :value="row.bars.2" :steps="bar_steps" />