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

recipes — 결정적 부분의 실행 코드 (use_figma)

같은 입력이면 같은 Figma 결과가 나와야 하는 단계(표지 · 프로토타입 링크 · 브랜드 특성화 · 브랜드 바인딩 · 슬롭 지문 측정 · 한글 어절 줄바꿈 · 프레임 모드 읽기 · 비교 시트 읽기 · Figma 대조 입력 · 구조 대조 입력 · 다른 밝기 복제본)는 글로 설명하지 않고

같은 입력이면 같은 Figma 결과가 나와야 하는 단계(표지 · 프로토타입 링크 · 브랜드 특성화 · 브랜드 바인딩 · 슬롭 지문 측정 · 한글 어절 줄바꿈 · 프레임 모드 읽기 · 비교 시트 읽기 · Figma 대조 입력 · 구조 대조 입력 · 다른 밝기 복제본)는 글로 설명하지 않고 코드로 고정한다. 에이전트는 파일 안의 PARAMS 블록만 채운다 — 실행마다 해석이 달라지던 부분(표지 y 값·라벨 간격, 모드 고정 누락, 프로토타입 0개)을 없애려는 폴더다. 브랜드 이름·값은 어떤 파일에도 적지 않는다(실행 때 라이브러리에서 읽는다 — figma-brand-selection.md). 관측한 값은 스크래치 파일 실측이고, 어디에도 없는 동작은 '미검증'으로 적었다.

공통 실행법#

  1. figma:figma-use 를 먼저 로드한다 (use_figma 호출 전 필수).
  2. 레시피 파일 전체를 읽어 use_figma 의 code 에 붙이고, // ===== PARAMS ~ // ===== END PARAMS 사이만 바꾼다. skillNames: "figma-use".
  3. 반환 객체를 읽는다. 아래 표의 보고에 싣는 것은 요약하지 말고 그대로 결과 보고에 넣는다.
  4. 한 호출은 한 페이지만 다룬다(figma-use 규칙: 페이지 전환은 호출당 1회). 페이지가 여럿이면 호출을 나눈다.
  5. 문법 점검(유지보수자): 파일 전체를 (async()=>{ … })() 로 감싸 node --check — 최상위 await·return 때문에 그대로는 실패한다.
레시피반환 중 보고에 싣는 것
cover.figma.jscheck 의 false 항목 · 썸네일 수동 안내
brand-catalog.figma.js브랜드별 색상군·채도 등급·명도·대비·적격 · warn
brand-bind.figma.js pure · pinnedAll · rebound · before / after · unmapped · mixedIcons · surface / surfacePinned
prototype-links.figma.js counts · skipped 전부 · flows
slop-scan.figma.js total · 프레임별 counts · 0 이 아닌 항목의 노드 목록 · accent.ratio / cap · capSource · wrap.skippedCount
keep-all.figma.js changedCount · ids · skipped 전부 · tooLong 전부 · leftover (0 이 아니면 임시 노드가 남은 것)
frame-modes.figma.js app · blocking 전부 · warnings · unpinned · otherCollections · 글자 단계( typography )의 모드
compare-sheet.figma.js issues 전부 · kpi 와 byVerdict · 섹션이 쓰는 컴포넌트의 rows (이름 · 판정 · CoUI 대응 · 메모) · domain 의 조립 부품
compare-frames.figma.js 반환 전체를 frames-<섹션>.json 에 그대로 — 보고에는 blocking 전부 · warnings 전부 · 프레임별 paints.literal
design-spec.figma.js 반환 전체를 <화면>.design.json 에 그대로 — 보고에는 blocking 전부 · warnings 전부 · counts
dark-frames.figma.js created · failed 전부 · remove 의 leftover(0 이 아니면 복제본이 남은 것)

cover.figma.js#

  • 하는 일: 페이지 Cover 에 1280×720 표준 5슬롯 표지를 짓는다. 같은 이름의 기존 Cover 프레임은 지우고 다시 만든다(재실행해도 결과 동일). 새 파일의 빈 Page 1 은 Cover 로 재사용한다(그래서 Cover 가 첫 페이지). 폰트가 하나라도 없으면 throw 한다 — 대체하지 않는다.
  • PARAMS: company·track(조직 표준 라벨 — 다른 조직·트랙이면 바꾼다. 값은 design.md Step 1.5) · appName(앱 로고 부재 시 대문자 워드마크 폴백 — 값은 design.md Step 1.5) · version · kind(App/Console) · period(기본 현재 연월). pageName 은 표준값(Cover) — 레시피 검증용 임시 페이지에서만 바꾼다.
  • 반환: check 는 기준 표지 실측값과 대조한 결과다(기준값은 cover.figma.js 의 check).
  • 한계: ① 파일 썸네일은 setFileThumbnailNodeAsync 가 use_figma 에서 지원되지 않아 수동이다(안내문은 반환 thumb). 만료: use_figma 가 그 API 를 지원하면 레시피 끝에 넣고 수동 안내를 걷는다. ② 우측 끝 대조는 기본 문구 기준이다 — 다른 문자열은 글리프 폭이 달라 rightEdges 가 false 일 수 있다(그대로 보고). ③ 앱 로고 인스턴스가 있으면 로고 상자에 직접 넣는다 — 워드마크는 로고 제작 전의 폴백이다. ④ 색·글꼴은 이 레시피의 고정값이다 — 변수 바인딩 대상이 아니다(Cover 페이지는 대조 대상 밖 — figma-workflow §1).

