Communitygithub.com

figma-lens

Figma 디자인을 픽셀 단위로 정확하게 구현하는 UI 개발 도구입니다. figma-lens 내보내기(design/<frame>/spec.md, tokens.json, 스크린샷, SVG 아이콘)를 사용하여 Figma MCP나 Dev Mode 없이도 UI를 구현할 수 있습니다.

figma-lens란 무엇인가요?

figma-lens는 Figma 디자인 시스템을 코드로 정확하게 변환하는 전문가용 개발 도구입니다. Figma 플러그인과 로컬 브리지로 구성되어 있으며, 사용자가 Figma 데스크톱에서 프레임을 선택하고 'Export to Claude'를 클릭하면 design/ 폴더에 프레임별로 spec.md, tokens.json, 스크린샷, SVG 아이콘 등이 내보내집니다. CLI 도구(figma-lens)를 통해 compare-and-fix 루프를 제공하여 실제 결과물과 Figma 디자인 간의 차이를 3% 이하로 줄일 수 있도록 지원합니다. Flutter와 웹(HTML/CSS/React/Tailwind) 플랫폼을 지원하며, spec.md의 정확한 값을 이미지보다 우선시하고, 컴포넌트 맵을 통해 기존 코드베이스의 위젯/컴포넌트와 토큰을 재사용하도록 유도합니다. 자동 레이아웃은 Row/Column/flex로 매핑되고, 절대 위치는 Stack/Positioned로 매핑됩니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MohamedRamadanSalah/figma-lens/tree/HEAD/skill/figma-lens

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

figma-lens: Figma와 일치하는 UI 구축하기

figma-lens는 Figma 플러그인과 로컬 브리지로 구성된 도구입니다. 사용자가 Figma 데스크톱에서 프레임을 선택하고 Export to Claude를 클릭하면 브리지가 프레임당 하나의 폴더를 생성합니다:

