문서 목차
이 페이지에서목차

CoUI Figma 라이브러리 소비 규약

cc-blueprint 가 Figma 에서 제품 화면을 저작할 때 쓰는 부품의 출처와 사용 방식을 고정한다.

cc-blueprint 가 Figma 에서 제품 화면을 저작할 때 쓰는 부품의 출처와 사용 방식을 고정한다. 라이브러리를 만드는 쪽이 아니라 쓰는 쪽의 규약이다 — 생성 파이프라인의 SoT 는 coco-de/coui 저장소의 tools/figma-token-sync 플러그인이다.

§1. 출처 — 무엇이 정본인가#

항목값
라이브러리 생성기 coco-de/coui tools/figma-token-sync — coui_core 컨트랙트의 투영
데이터 SSOTcomponents.json schema v3 (coui repo 산출물)
페이지 규약 컴포넌트당 1페이지 + 섹션 헤더 페이지 — 이름·섹션·프레임 구성은 생성기 정본(coui tools/figma-token-sync README) 소유
축·프로퍼티 Variant= · Size= · State= · Enabled= 등 부품마다 다른 축 + TEXT·BOOLEAN component property (문구·플래그). 축 이름·값은 variant set 의 실제 property 에서 읽는다(추측 금지)
브랜드 축 색 variable 은 디자이너가 발행한 컬렉션 Semantic/ Color · {Brand} 로 나뉜다. 어떤 브랜드가 있는지는 실행 때 발견하고 하나를 골라 처음부터 바인딩한다 — 절차·금지는 figma-brand-selection.md . 저작 중 컬렉션을 갈아끼우지 않는다. 반경 · 선 두께 · 그림자(와 라이브러리가 싣는 만큼의 표면 재질)는 라이브러리에 Semantic/ Surface 가 있으면 그 컬렉션의 모드(서페이스 스타일)로, 간격 · 컨트롤 행 높이는 Semantic/ Dimension 에 밀도 모드가 있으면 그 모드로 바뀐다 — 고르는 법은 figma-direction.md §D2
버전 기준 둘이고, 묻는 것이 다르다. ① 스킬 기준 — 이 저장소 루트 coui.version.json 의 coui_skills_basis (bare-widget 세대: 위젯 Co 접두 제거, Core* enum 체계 유지). 스킬 문서의 이름 · 예제가 그 세대로 쓰였는가를 답한다. ② 앱 기준 — 그 앱이 해석한 CoUI 릴리스( .dart_tool/package_config.json · pubspec.lock , cc-pixel-loop:visual-verify 의 figma_compare.py coui ). 그 앱이 무엇을 그릴 수 있는가를 답한다 — 어떤 컴포넌트 · API 가 있는지, 캔버스와 렌더의 차이가 버전 차이인지는 앱 기준으로만 판단한다. ①을 앱의 판으로 읽으면 버전 차이를 앱 코드에서 "고친다". 라이브러리 구조·축은 마이너 단위로도 변한다 — 세대 내 변경의 감지·흡수는 skills/coui-library-update

§2. 사용 규칙#

  1. 라이브러리 인스턴스만 — 제품 파일에는 CoUI 라이브러리 컴포넌트의 instance 를 놓는다. detach·복제 후 수정·CoUI 에 있는 부품을 프리미티브로 흉내 전부 금지 (figma-workflow §4 L0). 인스턴스 안 값 조정은 복제가 아니다 — 허용 범위는 §7, 라이브러리가 표현 못 하는 필요는 §4·§8.
  2. variant 는 property 로 전환 — 상태·크기·종류는 인스턴스의 component property (Variant=/Size=/State=/Enabled=/기타 축)로 바꾸고, 문구·플래그는 TEXT·BOOLEAN property 로 넣는다. 다른 variant 를 새로 그리거나 문구용 텍스트 노드를 덧대지 않는다.
  3. 색·간격·타이포·표면은 제품 테마에 바인딩 — 라이브러리 및 제품 로컬 변수·텍스트/효과 스타일을 사용한다. 전체 테마와 컴포넌트/variant/state/슬롯/인스턴스 override를 구분한다. 제품 고유 값은 제품 토큰으로 정의할 수 있다. 등록한 base·brand·component 의존성은 혼입이 아니며, 의도하지 않은 기본 브랜드 잔존을 검사한다.
  4. 코드 대응 확인 — 인스턴스로 놓은 컴포넌트가 코드에서 무엇인지는 그 페이지의 Properties 표·Examples 프레임이 1차 소스다. Flutter 구현 시 cc-coui:* 스킬 중 해당 컴포넌트의 것이 API 정본이다.