brand-catalog.figma.js#

  • 하는 일: 게시된 브랜드 컬렉션마다 key 5개를 import 해 Light 모드 실값으로 푼다(Light 로 고정한 임시 프레임 기준). 임시 프레임은 끝에서 지운다. 변수·스타일·라이브러리·파일을 바꾸지 않는다.
  • 키 수집: 검색 호출·필터 규칙은 figma-brand-selection.md §2 — 걸러 낸 항목의 key 를 브랜드마다 keys 에 넣는다. 항목의 variableSetKey(컬렉션 키) 하나로 { brand, setKey } 만 줘도 된다 — 컬렉션 변수 목록에서 5개를 찾는다.
  • 반환: 필드는 코드의 return. warn 은 변수·컬렉션 이름이 기대와 다르거나 Light 모드가 없을 때 채워지고 그 브랜드는 eligible:false 다. 브리프 색상군을 BRIEF_FAMILIES 에 주면 hueGap 이 채워진다(무채색은 null — 순위 §4).
  • 한계: Light 모드만 본다(Dark·HC 미검증).

brand-bind.figma.js#

브랜드 변수 key는 로컬/게시 변수 모두 가능하다. ALLOWED_OVERRIDES에는 manifest가 지정한 { nodeId, collectionId }를 넣어 해당 component/slot subtree의 별도 색상 스타일을 보존한다. 파일에 로컬 컬렉션이 있다는 사실만으로 pure가 실패하지 않는다. 등록하지 않은 다른 브랜드 바인딩은 계속 elsewhere로 보고한다. pure는 이 레시피의 색 페인트 검사이며 전체 테마·상태· 레이아웃·코드 재현까지 통과했다는 뜻은 아니다.

  • 하는 일·실행 시점: 모드 고정 → 다른 컬렉션 페인트 재바인딩 → 전·후 스캔·순도 판정. 정의는 figma-brand-selection.md §6 — 마지막에 돌리는 이유는 consumption §7 '순서'. 이후 Variant 를 바꿨다면 다시 실행한다 — 재실행은 안전하고 이미 맞는 페인트는 건드리지 않는다.
  • PARAMS: BRAND = { name, key } — name 은 · 뒤 브랜드 이름, key 는 그 컬렉션 변수 아무거나 하나(brand-catalog 입력의 keys.primary 등). TYPO_KEY(Web 프레임이 있으면 필수 — Typography 변수 하나의 key, 예 font-size/headline-large; 폭 ≥ 1024 프레임을 Desktop 모드로 고정) · FRAMES — 같은 페이지의 화면 프레임 id 또는 공유 컴포넌트(COMPONENT·COMPONENT_SET) id(섹션 아님). 컴포넌트는 페인트 재바인딩만 받고 모드는 고정하지 않는다 — 모드 고정(색·타이포·서페이스·밀도)은 화면 프레임에만 건다. 컴포넌트에 고정하면 그 인스턴스가 프레임의 Dark 를 따르지 않아 다크 확인에서 표 같은 공유 부품이 Light 로 남는다. 프레임 고정이 거부되면 pinFail 에 사유가 남고 pinnedAll 이 false 가 된다. name 과 컬렉션 이름이 다르면 throw 한다. SURFACE = { key, mode }(선택) — 형태 · 재질 레버(figma-direction.md §D2). key 는 Semantic/ Surface 변수 하나의 key(예 radius/16 — CoUI 라이브러리로 한정한 search_design_system 결과에서 컬렉션 이름이 Semantic/ Surface 인 항목), mode 는 서페이스 스타일 이름. 모든 프레임에 그 모드를 고정한다 — 반경·선 두께와 그림자가 바뀌고, 라이브러리가 표면 재질 행을 싣고 있으면 반투명 · 블러 · 안쪽 하이라이트까지 바뀐다(§D2 확인 방법). DENSITY = { key, mode }(선택) — 테마 밀도 레버(§D2). key 는 Semantic/ Dimension 변수 하나의 key(예 space/16), mode 는 compact · standard · spacious. 모든 프레임에 고정한다 — 간격 · 컨트롤 행 높이 · 행을 따르는 모서리가 바뀌고 글자 크기는 그대로다. 두 파라미터 모두 key 가 비면 고정하지 않는다. 모드 이름이 없으면 있는 모드 목록과 함께 throw 한다.
  • 이름 → 키: 코드가 컬렉션 열거(getVariablesInLibraryCollectionAsync)로 푼다 — 소비 파일에서 동작한다.
  • 반환: 필드는 코드의 return — 통과 판독(pure && pinnedAll · libraryLiteral)은 figma-brand-selection.md §6.
  • 한계: 시맨틱·선택한 MODE·SOLID 부분집합만 다룬다(그라디언트·이미지 페인트·효과 색은 처리하지 않는다). 선택 컬렉션 밖의 프리미티브에 직접 묶인 페인트는 옮기지 않고 elsewhere 로 알린다.
  • 하는 일: 스펙의 이동 정보를 옮긴 EDGES 로 출발 노드에 ON_CLICK 반응을 걸고 페이지 flowStartingPoints 를 지정한다. 각 간선은 따로 try 하므로 일부가 실패해도 나머지는 적용되고, 실패는 사유와 함께 skipped 로 돌아온다. 적용한 노드는 반응을 다시 읽어 대조한다.
  • PARAMS: PAGE(이름 또는 id) · EDGES = [{ fromFrame, fromNode, toFrame, action:'NAVIGATE'|'BACK' }] · FLOWS = [{ name, frame }]. <…> 자리표시자가 남아 있으면 throw 한다. 프레임 지정 = 노드 id | 섹션 > 프레임 | 페이지 안에서 유일한 프레임 이름. fromNode = 노드 id(I…;… 포함, fromFrame 안이어야 함) | 프레임 안 이름 경로 Row > Avatar(단계마다 하위 어디든, 끝이 유일해야 함 — 인스턴스 안쪽 포함).
  • 규칙: 노드당 ON_CLICK 1개 · 기존 ON_CLICK 교체 · FLOWS 대입 시점은 코드 주석 3·4번.
  • 반환: 필드는 코드의 return. 안쪽 중첩 반응은 인스턴스 루트에서 읽히지 않는다 — 읽을 때는 트리를 따라 내려간다.
  • 한계: 같은 페이지의 프레임·섹션 직속 프레임만 찾는다(섹션 안 섹션 미지원 · 다른 페이지 도착은 불가 — Figma 가 오류로 거부한다. 그런 간선은 시도하지 않고 미연결 목록에 사유와 남긴다). visible=false(BOOLEAN 로 숨긴) 노드는 못 찾는다. ON_CLICK→NAVIGATE/BACK 외 트리거·액션은 다루지 않는다.