design/
  INDEX.md                  ← 내보낸 모든 프레임 목록 (여기서 시작)
  <frame>/                   
    spec.md                 ← 먼저 읽기: 정확한 값이 포함된 간결한 레이어 트리
    spec.json               ← 전체 데이터 (spec.md로 부족할 때 Figma의 레이어별 CSS 포함)
    tokens.json             ← 변수 (모든 모드), 스타일, 원시 값 사용 횟수
    [email protected]           ← 전체 렌더링 (기본 2x)
    sections/NN-name.png    ← 최상위 블록의 전체 해상도 크롭
    assets/*.svg            ← 아이콘/벡터 아트, 바로 사용 가능
    images/*                ← 이미지 채움에 사용된 원본 비트맵
    compare/                ← `figma-lens check` 에 의해 생성됨 (report.md, side-by-side.png, diff.png)
figma-lens.map.json         ← (프로젝트 루트) Figma 컴포넌트/토큰 → 이 프로젝트의 실제 코드

figma-lens 명령어는 figma-lens 저장소의 CLI입니다 (거기서 npm link 하면 전역으로 사용 가능; 그렇지 않으면 node <figma-lens>/bin/figma-lens.js … 실행). 모든 명령어는 figma-lens help로 확인하세요.

내보내기가 아직 없는 경우

  1. 사용자 프로젝트의 브리지를 시작하고 백그라운드에서 계속 실행합니다: figma-lens bridge --out <project>/design
  2. 사용자에게 Figma 데스크톱에서 프레임을 선택하도록 요청합니다 → 플러그인 → 개발 → Figma Lens → Export to Claude
  3. design/INDEX.md를 읽어 내보내기가 완료되었는지 확인합니다.

내보내기가 가능할 때 디자인을 설명으로 추측하지 마세요. 내보내기를 요청하세요.

워크플로우

  1. spec.md를 완전히 읽습니다 (작습니다). 프레임 크기, 레이아웃 트리, 변수 및 스타일, 에셋을 확인합니다.
  2. 이미지를 봅니다. sections/*.png를 하나씩 읽어 세부 사항을 확인합니다. [email protected]는 전체 그림을 볼 때만 사용합니다. 큰 이미지는 볼 때 축소되기 때문입니다. 값은 항상 스펙에서 가져오며, 픽셀을 측정하지 않습니다.
  3. 컴포넌트 맵을 사용합니다 (아래 "컴포넌트 맵" 참조). spec.md의 모든 ⇒ USE ...는 필수입니다: 해당 위젯/컴포넌트와 프로퍼티 매핑을 사용하고, 매핑된 토큰((color/primary ⇒ AppColors.primary))을 원시 값 대신 사용합니다. (not in map)으로 표시된 항목은 코드베이스에서 기존 테마와 위젯(Flutter ThemeData, AppColors, TextTheme, 간격 상수, lib/widgets; 웹 CSS 변수, Tailwind 설정, src/components)을 검색합니다. 토큰이 없는 경우에만 원시 값을 사용하고, 추가한 값을 사용자에게 알려줍니다.
  4. 컴포넌트를 재사용합니다. component Button {Type=Primary}로 표시된 레이어는 프로젝트의 기존 Button 위젯 또는 일치하는 props를 가진 컴포넌트를 사용해야 합니다. 기존 것이 없을 때만 새로 만들고, 재사용 가능하게 만든 후 맵에 추가합니다.
  5. 트리를 따라 위에서 아래로 구축합니다. 자동 레이아웃은 Row/Column/flex로 매핑됩니다(아래 표 참조). @x,y 레이어(자동 레이아웃이 없거나 absolute)만 Stack/Positioned 또는 절대 위치 지정이 필요합니다.
  6. 내보낸 파일을 사용합니다. assets/*.svg와 images/*를 프로젝트의 에셋 폴더에 넣고 참조합니다. 아이콘을 다시 그리거나 다른 아이콘 세트로 대체하지 마세요.
  7. 텍스트를 정확히 복사합니다 spec.md의 "…"에서 대소문자, 구두점 및 아랍어/RTL 문자열을 포함하여 복사합니다. 여러 run 줄이 있는 텍스트는 리치 텍스트(TextSpan/중첩 spans)가 필요합니다.
  8. 비교-수정 루프로 검증합니다 (아래 참조). 코드를 읽고 완료되었다고 선언하지 마세요. 스크린샷 diff로 증명하세요.

비교-수정 루프

화면을 구축한 후, 실제 결과를 Figma와 비교하고 다른 점을 수정합니다:

  1. 빌드를 실행하여 해당 화면을 표시합니다:
    • 웹: dev 서버를 시작하고 경로를 엽니다.
    • Flutter 웹: flutter run -d web-server --web-port 8080, 그런 다음 해당 URL을 사용합니다.
    • Flutter Android: 에뮬레이터/기기에서 앱을 실행하고 화면으로 이동합니다.
  2. 확인합니다:
    • 웹 / Flutter 웹: figma-lens check --design design/<frame> --url http://localhost:<port>/<route> (뷰포트는 프레임 크기와 배율로 자동 설정됩니다).
    • Android: figma-lens check --design design/<frame> --adb --crop-top <상태 표시줄 px> [--crop-bottom <내비게이션 바 px>] (상태 표시줄 ≈ 24–48 dp × 기기 밀도; 앱 콘텐츠만 비교되도록 자릅니다).
  3. design/<frame>/compare/report.md를 읽고 (불일치 %, 가장 큰 차이 영역 및 각 영역의 Figma 레이어 ID) compare/side-by-side.png를 봅니다 (Figma | 빌드 | diff, 빨간색 = 다름).
  4. 위에서 아래로 수정합니다: 높이 또는 간격 차이를 먼저 수정합니다(아래 모든 것을 이동시킴). 그런 다음 크기, 색상, 타이포그래피, 반경, 그림자를 수정합니다. 나열된 각 레이어 ID를 spec.md에서 정확한 값을 찾습니다.
  5. 확인을 다시 실행합니다. 보고서에는 이전 점수가 표시되어 진행 상황을 볼 수 있습니다.
  6. 불일치가 ≤ 3%일 때 중지합니다 (텍스트 안티앨리어싱은 0에 도달하지 않음) 또는 두 번 연속 실행에서 더 이상 개선되지 않을 때 중지합니다. 그런 다음 최종 점수와 일치시킬 수 없었던 항목 및 그 이유(누락된 글꼴, 플랫폼 렌더링, 동적 데이터)를 보고합니다.

규칙: 실제 코드를 수정하고, diff를 속이지 마세요(앱 내에 디자인의 스크린샷이나 이미지 없음). 확인할 때 디자인과 동일한 데이터/텍스트를 사용합니다(필요시 모의 데이터). 그렇지 않으면 텍스트 차이가 점수를 지배합니다.

컴포넌트 맵

figma-lens.map.json (프로젝트 루트)은 Figma 컴포넌트와 토큰을 이 프로젝트의 코드에 매핑합니다:

{
  "components": {
    "Button": { "flutter": "lib/widgets/app_button.dart#AppButton", "web": "src/components/Button.tsx#Button", "props": { "Label": "label", "Type": "variant" } },
    "Icon/*": "SvgPicture.asset('assets/icons/<name>.svg')"
  },
  "tokens": { "color/primary": { "flutter": "AppColors.primary", "web": "var(--color-primary)" } }
}
  • 맵이 아직 없음 → figma-lens map --design design을 실행합니다. 내보내기의 모든 컴포넌트와 토큰을 빈 값("")으로 추가합니다.
  • 직접 채웁니다: 각 컴포넌트(이름, 모양, props로)와 각 토큰 값을 코드베이스에서 검색하고 path#Symbol(또는 표현식)을 작성합니다. 아직 존재하지 않는 경우에만 ""를 남깁니다. "Prefix/*"는 전체 패밀리(아이콘)와 일치합니다.
  • 맵을 편집한 후 figma-lens render design/<frame>을 실행하여 spec.md에 ⇒ USE 화살표가 표시되도록 합니다. 브리지는 새 내보내기 시에도 맵을 적용합니다.
  • 매핑되지 않은 컴포넌트에 대해 새 재사용 가능 위젯을 만들면 맵에 추가합니다.

spec.md 읽기

  • 단위는 디자인 px이며, CSS px 및 Flutter 논리적 px와 같습니다. 스크린샷 배율을 곱하지 마세요.
  • row|column gap G pad t,r,b,l main:X cross:Y는 자동 레이아웃입니다. pad 16은 네 면 모두 16입니다. gap auto는 space-between입니다.
  • w:FILL h:HUG는 각 축의 크기 조정을 제공합니다: FIXED(표시된 정확한 크기), HUG(내용에 맞춤) 또는 FILL(사용 가능한 공간 차지).
  • @x,y는 부모 내의 위치입니다. absolute는 자동 레이아웃 내의 절대 위치 자식입니다. pin H/V는 제약 조건을 제공합니다(MIN = 왼쪽/위쪽, MAX = 오른쪽/아래쪽, STRETCH = 둘 다, CENTER, SCALE).
  • bg A + B는 페인트를 아래에서 위로 나열합니다. (name)은 Figma 변수이고 [style name]은 Figma 스타일입니다.
  • 색상은 #RRGGBB 또는 #RRGGBBAA입니다(알파가 마지막).
  • 텍스트는 "문자" · Family Style (weight) size/lineHeight color 형식을 사용합니다. lineHeight는 px, % 또는 auto입니다. ls는 글자 간격(px) 또는 글꼴 크기의 %입니다.
  • radius 12는 모든 모서리를 설정합니다. radius tl,tr,br,bl은 각 모서리를 설정합니다. smoothing은 iOS 스타일 모서리 부드럽게 처리입니다. 근사하거나 Flutter에서 squircle 패키지를 사용하지 않는 한 무시합니다.
  • drop-shadow x,y blur B spread S color, inner-shadow, layer-blur R, background-blur R.
  • → assets/x.svg는 레이어가 아이콘임을 의미합니다. 나열된 크기로 SVG를 사용하세요.
  • vars{itemSpacing=space/md}는 레이어 속성이 해당 변수에 바인딩됨을 의미합니다. 토큰을 사용하세요.

Flutter 매핑

FigmaFlutter
column / rowColumn / Row (Flutter ≥ 3.27에서는 spacing: G 사용, 그렇지 않으면 SizedBox 간격)
wrap, rowGap RWrap(spacing: G, runSpacing: R)
pad t,r,b,lPadding(padding: EdgeInsets.fromLTRB(l, t, r, b)). 순서에 주의하세요.
main MIN / CENTER / MAX / SPACE_BETWEENMainAxisAlignment.start / center / end / spaceBetween
cross MIN / CENTER / MAX / BASELINECrossAxisAlignment.start / center / end / baseline (baseline에는 textBaseline 필요)
HUG on main axismainAxisSize: MainAxisSize.min
FILL on parent's main axisExpanded (또는 Flexible); grow N은 flex: N 설정
FILL on parent's cross axis부모에서 CrossAxisAlignment.stretch, 또는 SizedBox(width: double.infinity)
FIXED w×hSizedBox(width: w, height: h)
@x,y / absoluteStack + Positioned(left: x, top: y); 오른쪽/아래쪽에는 pin 사용
bg solidBoxDecoration(color: Color(0xAARRGGBB)). 알파를 앞으로 이동: #11223380 → 0x80112233
gradient-linear from/toLinearGradient(begin: Alignment(fx*2-1, fy*2-1), end: Alignment(tx*2-1, ty*2-1), colors, stops) spec.json의 from/to 사용
radiusBorderRadius.circular(r) 또는 BorderRadius.only(...)
border W color insideBorder.all(width: W, color: …) (inside는 Flutter의 기본값; center/outside는 strokeAlign 필요)
drop-shadow x,y blur B spread SBoxShadow(offset: Offset(x, y), blurRadius: B, spreadRadius: S, color: …)
background-blur RClipRRect + BackdropFilter(filter: ImageFilter.blur(sigmaX: R/2, sigmaY: R/2))
opacityOpacity (또는 페인트의 색상 알파)
text size/lineHeightTextStyle(fontSize: size, height: lineHeight / size). %의 경우: height: pct/100. Figma의 수직 중앙 정렬과 일치시키려면 leadingDistribution: TextLeadingDistribution.even 설정
weight 600FontWeight.w600
ls px / ls %letterSpacing: px / letterSpacing: size * pct / 100
ellipsis maxLines NmaxLines: N, overflow: TextOverflow.ellipsis
align CENTERtextAlign: TextAlign.center. RTL 화면의 경우 TextAlign.start 및 EdgeInsetsDirectional 사용
assets/x.svgSvgPicture.asset('assets/…/x.svg', width:, height:) (flutter_svg)
image fill FILL / FITImage.asset(fit: BoxFit.cover / BoxFit.contain)

웹 (CSS / Tailwind / React) 매핑

FigmaCSS
column / rowdisplay:flex; flex-direction: column / row; gap: Gpx
wrap, rowGap Rflex-wrap: wrap; column-gap: G; row-gap: R
pad t,r,b,lpadding: t r b l (CSS와 동일한 순서)
main MIN / CENTER / MAX / SPACE_BETWEENjustify-content: flex-start / center / flex-end / space-between
cross MIN / CENTER / MAX / BASELINEalign-items: flex-start / center / flex-end / baseline
HUGwidth/height: fit-content (또는 그냥 auto)
FILL (main axis)flex: 1 1 0; min-width: 0
FILL (cross axis)align-self: stretch
FIXEDwidth: Wpx; height: Hpx; flex-shrink: 0
@x,y / absolute부모 position: relative; 자식 position:absolute; left:x; top:y (오른쪽/아래쪽에 pin 사용)
colors #RRGGBBAA그대로 유효한 CSS
gradient-linear Ndeglinear-gradient(Ndeg, c1 p1%, c2 p2%)
drop-shadowbox-shadow: x y B S color (inner-shadow는 inset 추가)
layer-blur / background-blur Rfilter: blur(R/2 px) / backdrop-filter: blur(R/2 px)
textfont: weight size/lineHeight family; letter-spacing (ls % → pct/100 em)
ellipsis maxLines Ndisplay:-webkit-box; -webkit-line-clamp:N; -webkit-box-orient:vertical; overflow:hidden
assets/x.svg<img src> 또는 인라인 SVG; 내보낸 크기 유지

spec.json에는 대부분의 레이어에 대해 Figma가 자체 생성한 CSS(css)도 있습니다. CSS 값을 교차 확인하는 데 사용하세요. 토큰 및 레이아웃 구조는 여전히 위의 트리에서 가져옵니다.

규칙

  • 스펙 값은 이미지에서 보는 것보다 우선합니다. 충돌이 있을 경우 스펙을 신뢰하고 충돌을 언급하세요.
  • 사용자가 토큰으로 정규화를 요청하지 않는 한 값을 반올림하거나 "개선"하지 마세요(14는 14로 유지, 16이 아님).
  • 숨겨진 레이어는 내보내지지 않습니다. 내보내기에 없는 상태(호버, 오류, 빈 상태)를 만들지 마세요. 해당 프레임을 요청하세요.
  • 누락된 글꼴을 표시하세요. 글꼴 패밀리가 프로젝트에 번들되지 않은 경우, 그렇게 말하고 사용자에게 추가할 사항(pubspec fonts / @font-face)을 알려주세요.
  • 여러 중단점이나 상태의 경우 각 프레임을 내보내도록 사용자에게 요청하세요. 각각이 자체 폴더가 됩니다.

관련 스킬