§3. 프리플라이트 (실행 단계의 정본은 design.md Step 0)#

그 단계가 기대는 규칙만 둔다:

  • 변수·CoUI 컴포넌트 검색은 get_libraries 로 얻은 CoUI 라이브러리 키로 한정한다(search_design_system 의 includeLibraryKeys=[CoUI 키]) — 변수는 컬렉션마다 같은 이름이 하나씩, 키만 다르게 나오므로 한정하지 않으면 엉뚱한 컬렉션의 것을 집을 수 있다. 아이콘 글리프는 별도 아이콘 라이브러리에 있어 이 검색에 안 나온다 — 그 라이브러리 키로 따로 찾는다.
  • 라이브러리가 '추가됨'이 아니라 libraries_available_to_add 로만 보여도 키 import 는 동작한다 — 중단하지 말고 보고에 「라이브러리 미추가」를 적는다.
  • 세대(coui_skills_basis)가 어긋나면 저작은 계속하고 skills/coui-library-update 로 라우팅한다(보고 문구는 design.md Step 0). 구현할 앱이 정해져 있으면 앱 기준도 본다 — 앱의 릴리스와 라이브러리 사이에서 달라진 컴포넌트는 그 화면의 캔버스와 렌더가 다를 자리다(design.md Step 0 항목 4). 저작은 막지 않는다.
  • 기본 브랜드 및 제품·컴포넌트 override 컬렉션의 이름·id·적용 범위를 DDR에 기록한다. 원격 import/로컬 조회 후 실제 소속과 해석값을 검증한다. 모드 고정·재바인딩은 figma-brand-selection.md §6을 따른다.

§4. 라이브러리에 없는 부품이 필요할 때#

디자이너 원본 규칙(figma-workflow §5 R5)의 판정 질문을 따른다 — 차단이 아니라 분류다:

  1. 기존 컴포넌트 조합으로 대체 가능한지 먼저 판정 (cc-coui:coui-composition-and-extension 참조)
  2. 불가하면 판정 질문: 「이 제품이 아닌 다른 제품에서도 이게 필요한가?」
    • 그렇다(범용) → promote/{Component} 로 만들어 쓴다 — 부채 표시이며 CoUI 승격 백로그로 보낸다(coui repo 에 승격 제안 + DDR 첨부 — §6 절차). 저작은 계속한다.
    • 아니다(도메인 전용) → {domain}/{Component} (예: 제품 슬러그가 petcare 이면 petcare/VaccinationCard) 도메인 컴포넌트로 만들어 900 components 페이지에 둔다. {domain} = 제품 슬러그(파일 이름·기존 도메인 접두에서) — 페이지 이름·레지스트리 domain 칸의 기능 도메인(store 등)과는 다른 값이다. 여러 화면 재사용일 때만 컴포넌트화 — 한 화면 전용 요소는 해당 프레임 안에 그대로 둔다(둘 이상의 화면에 나오면 design.md Step 1.7).
  3. 두 경로 모두 CoUI 인스턴스의 detach·재제작으로 흉내 내지 않는다(§2-1). /cc-blueprint:sync 가 promote/ 부채 목록을 추적한다.

§5. 라이브러리 부품이 잘못 그려질 때#

부품이 없는 것이 아니라 있는데 이상한 경우다. 가장 흔한 형태 하나를 이름 붙여 둔다 — 인스턴스를 늘렸는데 안쪽이 안 따라오는 것. 실제로 발견된 계열이며 한 부품의 문제가 아니라 여러 부품에 같은 모양으로 존재했다.

우회하지 않는다#

부품 안쪽 구조 결함(늘려도 겉면이 안 따라오는 것)은 소비 파일에서 고치지 않는다. 고치려고 detach·복제하면 §2-1 의 L0 다. 의미에 맞는 컴포넌트를 그대로 쓰고, 결함은 생성기에서 고친다. 결함 없는 다른 컴포넌트로 갈아타는 것도 답이 아니다 — 시안이 의미를 잃고, 재발행 후 다시 갈아타야 한다.

반대로 §7 의 허용 오버라이드는 우회가 아니라 부품을 쓰는 정상 방법이다 — 결함 이슈의 근거로도 세지 않는다.

예외는 하나다. 다른 컴포넌트가 그 자리에 원래 더 맞을 때는 갈아탄다. 그 판단의 근거는 "결함이 없어서"가 아니라 "형태가 맞아서"여야 하고, DDR 에 그렇게 적는다.