slop-scan.figma.js#

  • 하는 일: 화면 프레임에서 AI 슬롭 지문 후보를 노드 값으로 센다 — 카드 안 카드 · 같은 크기 설명 타일 · 한쪽 굵은 색 띠 · 강조색 면적 · 그림 문자 · 흔한 가명·더미·부품 기본 문구 · 스펙 문구에 남은 저작 메모 · 지표 모양의 수치 · 조사 병기 · 높임 수준 혼용 · 대시·가운뎃점 구분 · Figma 가 스스로 꺾은 한글 글자. 읽기 전용이다 — 노드·변수·스타일을 바꾸지 않는다. 무엇을 왜 세는지는 figma-slop-tells.md, 판정(후보 → 등급)은 design-self-check 11번이 한다.
  • PARAMS: FRAMES — 같은 페이지의 화면 프레임 id(섹션 아님) · ACCENT_CAP — 브리프 '강조 면적' 칸의 상한(0–1), 칸이 기본이거나 비었으면 null(기본값 사용). <…> 자리표시자가 남아 있으면 throw 한다. 경계값(크기 · 두께 · 문구 목록 · 기본 상한)은 파일 상단 상수가 유일한 정의다.
  • 누가 놓은 것만 세는가: 경계·선 항목(카드 안 카드 · 색 띠)은 라이브러리(remote) 컴포넌트 설계에 원래 있는 노드 — 가장 가까운 remote 인스턴스의 main component 에 같은 이름 경로가 있는 노드 — 를 빼고 센다. 슬롯에 넣은 내용과 오버라이드는 저작자 것으로 센다. 글자 항목은 인스턴스 안 글자도 센다.
  • 반환: 필드는 코드의 return. metric 은 지표 모양의 글자일 뿐이다 — 스펙 수치 체크리스트와의 대조는 self-check 가 한다(가격 · 시각 · 개수와 전화번호의 국가 번호는 뽑지 않는다). separator 는 기호로 이어 붙인 자리가 있는 글자 노드 목록이고, 화살표 뒤는 그 자리의 앞뒤 조각이다(건수는 노드 수). accent.nodes 는 겹쳐 세지 않도록 이미 센 강조 면의 안쪽을 뺀 목록이다. memo 는 저작 메모 후보 — 화살표 뒤가 걸린 말이다(placeholder 로 이미 센 글자는 다시 세지 않는다). wrap.nodes 는 높이로 센 줄 수가 직접 나눈 줄 수보다 많은 한글 글자 — 후보이고 줄 끝이 어절 경계인지는 사람이 본다(반환의 notes.wrap 도 같은 말). wrap.skipped 는 줄 높이를 알 수 없어 세지 않은 글자와 사유다 — 범위마다 스타일이 다름 · 줄 높이 자동 · 위아래 여백 자르기 · 고정 상자.
  • 한계: ① 실제 Figma 파일 실측 전이다(가짜 노드 트리로 동작만 확인 — 미검증, wrap · memo 는 ⑦ 의 실측이 있다). 경계값은 초기값이고, 첫 실측에서 오탐·누락이 보이면 상수를 고치고 이 줄을 실측 기록으로 바꾼다. ② 이름 경로로 라이브러리 설계를 가르므로, 인스턴스 안 레이어 이름을 바꾸면 저작자 것으로 센다. ③ 높임 수준은 문장 끝 어미로만 가른다 — 늘 판단 이다. ④ 그라디언트·이미지 채움은 강조색 면적에 넣지 않는다(SOLID 만). ⑤ 대시·가운뎃점은 글자만 보고 센다 — 단위가 낀 범위 · 띄어 쓴 빼기 부호 · 붙여 쓴 열거도 후보로 나오고(판단), 양쪽이 숫자로 맞닿은 붙임표 · 엔 대시는 구분이어도 세지 않는다. | · / 는 세지 않는다. ⑥ 저작 메모는 보수적으로 센다 — 문구에 대한 말임이 분명한 구는 어디서든, 낱말 하나(가안 · 예시 · 임시 · 미정 · TBD)는 괄호 안에 그것만 있거나 줄 끝 대시 뒤에 그것만 있을 때만. 그래서 문장 중간에 낱말로 섞인 메모는 놓친다. ⑦ 줄 수는 높이 ÷ 줄 높이(문단 간격을 뺀다)로 세므로 어디서 꺾였는지는 모른다 — 띄어쓰기 자리에서 꺾인 글자도 후보로 나온다. wrap 과 memo 는 스크래치 파일에서 실측했다 — 어절 중간에서 꺾인 글자 2개가 후보로 나왔다가 keep-all 뒤 0 이 됐고, — 데모 문구 는 걸리고 체험용 데모 — … · (데모 값) 은 걸리지 않았다.

