recipes — 결정적 부분의 실행 코드 (use_figma)
같은 입력이면 같은 Figma 결과가 나와야 하는 단계(표지 · 프로토타입 링크 · 브랜드 특성화 · 브랜드 바인딩 · 슬롭 지문 측정 · 한글 어절 줄바꿈 · 프레임 모드 읽기 · 비교 시트 읽기 · Figma 대조 입력 · 구조 대조 입력 · 다른 밝기 복제본)는 글로 설명하지 않고
같은 입력이면 같은 Figma 결과가 나와야 하는 단계(표지 · 프로토타입 링크 · 브랜드 특성화 · 브랜드 바인딩 · 슬롭 지문 측정 · 한글 어절 줄바꿈 · 프레임 모드 읽기 · 비교 시트 읽기 · Figma 대조 입력 · 구조 대조 입력 · 다른 밝기 복제본)는 글로 설명하지 않고
코드로 고정한다. 에이전트는 파일 안의 PARAMS 블록만 채운다 — 실행마다 해석이 달라지던 부분(표지 y 값·라벨 간격, 모드 고정
누락, 프로토타입 0개)을 없애려는 폴더다. 브랜드 이름·값은 어떤 파일에도 적지 않는다(실행 때 라이브러리에서 읽는다 —
figma-brand-selection.md).
관측한 값은 스크래치 파일 실측이고, 어디에도 없는 동작은 '미검증'으로 적었다.
공통 실행법#
figma:figma-use를 먼저 로드한다 (use_figma 호출 전 필수).-
레시피 파일 전체를 읽어
use_figma의code에 붙이고,// ===== PARAMS~// ===== END PARAMS사이만 바꾼다.skillNames: "figma-use". - 반환 객체를 읽는다. 아래 표의 보고에 싣는 것은 요약하지 말고 그대로 결과 보고에 넣는다.
- 한 호출은 한 페이지만 다룬다(figma-use 규칙: 페이지 전환은 호출당 1회). 페이지가 여럿이면 호출을 나눈다.
-
문법 점검(유지보수자): 파일 전체를
(async()=>{ … })()로 감싸node --check— 최상위await·return때문에 그대로는 실패한다.
| 레시피 | 반환 중 보고에 싣는 것 |
|---|---|
cover.figma.js | check 의 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로 알린다.
prototype-links.figma.js#
-
하는 일: 스펙의 이동 정보를 옮긴
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_CLICK1개 · 기존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-check11번이 한다. -
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.mdStep 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.mdStep 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-check3번 토큰 단일 출처 — 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_specv1)으로 읽는다 — 노드마다 범위(프레임 기준 논리 px) · 칠(색 · 묶인 변수 이름 · 스타일 이름) · 선 · 상자 반경 · 그림자 · 오토 레이아웃(패딩 · 간격), 글자의 서체 · 굵기 · 크기 · 행간 · 자간 · 대소문자 · 꾸밈 · 정렬 · 텍스트 스타일 이름 · 글자 속성에 묶인 변수, 인스턴스의 세트 이름 · 변형 속성 · 컴포넌트 속성. 읽기 전용이다. 반환을 그대로<화면>.design.json에 저장해 co_golden 의GoldenDesignReference(specPath: …)로 넘기면(사용법은 co_golden README 「Design references」 · 「Design analysis」와cc-flutter:golden-test-guide6절), co_golden 이 캡처의 위젯 트리와 맞춰 어떤 글자 · 위젯 · 컴포넌트가 어떻게 다른지(굵기 700 → 600 · 간격 8 → 16 · ButtonVariantprimary ↔ 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) · 세트 인스턴스(IconSize=24, 바꿔 끼운lucide/info)가 읽혔고, 반환은 co_golden 파서로 그대로 읽혔다. 숨긴 슬롯 아래의 Badge 인스턴스는 싣지 않았고, 이 모드에서 투명한 그림자 토큰(#00000000)이 남던 것을 뺐다. 제품 화면 파일 실측 전이다 — 노드가 수백 개인 화면의 반환 크기 · 시간은 미검증. 가짜 노드 트리 검증은scripts/test_design_spec_recipe.mjs(노드 종류에 없는 속성을 읽으면 throw 하는 use_figma 처럼 동작한다).
dark-frames.figma.js#
-
하는 일: 대조용으로 화면 프레임을 복제해 다른 밝기 모드로 고정한다 — 원본은 건드리지 않는다. 복제본은 이름 앞머리
⟂ compare, 페이지 바로 아래 · 오른쪽 빈자리, 잠금 상태로 놓인다. 쓰기다 — 이 레시피 중 화면 파일에 노드를 만드는 유일한 대조 단계라, 받은 뒤에는 반드시remove를 돌린다. -
실행 순서:
ACTION = "create"→ 돌려준cloneid 로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. 변수에 묶인 뱃지 채움은 다크 값으로 내보내졌고, 리터럴 채움은 라이트 그대로였다.