절차#

1. 재본다 — 자연 크기 인스턴스와 늘린 인스턴스를 나란히 두고 안쪽 노드를 대조한다.
   재는 법은 `skills/coui-library-stretch-audit`.
2. 같은 계열이 더 있는지 훑는다 — 하나 나오면 대개 여럿이다. 세로 축과 비기본
   variant 까지 본다.
3. coui 저장소로 라우팅 — §6 절차. 증거는 잰 숫자 그대로.
   생성기 수정 PR 과 재발 방지 검사는 coui 저장소 작업이 요청 범위에 있을 때만 이어서 연다 — §6 처리는 범위와 무관하게 항상 한다.
4. 저작은 계속한다 — 차단하지 않는다. DDR 로 기록하고, 화면 레지스트리·설계 문서에
   "재발행 대기" 와 §6-5 의 기록을 남긴다.
5. 해소 시점은 라이브러리 재발행이다 (`skills/coui-library-update`).

왜 소비 측이 이걸 발견하게 되는가#

생성기 쪽 검사는 빌더가 무엇을 썼는지를 본다. 이 결함은 그린 결과를 늘려 봐야 드러난다 — 소스에는 늘어난다고 적혀 있는데 늘어남을 담당하는 노드가 다른 노드였던 경우가 실제로 있었다. 그래서 제품 화면을 저작하는 쪽이 첫 목격자가 되는 일이 구조적으로 생긴다. 목격했으면 위 절차로 넘긴다 — 시안에서 조용히 피해 가면 다음 사람이 같은 것을 다시 발견한다.

§6. 라이브러리 결함 이슈 처리 — 모드별#

소비 측에서 라이브러리 결함을 목격하면 놓치지 않고 coco-de/coui 로 올린다. 사람에게 "이슈로 올릴까요?" 묻지 않는다. 외부 저장소에 쓰는 일은 요청자가 허용했을 때만 하므로 모드가 있다:

모드조건처리
draft (기본 — 단독 실행 포함) 요청문·세션 지시가 gh 사용을 명시적으로 허용하지 않음 gh 를 쓰지 않는다(검색·댓글 포함). 아래 4번 본문을 초안 파일로 남기고 draft pending 으로 표시
file 요청자가 gh 로 이슈 등록을 명시적으로 허용 아래 절차 전부 — 이 경우 이 절이 상시 승인이다

등록 대상 — 무엇이 라이브러리 결함인가#

분류판별 기준 (소비 측에서 잰 사실)
크기 추종인스턴스를 늘렸는데 부품 자신의 겉면(배경·외곽선·규칙선·트랙·본문 영역)이 따라오지 않음 — §5
속성 결선 누락TEXT·BOOLEAN component property 를 바꿔도 화면이 바뀌지 않음 (라벨·표시 여부가 노드에 연결 안 됨)
속성 미노출·상한 화면마다 바꿔야 하는 문구·값·플래그가 property 로 노출되지 않아 직접 오버라이드만 가능하거나 표현 자체가 안 됨, 또는 스펙이 요구하는 개수가 부품 상한을 넘음(§8)
미발행원본 파일에 있는 컴포넌트가 소비 파일에서 key import 실패 (not found)
렌더 붕괴특정 셀·슬롯의 텍스트 폭이 0 이 되는 등 자연 크기에서도 깨짐
토큰 드리프트 코드 토큰과 라이브러리 variable 값이 다름 (coui-library-update 로 흡수할 skew 는 제외 — 재발행 필요 판정만 등록)
접근성 기준 미달부품 기본 토큰·크기가 접근성 기준(대비·터치 영역)에 못 미침 — 잰 값과 기준을 적는다(§C7 ③)

등록하지 않는 것: 저작 실수, 정상 판정(skills/coui-library-stretch-audit "안 커지는 것이 맞는 것")에 해당하는 것, 제품 도메인에만 필요한 부품의 부재(§4 — 도메인 컴포넌트로 해결). CoUI 에 없는 범용 부품의 부재는 결함이 아니라 승격 제안이다 — 같은 절차로 올리되 제목을 "승격 제안"으로 구분한다.

절차 (저작을 막지 않는다)#

1. 모은다 — 결함 1종 = 이슈 1건. 인스턴스마다 올리지 않는다. 같은 원인의 variant·축은 한 건에
   묶고, 원인이 다르면 나눈다. 세션 중 발견분을 모아 저작 단계 끝(design.md Step 4 의 이슈 처리 단계)에 한 번에 처리.