keep-all.figma.js#

  • 하는 일: 한글이 든 글자가 줄을 넘길 때 어절 경계에서 꺾이게 한다 — 같은 글꼴 속성의 임시 글자 노드로 어절 누적 폭을 재서, 넘치는 어절 앞의 띄어쓰기를 U+2028(Figma 의 Shift+Enter)로 바꾼다(CSS word-break: keep-all 흉내). 규칙과 이유는 figma-slop-tells.md §S11. 임시 노드는 실패해도 지운다(finally).
  • 실행 시점: 글자와 폭(패딩 · 레이아웃)을 다 정한 뒤, brand-bind 앞에 페이지마다 한 번. 그 뒤 글자나 폭이 바뀌면 MODE = 'rewrap' 으로 다시 돌린다.
  • PARAMS: FRAMES — 같은 페이지의 화면 프레임 · 공유 컴포넌트 id(섹션 아님). MODE — 'wrap'(넘치는 줄만 나눈다 · 있는 줄바꿈은 지킨다) · 'rewrap'(전에 넣은 U+2028 을 띄어쓰기로 되돌린 뒤 다시 나눈다 · LF 는 지킨다) · 'unwrap'(U+2028 을 띄어쓰기로 되돌리기만 — 레시피가 넣은 줄바꿈을 걷어 낼 때). 손으로 정한 줄 나눔은 LF(Enter)로 넣어야 rewrap · unwrap 이 건드리지 않는다.
  • 글자 쓰기: coui-library-consumption.md §7 '글자 바꾸기' 와 같다 — TEXT property 에 결선된 인스턴스 글자는 setProperties, 안 바뀌면(결선 누락) 글자를 직접 쓴다. 쓰기 전에 글꼴을 로드한다.
  • 반환: 필드는 코드의 return. ids 는 바꾼 노드 전부(잘리지 않는다), changed 는 앞 25개의 원래 글자와 줄 수 · 쓴 방법(property · characters). skipped 는 건너뛴 글자와 사유(범위마다 스타일이 다름 · 글꼴 없음 · 첫 줄 들여쓰기 · 폭 0) — 그 글자는 손으로 정한다. tooLong 은 한 줄보다 긴 어절이다 — 그 어절은 그대로 두었다(폭이나 값을 바꿀지는 저작이 정한다).
  • 한계: ① 띄어쓰기에서만 나눈다 — 붙임표 · 빗금 뒤처럼 CSS 가 허용하는 다른 자리는 보지 않는다. ② 범위마다 스타일이 다른 글자는 건너뛴다(통째로 다시 쓰면 첫 글자의 스타일로 덮이기 때문). ③ 임시 노드는 글꼴 · 크기 · 자간 · 대소문자와 텍스트 스타일을 따라 하지만 OpenType 기능 등 나머지 차이는 FIT_SLACK 으로만 흡수한다. ④ 실측(스크래치 파일, Pretendard) — 16px 수업명이 Figma 에서 …회복 스트레/칭 으로 꺾이던 폭에서 …회복⏎스트레칭 으로, 14px 세 줄 문단은 어절 경계에서만 꺾였다. 줄 수와 높이는 그대로였고(Figma 가 U+2028 을 줄바꿈으로 그린다) 임시 노드는 남지 않았다. 같은 실측에서 불러오기 전의 hasMissingFont 가 사용 가능한 글꼴에도 true 로 읽힌 적이 있다 — 그래서 글꼴을 먼저 불러 보고 판단한다.

