04. 모듈 기본 화면(UI) 표준
쉬운 요약 — 모듈마다 바로 쓸 수 있는 기본 화면을 함께 준다. 어느 프로그램(React·Vue·그냥 HTML)에든 <script> 하나와 태그 하나로 붙고, 색·모서리·글자 크기는 페이지가 바꾸고, 놓는 곳(글 안·전체 페이지·옆 패널·떠 있는 창·팝업)과 둘레 너비에 따라 모양이 바뀐다. 모듈 센터 「모듈 체험 → 기본 화면 · 디자인」에서 모두 바꿔 가며 확인한다.
1. 파일과 이름표
modules-out/<id>/ui/
src/iso-<이름>.js 화면 코드만 (사람이 고치는 곳)
iso-<이름>.js = 공통 바탕 + src ← scripts/build-ui.js 가 만듦 (손으로 고치지 않음)
module.json → "provides": { "ui": ["ui/iso-<이름>.js"] } (여러 개면 모두)- 태그 이름:
iso-<모듈 또는 화면 이름> (예: iso-otp-setup, iso-messenger). 한 파일에 화면 여러 개 가능 - 공통 바탕:
docs/module-platform/ui/iso-ui-base.js (window.IsoUI — Base·h·icon·define). 고치면 node scripts/build-ui.js 로 전부 다시 묶음
2. 붙이는 법 (사용처)
<script src="/static/modules/otp/iso-otp.js" defer></script>
<iso-otp-setup api="/api/otp" account="kim@isolo.co.kr" placement="modal" theme="auto"></iso-otp-setup>
- 서버 연결:
api="/api/xxx" 를 주면 POST {api}/{동작} 으로 JSON 을 주고받음(같은 출처·쿠키). 동작 이름과 주고받는 모양은 모듈 INTEGRATION.md 「화면」 칸에 적음. 다른 방식(GraphQL·인증 머리 등)이면 el.client = { call(동작, 본문) { … return Promise } } 로 바꿈. el.headers = {…} 로 머리 추가. - 사건(이벤트):
iso:<이름> (bubbles·composed) — 예 iso:enabled, iso:sent. 프로그램은 el.addEventListener('iso:sent', …) - 글 바꾸기:
el.labels = { title: '…' } 또는 속성 label-title="…" - React:
<iso-otp-setup ref={r} …/> 후 r.current.client = … (속성은 문자열만)
3. 놓는 곳 — placement
| 값 | 모양 | 쓰는 곳 |
|---|
inline (기본) | 둥근 카드, 둘레 너비에 맞춤 | 글·설정 화면 가운데 |
page | 페이지 전체, 넓으면 칸 나눔(목록+내용) | 메뉴 하나를 통째로 |
panel | 높이 100% 세로 칸(왼쪽 선만) | 오른쪽 옆 패널·서랍 |
widget | 오른쪽 아래 둥근 단추 → 떠 있는 창 (position="bottom-left" 가능). 휴대폰은 화면 전체 | 채팅·문의·알림처럼 어디서나 |
modal | 가운데 팝업 + 어두운 배경, Esc·바깥 누르면 닫힘. 휴대폰은 아래에서 올라오는 판 | 확인·입력 한 번 |
el.open() · el.close() · el.toggle() · 속성 open (widget·modal). 떠 있는 창 단추의 숫자: el.badge(n)- 화면마다 알맞은 곳만 허용해도 됨 (체험 무대의
placements)
4. 너비에 따라 — 컨테이너 쿼리
- 바탕이
:host{container-type:inline-size} → 화면 CSS 는 창 너비가 아니라 자기 둘레 너비로 @container (min-width:…) 를 씀. 그래서 같은 화면이 넓은 페이지에서는 두 칸, 옆 패널(360px)에서는 한 칸이 됨. - 기준(권장):
< 420px 좁음(한 칸·아이콘만) · 420~760px 보통 · > 760px 넓음(목록+내용 두 칸) - 휴대폰 너비(≤480px 화면)에서 widget·modal 은 바탕이 알아서 화면 전체/아래 판으로 바꿈
5. 디자인 값 — --iso-* (페이지에서 바꿈)
| 값 | 기본 | 뜻 |
|---|
--iso-accent / --iso-on-accent | #3B6FE0 / #fff | 강조색(단추·선택) / 그 위 글자 |
--iso-bg --iso-surface --iso-surface-2 | 흰색·옅은 회색 | 바탕·칸·한 단계 더 |
--iso-text --iso-muted --iso-border | | 글·흐린 글·선 |
--iso-ok --iso-warn --iso-danger (+ -soft) | | 상태 색 |
--iso-radius | 12px | 모서리 (작은 것은 2/3) |
--iso-font --iso-mono --iso-font-size | Pretendard · 15px | 글꼴·크기 |
--iso-gap | 12px | 간격(빽빽함) |
--iso-shadow · --iso-offset · --iso-widget-width/height · --iso-modal-width · --iso-page-height | | 그림자·떠 있는 창 위치·크기 |
- 테마:
theme="light|dark|auto" (auto = 사용자 기기 설정). 어둡게는 위 값들을 바탕이 바꿈 — 페이지가 --iso-* 를 주면 그것이 이김 - 더 깊게:
::part(root|header|launcher|popup|dialog|…) 로 안쪽 모양 바꾸기 - 화면 CSS 는 반드시
var(--_accent) 같은 바탕 변수만 씀(색 직접 쓰지 않기 — 상태 표시 그림 등 예외)
5.1 공통 부품 (바탕 1.2)
| 클래스 / 함수 | 쓰는 곳 |
|---|
IsoUI.skeleton(n) · .iso-skel(.line/.title/.box/.circle) | 불러오는 중 — 글 「불러오는 중…」 대신 뼈대 |
IsoUI.empty(아이콘, 제목, 한 줄, 단추) | 빈 목록·처음 화면 |
.iso-sec + .iso-sec-head .iso-sec-title .iso-sec-desc | 설정 양식의 구역 나누기 |
.iso-avatar(.round/.sm) | 목록 앞 아이콘·이름 첫 글자 |
.iso-meta .iso-strong .iso-kv .iso-divider | 보조 글(시각·크기), 강조, 이름-값 표, 나눔선 |
6. 규칙 — 반드시
- 의존 없음(틀·라이브러리 X), 한 파일. 외부 글꼴·아이콘 글꼴 X →
IsoUI.icon('이름') 선 아이콘(필요하면 화면 파일 안에 path 추가) - CSP 안전:
innerHTML·eval·new Function·인라인 on…= 금지. 글은 h()/textContent 로만 (서버 글이 HTML 로 해석되지 않게). 스타일은 바탕이 만든 시트(adoptedStyleSheets)로 - 비밀값을 화면·사건·콘솔에 남기지 않음 (키·토큰은 가려서, 입력칸은
type=password) - 접근성: 단추는
<button> + aria-label, 결과 알림은 aria-live, 팝업은 Esc·포커스, 키보드만으로 다 쓸 수 있게, 글자 대비 4.5:1 - 한국어 기본 글, 짧은 존댓말. 모든 글은
this.t('키', '기본') 로 (바꿀 수 있게) - 서버가 실패하면 화면이 죽지 않고 이유를 보여 줌 (
iso-note danger). 불러오는 동안 표시 - 같은 페이지에 같은 화면 여러 개 · 여러 모듈 화면을 같이 놓아도 서로 섞이지 않음 (Shadow DOM)
7. 모듈 센터에서 확인
「모듈 시험 → 체험」 → 모듈 페이지 맨 위 「기본 화면 · 디자인」: 놓는 곳 5가지 × 화면 너비(컴퓨터 1280 · 태블릿 820 · 휴대폰 390) × 테마 × 강조색 × 모서리 × 글자 크기를 바꿔 가며 가짜 웹사이트(머리·글·옆 칸) 안에서 실제로 눌러 본다. 무대 화면은 체험 상자의 진짜 모듈과 주고받는다.
8. 판 올리기
화면을 처음 넣으면 모듈 MINOR (예 1.0.0 → 1.1.0) — 기존 서버 부품은 그대로라 사용처가 바꿀 것 없음. 화면의 동작 이름·모양을 바꾸면(사용처 서버를 고쳐야 하면) MAJOR.