2. 재본다 — 잰 숫자가 없으면 올리지 않는다. 자연 크기 vs 늘린 크기, property 전·후 값,
   import 응답 원문. 가능하면 원본 라이브러리 파일에서 읽기 전용으로 다시 확인한다.
3. 중복 확인 (file 모드) — `gh issue list -R coco-de/coui --state all --search "<컴포넌트> <증상>"`.
   열린 같은 이슈가 있으면 새로 만들지 않고 **새 증거를 댓글로** 단다. 닫힌 이슈에 같은 증상이
   재발했으면 새 이슈를 만들고 닫힌 이슈를 링크한다(회귀 표시).
4. 본문 — coui 저장소의 이슈 템플릿·라벨을 따른다. 필수 항목:
   현상 · 재현(원본 파일 key · 컴포넌트 node · 소비 파일 인스턴스 크기) · 측정값 ·
   소비 측 대응(§7 오버라이드·§8 대안/생략) · 기대 동작 · 수정 위치 추정(생성기) · 재발 방지 검사 제안.
5. 되적는다 — 이슈 URL(또는 아래 draft pending 한 줄)을 self-check L1 사유 칸, DDR, 화면 레지스트리 note 에 넣는다.
   L1 사유가 "라이브러리 결함"인데 둘 다 없으면 그 L1 은 사유 미기록으로 본다.

draft 모드 — 등록하지 않을 때#