frame-modes.figma.js#

  • 하는 일: 화면 프레임에 고정한 변수 모드를 읽어 앱 인자로 옮긴다 — Semantic/ Color · <브랜드>(브랜드 · 밝기) · Semantic/ Surface(서페이스 스타일) · Semantic/ Dimension(밀도) · Semantic/ Typography(글자 단계). 읽기 전용이다 — 노드 · 변수 · 스타일을 바꾸지 않는다. 읽는 값은 resolvedVariableModes — 프레임과 그 조상(섹션)에 명시한 모드다. 아무도 고정하지 않은 축은 그 컬렉션의 기본 모드(defaultStyle · standard · Mobile)로 센다. 옮기는 규칙은 cc-flutter:figma-to-coui 「Figma → CoUI 번역 명세」 F5, 실행 시점은 commands/build.md Step 0.
  • PARAMS: FRAMES — 같은 페이지의 화면 프레임 id(섹션 아님). 앱 하나의 화면 프레임 전부를 준다 — 앱 인자는 화면이 아니라 앱의 값이다. <…> 자리표시자가 남아 있으면 throw 한다.
  • 반환: app = { brand, surfaceStyle, density }(앱 루트 CoUIApp 에 옮길 값) 또는 null. blocking 이 비어 있지 않으면 null 이다 — 고정한 브랜드 · 서페이스 스타일 · 밀도가 프레임마다 다르거나, 한 프레임에 색 컬렉션이 둘 이상이거나, 이름을 읽지 못한 컬렉션 · 모드가 있다(unread). warnings · unpinned 는 색 모드를 고정하지 않은 프레임이다 — 묶인 변수를 기본 모드로 그리므로 브랜드가 바뀌지는 않지만 다크 확인에서 따라오지 않는다(brand-bind 를 다시 돈다). otherCollections 는 CoUI 의 네 축이 아닌 컬렉션 모드다. typography 는 프레임마다의 글자 단계 — 코드 인자가 아니다(코드는 한 벌 · 알려진 차이).
  • 브랜드 이름: 컬렉션 이름의 Semantic/ Color · 뒤다. 앱 테마 모듈의 CoreBrand 를 id 또는 displayName 으로 찾는다 — 실측에서 컬렉션 이름은 표시 이름이 아니라 소문자 id 였다.
  • 한계: ① 모드 고정만 읽는다 — 페인트가 어느 컬렉션에 묶였는지는 보지 않는다(그건 brand-bind 의 순도 스캔). ② 실측(제품 파일, 휴대폰 화면 프레임 15개 · 한 페이지) — 13개가 브랜드 컬렉션의 Light 를 고정했고 상태 프레임 2개는 고정하지 않아 warnings 로 나왔다. 서페이스 스타일은 일부 프레임만 고정해 나머지가 기본으로 세어졌고(모두 defaultStyle), 밀도는 standard, 글자 단계는 전부 Mobile 이었다. CoUI 의 네 축이 아닌 컬렉션(Colors · Primitives)이 otherCollections 로 나왔다. 판정 분기(일치 · 기본값 · 불일치 · 미고정 · 읽지 못함)는 가짜 노드 트리로도 확인했다.

compare-sheet.figma.js#

  • 하는 일: 디자이너가 제품 컴포넌트를 CoUI 와 나란히 놓고 판정한 비교 시트를 표로 읽는다 — 카드마다 이름 · 판정 · CoUI 대응 · 달라지는 점 · 구조/조립 메모, 그리고 시트의 판정별 개수(KPI)와 목록. 읽기 전용이다. 판정을 쓰는 사람은 디자이너다 — 이 레시피는 판정을 만들지 않고 읽는다. 옮기는 규칙은 cc-flutter:figma-to-coui 「Figma → CoUI 번역 명세」 F8, 실행 시점은 commands/build.md Step 0.
  • PARAMS: ROOT — 비교 시트를 담은 프레임 또는 섹션 id. VERDICTS — 시트의 판정 낱말(KPI 칸의 이름). 띄어쓰기는 무시하고, 카드가 판정 뒤에 · <제품> 을 붙여도 같은 판정으로 센다. <…> 자리표시자가 남아 있으면 throw 한다.
  • 반환: rows — 카드마다 { id, name, category, verdict, coui, absent, changes, noteKind, note, datedNote, usage }. coui 는 카드의 'CoUI 현재' 줄, 없으면 목록 행의 CoUI 대응이고, absent 는 카드가 'CoUI 에 없음' 이라 적은 것이다. noteKind 는 메모 제목('구조' · '조립'), datedNote 는 날짜로 시작하는 갱신 메모(다시 불러와 비교한 기록 류), usage 는 목록 행의 쓰임(화면 · 개수)이다. domain — 카드 밖 '조립:' 글로 적은 도메인 전용 묶음의 { name, purpose, parts }. issues — KPI 의 판정별 개수와 읽은 카드 수가 다르거나, KPI 에 없는 판정이 있거나, 같은 이름의 카드가 둘 이상이면 여기에 적는다.
  • 판정: issues 가 비어 있지 않으면 표를 그대로 쓰지 않는다 — 시트의 모양이 레시피의 가정과 달라 카드를 놓쳤거나 더 읽은 것이다. 노드를 열어 확인하고, 시트를 고칠 일이면 디자이너에게 요청한다.
  • 카드를 찾는 법: 자기 글자(카드 안 라이브러리 인스턴스의 글자는 뺀다)의 셋째 이내에 판정 낱말이 있고 '달라지는 점' 이 있는 가장 안쪽 프레임이 카드다 — 두 카드를 담은 행은 카드가 아니다. 목록 행은 카드 밖에서 셋째 글이 판정인 4~6 글 프레임, KPI 칸은 [판정, 'n개', 설명] 프레임이다.
  • 한계: ① 시트의 글자 모양에 기댄다 — 판정 낱말 · '달라지는 점' · 'CoUI 현재' · '구조' · '조립' 이 바뀌면 PARAMS 나 레시피를 고친다. issues 가 그 신호다. ② 실측(제품 파일 두 벌, 시트 한 장씩) — 한 시트는 카드 34장이 KPI 4칸(17 · 6 · 2 · 9)과 맞았다. 다른 시트는 카드 43장 · KPI 5칸(CoUI 에 추가됨 포함)인데, 도메인 전용 카드가 판정 뒤에 제품 이름을 붙여 처음에는 0장으로 셌다 — 그래서 접미사를 같은 판정으로 센다. ③ 카드의 미리보기 인스턴스는 읽지 않는다 — 시안의 모양은 get_design_context 가 읽는다. ④ 시트의 'CoUI 현재' · 메모의 이름은 캔버스 이름이다 — 코드의 이름 · 인자는 F0 의 components.json 과 F9 로 확인한다.

compare-frames.figma.js#

  • 하는 일: 화면 레지스트리 섹션의 상태 프레임을 Figma 대조의 입력으로 읽는다 — 프레임마다 플랫폼 · 상태(이름에서), 논리 크기, 색 컬렉션과 모드, 색 페인트가 변수에 묶였는지, 쓰인 서체, 상태 바 · 홈 인디케이터 높이, 그리고 노드 행(글자 · 라이브러리 인스턴스 · 직계 자식의 범위)을 돌려준다. 읽기 전용이다. 반환을 그대로 frames-<섹션>.json 에 저장하면 cc-pixel-loop 의 visual-verify 가 figma_compare.py plan 의 입력으로 쓴다 — 어떻게 비교하는지는 그 스킬이 정한다.
  • PARAMS: SECTION — 레지스트리의 figma_node(섹션). 화면 프레임 id(use_case 의 designLink)를 주면 그 프레임이 든 섹션 전체를 읽는다 — 등록된 상태마다 대조하기 때문이다(warnings 에 남는다). FRAMES — 비우면 섹션 안 화면 전부, 주면 그 id 만(프레임 하나만 대조할 때). NAMES — 「플랫폼-상태」 이름이 아닌 옛 파일에서만 프레임 id → 이름을 준다(쓰면 warnings 에 남는다). INSET_NAMES — 상태 바 · 홈 인디케이터를 찾는 이름 패턴. NODE_CAP — 프레임마다 노드 행 상한(글자 행이 먼저).
  • 화면의 모양: 프레임 · 화면 컴포넌트 · 그 인스턴스 모두 화면으로 읽는다 — 실측한 제품 파일 두 벌은 화면을 인스턴스로 두었다.
  • 반환: frames[] = { id, name, platform, state, width, height, color, paints, fonts, insets, nodes, instances } · warnings · blocking. 숨긴 프레임은 읽지 않고 warnings 에 이름을 남긴다. 「플랫폼-상태」 이름이 아닌 프레임은 그대로 실린다 — plan 이 판정 불가로 세거나(unplanned) --ignore-frame 의 이유로 뺀다. 플랫폼은 Mobile · Web 둘이라 Tablet-… 프레임도 이름이 아닌 것으로 읽히고, 경고가 따로 남는다(figma-workflow.md §5 R2 — 태블릿은 Widgetbook 의 Viewport 로 본다). paints.literal 이 0 이 아니면 그 프레임은 다른 밝기로 그릴 수 없다 — plan 이 그 비교를 unthemable 로 둔다(design-self-check 3번 토큰 단일 출처 — L0). fonts 에는 상태 바 · 홈 인디케이터 안 글자를 넣지 않는다 — 앱이 그리지 않는 글자다. instances 는 라이브러리(remote) 인스턴스만 센다 — 제품 파일의 로컬 컴포넌트는 CoUI 와 이름이 같아도 라이브러리가 그린 것이 아니다. nodes 의 마지막 칸은 그 노드가 든 가장 바깥 라이브러리 인스턴스의 세트 이름이다 — 차이 영역을 CoUI 버전 차이로 면제할 때 그 근거가 된다.
  • 판정: blocking 이 비어 있지 않으면 비교하지 않는다. warnings 는 보고에 그대로 싣는다(이름 표기 · 채움 없음 · 리터럴 색 · 상태 바 없음 · 노드 상한).
  • 한계: ① 상태 바 · 홈 인디케이터는 프레임 직계 자식의 이름 · 위치로 찾는다 — 다르게 그린 파일은 INSET_NAMES 를 고친다. ② 실측(제품 파일, 휴대폰 화면 1장) — 402 × 874 · 위 띠 62 · 서체는 상태 바의 SF Pro 를 뺀 Pretendard 하나였고, 색 페인트 82개 중 72개가 리터럴이라 다른 밝기는 그릴 수 없다고 나왔다. 다른 제품 파일의 화면은 색 페인트가 하나도 변수에 묶이지 않았다.