gh 미인증·권한 없음·네트워크 차단이거나 draft 모드이면 저작은 계속하고, 위 4번 본문을 초안 파일로 남긴다. 위치: 요청이 정한 경로 → 없으면 .claude/docs/{scope}/coui-issue-{컴포넌트}-{증상}.md → 코드 scope 도 없거나 파일을 쓸 수 없으면 보고서 본문에 초안 전문을 싣는다(DDR 도 같은 곳). L1 사유에는 한 줄로 이슈 초안 {경로} — 미등록(draft pending) 을 적는다({경로} 는 파일 경로, 본문이면 보고서 본문 #{초안 번호}) — 이 문자열이 사유 미기록 판정의 기준이니 다른 문구를 쓰지 않는다. 3번 중복 확인은 gh 가 필요하므로 건너뛰고 초안에 "중복 미확인"으로 적는다. 최종 보고에 초안 대기 건수를 따로 싣는다. 등록한 척하지 않는다 — URL 이 없으면 등록되지 않은 것이다.

공개 범위#

이슈 본문에는 라이브러리 쪽 사실만 적는다. 소비 제품의 화면 문구·고객 데이터·내부 운영 경로는 넣지 않는다 — 재현에 필요한 것은 소비 파일의 인스턴스 크기와 노드 구조뿐이다.

§7. 인스턴스 오버라이드 허용 범위#

인스턴스를 놓은 뒤 안쪽 값을 바꿔도 되는 범위다. 어느 값을 쓰는지(행 인셋·슬롯 정렬의 선택)는 figma-composition-patterns.md §C1~§C9 — 이 절은 연산이 허용되는지만 정한다.

인스턴스 오버라이드는 §2-1 이 금지한 '복제 후 수정'이 아니다 — 부품은 그대로 두고 값만 얹는 정상 사용이다. 전체 테마 위에 컴포넌트·variant/size/state·부모 슬롯·인스턴스 스타일을 적용할 수 있다. 로컬 토큰/효과 스타일도 허용한다. 지원하지 않는 내부 구조는 제품 컴포넌트 조합이나 API 확장으로 표현하고, 코드 지원 gap을 남긴다. 아래 표는 이미 검증한 연산의 예시이며 허용 속성의 상한이 아니다. 표 밖의 속성도 현재 API를 확인하고 작은 인스턴스에서 적용·읽기·렌더·swap 후 보존을 검증하여 사용할 수 있다. 지원 불가 속성은 제품 조합/스타일/API 확장으로 표현하고 코드 대응과 남은 gap을 기록한다.

허용 연산방법 (실측)주의
글자 바꾸기 property 가 있는 글자는 setProperties . 쓴 직후 그 글자 노드를 다시 읽어 안 바뀌었으면(결선 누락) 텍스트 노드 characters 를 직접 쓴다. 쓰기 전에 그 노드의 현재 폰트를 로드( getStyledTextSegments(['fontName']) → loadFontAsync ) 로드 없이 쓰면 unloaded font 오류. 결선 누락·미노출은 §6 대상
채움·글자색·선·패딩 재바인딩 · 안쪽 선·반경 끄기 · 루트 clip·반경·gap · 효과 스타일 적용 인스턴스 루트와 안쪽 노드 모두 가능. 페인트는 setBoundVariableForPaint 가 돌려주는 새 페인트를 다시 대입. 효과는 라이브러리 효과 스타일( shadow/* ·inset)을 키로 import 해 프레임·인스턴스 루트에 setEffectStyleIdAsync — 크기 변화 없고 Variant 교체 뒤에도 유지 색·간격 출처는 §2-3. 높이 = 본문 + 2×패딩 + 2×선 두께. 선이 없던 안쪽 노드에 선을 새로 주면 루트가 커진다 — 기존 선 재바인딩이 먼저. 효과 스타일은 원격 스타일이라 로컬 스타일 생성이 아니고, 효과 컬렉션의 shadow/* 변수는 문자열이라 바인딩 대상이 아니다
정렬 인스턴스 루트 counterAxisAlignItems='CENTER' . 대안: leading layoutSizingVertical='FILL' + counterAxisAlignItems='CENTER' — trailing 은 위에 남는다 leading 의 counterAxisAlignItems 단독 은 오류 없이 아무 일도 안 한다 — 쓰지 않는다. 루트 CENTER 는 trailing 도 가운데로 옮긴다
슬롯 부품 교체 INSTANCE_SWAP property 를 setProperties 로 — 값은 variant 컴포넌트 id 교체할 variant 는 이름으로 골라 Size 를 명시. 교체해도 슬롯의 기존 덮어쓰기(칠·선)가 새 내용에 남아 칠이 사라진다 — 교체 전 의도된 override를 기록하고 잔존값만 정리한다. resetOverrides() 가 필요하면 이후 제품 override를 재적용·검증한다

순서 — Variant 를 바꾸면 fill 오버라이드가 사라지고, State 를 바꾸면 일부(루트 fill)가 사라진다. Size·TEXT 변경은 유지된다. 그래서 Variant·State·Size·글자를 먼저, 패딩·정렬·반경· 브랜드 재바인딩은 마지막에 한다.

지켜졌는지 — 인스턴스 안에 원본(main component)에 없는 노드 0(이름 경로로 대조 — 제품 컴포넌트의 SLOT 안은 제외) · 값·색은 design-self-check 3번.

§8. 사양과 라이브러리가 충돌할 때#

스펙이 요구하는 값·개수·구성·구분 단서를 정상인 부품이 표현하지 못하는 경우다 — 부품이 잘못 그려지는 §5, 부품이 없는 §4 와 다르다(예: Progress 값 · Tabs 7번째 탭).

규칙: 눈에 보이는 틀린 값을 시안에 남기지 않는다. 아래 순서로 처리한다.

1. 대안 표현 — 같은 의미를 다른 CoUI 부품·배치로 정확히 표현할 수 있으면 그것으로. 근거는 "결함이 없어서"가
   아니라 "형태가 맞아서"여야 하고(§5 예외와 같은 기준) DDR 에 그렇게 적는다. 부품이 없어서(예: Badge·Button 을
   담는 표) 못 그리는 것이면 §4 로 간다.
2. 대안이 없으면 생략 + known-gap 표시 — 틀린 값을 그리느니 그 값·항목을 빼고(요소 전체 또는 넘치는
   항목만), 같은 섹션 안 프레임 행 아래에 두 줄짜리 캡션 텍스트 노트(첫 줄 `known-gap {요소}`, 둘째 줄 `스펙: {스펙이 요구한 값}`)를 눈에 보이게
   둔다. 노트는 프레임이 아닌 텍스트 노드이고 제품 화면 안에는 넣지 않으며, 색·글꼴은 §2-3 그대로다.
3. 어느 쪽이든 기록 — DDR(스펙 문장 · 충돌 · 고른 처리 · 만료 조건) + 상류 목록(§6 — 표의 '속성 미노출·상한'
   또는 승격 제안).

만료 — 대안·known-gap 마다 "coui 가 지원해 재발행하면 걷고 원래 요소로 되돌린다" 식으로 DDR 에 적는다. 만료 조건 없는 대응은 영구 우회가 된다.

지켜졌는지 — design-self-check 9번에서 표시값 ≠ 스펙값이 0 이고, known-gap 노트마다 대응하는 DDR 이 있으며 그 DDR 이 상류 목록 항목(§6-5)에 이어져 있다(같은 원인은 한 이슈로 묶이므로 노트 수와 이슈 수는 달라도 된다 — §6-1).

검색어를 입력하면 문서를 찾습니다.

↑ ↓ 이동 · Enter 열기Esc 닫기