design-spec.figma.js#

  • 하는 일: 상태 프레임 하나를 co_golden 의 구조 대조 입력(co_golden.design_spec v1)으로 읽는다 — 노드마다 범위(프레임 기준 논리 px) · 칠(색 · 묶인 변수 이름 · 스타일 이름) · 선 · 상자 반경 · 그림자 · 오토 레이아웃(패딩 · 간격), 글자의 서체 · 굵기 · 크기 · 행간 · 자간 · 대소문자 · 꾸밈 · 정렬 · 텍스트 스타일 이름 · 글자 속성에 묶인 변수, 인스턴스의 세트 이름 · 변형 속성 · 컴포넌트 속성. 읽기 전용이다. 반환을 그대로 <화면>.design.json 에 저장해 co_golden 의 GoldenDesignReference(specPath: …) 로 넘기면(사용법은 co_golden README 「Design references」 · 「Design analysis」와 cc-flutter:golden-test-guide 6절), co_golden 이 캡처의 위젯 트리와 맞춰 어떤 글자 · 위젯 · 컴포넌트가 어떻게 다른지(굵기 700 → 600 · 간격 8 → 16 · Button Variant primary ↔ secondary)를 보고한다. 픽셀 비교가 놓치는 굵기 한 단계 같은 차이를 값으로 잡는 입력이다. compare-frames 는 범위만 싣는다(픽셀 대조 영역의 이름용 — visual-verify) — 두 레시피는 함께 쓴다.
  • PARAMS: NODE_ID — 대조할 상태 프레임 하나(FRAME · COMPONENT · INSTANCE). designLink 의 node-id 를 그대로(12-345 도 된다). 섹션이면 throw 한다 — 섹션의 프레임 목록은 compare-frames 가 읽는다. FILE_KEY · SOURCE_URL · VERSION — 선택, source 를 채운다(FILE_KEY 를 비우면 figma.fileKey — use_figma 에서 읽혔다). INSET_NAMES — 상태 바 · 홈 인디케이터 이름 패턴(compare-frames 와 같다). NODE_CAP — 노드 상한(기본 800).
  • 무엇을 싣나: 보이는 노드를 문서 순서로(부모가 먼저) 싣는다. 숨김 · 투명 · 잘려 보이지 않는 노드(그 아래까지) · 빈 글자 · 상태 바 · 홈 인디케이터 띠 안은 싣지 않는다(띠 높이는 frame.insets). 인스턴스 안의 글자 · 도형도 싣는다 — 버튼 라벨의 서체를 대조하려면 필요하다. 글자의 색은 text.color 에 둔다(노드 fills 로 두면 칠한 상자로 읽힌다 — REST 변환과 같다). 범위마다 스타일이 다른 글자는 가장 많은 글자를 덮는 스타일이 노드의 값이고, 다른 범위는 text.runs(UTF-16 위치, 그 범위의 전체 스타일)다. 컴포넌트 속성 이름의 #id 꼬리는 뗀다(id 는 바뀐다 — 이름으로 맞춘다). 바꿔 끼운 인스턴스(INSTANCE_SWAP)는 세트/이름 으로 푼다. 변수 · 스타일은 id 가 아니라 이름이다(color/on-surface · Title/Medium) — 변수의 컬렉션 이름은 칠의 collection 에 따로 둔다. 알파 0 인 그림자는 그리는 것이 없어 싣지 않고, 반경은 상자 모서리인 노드(FRAME · COMPONENT · INSTANCE · RECTANGLE)만 싣는다.
  • 상한: NODE_CAP 을 넘으면 칠하지 않는 묶음 → 칠한 도형 순으로 깊은 것부터 뺀다 — 글자 · 인스턴스 · 이미지는 빼지 않는다. 빠진 노드의 자식은 남은 가장 가까운 조상에 붙고, warnings 에 개수가 남는다. counts.bytes 는 노드 JSON 의 크기다 — 400 KB 를 넘으면 경고가 남는다. 반환이 잘리면 상한을 낮추거나 프레임을 나눈다.
  • 반환: { schema, schemaVersion, source, frame, nodes, counts, warnings, blocking } — co_golden 의 co_golden.design_spec 파서가 읽는 모양 그대로다(counts · warnings · blocking · collection · colorStyle · text.variables 는 파서가 무시하는 보조 칸이다). blocking 이 비어 있지 않으면 저장하지 않는다. 칠의 color 는 이 프레임이 그리는 모드의 값이다 — 다른 밝기는 값보다 변수 이름(variable · colorVariable)으로 대조한다. dark-frames 복제본에 다시 돌린 color 가 그 모드의 값으로 읽히는지는 미검증이다.
  • 한계: ① 파일 버전 · 수정 시각은 Plugin API 가 읽지 못한다 — VERSION 을 주지 않으면 null 이다(언제 내보냈는지는 커밋이 말한다). ② use_figma 에서 figma.root.name 은 파일 이름이 아니라 Document 로 읽혀(실측) fileName 을 null 로 둔다. ③ 그라디언트 · 이미지 칠은 종류만 싣는다(색 · 정지점 없음). 블러 · 블렌드 모드 · 경로 모양은 싣지 않는다. ④ 실측(CoUI 라이브러리 파일, 읽기만) — Card 변형 둘(260 × 143, 노드 11 · 12) · Alert 변형(340 × 66, 노드 9) · 문서 예시 프레임(514 × 676, 노드 9)에서 텍스트 스타일(Title/Medium · Body/Small) · 색 변수(color/on-surface) · 글자 변수(font-size/title-medium) · 세트 인스턴스(Icon Size=24, 바꿔 끼운 lucide/info)가 읽혔고, 반환은 co_golden 파서로 그대로 읽혔다. 숨긴 슬롯 아래의 Badge 인스턴스는 싣지 않았고, 이 모드에서 투명한 그림자 토큰(#00000000)이 남던 것을 뺐다. 제품 화면 파일 실측 전이다 — 노드가 수백 개인 화면의 반환 크기 · 시간은 미검증. 가짜 노드 트리 검증은 scripts/test_design_spec_recipe.mjs(노드 종류에 없는 속성을 읽으면 throw 하는 use_figma 처럼 동작한다).

dark-frames.figma.js#

  • 하는 일: 대조용으로 화면 프레임을 복제해 다른 밝기 모드로 고정한다 — 원본은 건드리지 않는다. 복제본은 이름 앞머리 ⟂ compare , 페이지 바로 아래 · 오른쪽 빈자리, 잠금 상태로 놓인다. 쓰기다 — 이 레시피 중 화면 파일에 노드를 만드는 유일한 대조 단계라, 받은 뒤에는 반드시 remove 를 돌린다.
  • 실행 순서: ACTION = "create" → 돌려준 clone id 로 download_assets(png, defaultScale) → ACTION = "remove"(실패해도). create 도 남아 있던 복제본을 먼저 지운다 — 앞머리가 없는 노드는 어떤 경우에도 지우지 않는다.
  • PARAMS: ACTION — create · remove. FRAMES — 같은 페이지의 화면 id(remove 는 그 페이지의 복제본 전부). MODE — 고정할 모드 이름 = 그 비교의 밝기(기본 Dark. 프레임이 Dark 로 고정돼 있으면 Light).
  • 모드 고정: 원본이 물려받던 모드(조상 섹션의 고정 포함)를 복제본에 그대로 고정한 뒤, MODE 이름의 모드가 있는 컬렉션은 전부 그 모드로 바꾼다 — 시안이 브랜드 컬렉션 밖의 색 컬렉션에 묶여 있어도 함께 바뀐다. 화면이 컴포넌트면 복제하지 않고 인스턴스를 만든다 — 컴포넌트를 복제하면 게시 대상이 하나 늘어난다.
  • 반환: created[] = { frame, clone, mode, switched } · failed[](브랜드 색 컬렉션이 없거나 그 모드가 없는 프레임) · removedStale. remove 는 removed · leftover(0 이 아니면 지우지 못한 복제본이 남은 것).
  • 한계: ① 같은 호출 안에서는 복제본의 resolvedVariableModes 를 다시 읽어도 바뀐 모드가 보이지 않는다(실측) — switched 는 고정한 컬렉션의 목록이고, 바뀌었는지는 내려받은 이미지가 보여 준다. ② 편집 권한이 없는 파일에서는 돌지 않는다 — 그 비교는 판정 불가로 남는다. ③ 리터럴 색은 모드를 바꿔도 그대로다 — compare-frames 의 paints.literal 이 0 이 아닌 화면은 돌릴 필요가 없다. ④ 실측(제품 파일) — create → 내려받기 → remove 에서 남은 복제본 0. 변수에 묶인 뱃지 채움은 다크 값으로 내보내졌고, 리터럴 채움은 라이트 그대로였다.

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

↑ ↓ 이동 · Enter 열기Esc 닫기