AI에게 '이 느낌으로 만들어줘'가 통하게 — 스타일 라이브러리를 열었습니다

Outcome 레퍼런스를 AI가 그대로 알아듣는 형태로 번역해 쌓는 스타일 라이브러리를 공개했다. 토큰층(shadcn CSS 변수)+프로즈층(DESIGN.md) 이중 서빙, 측정+비전 2단 추출 파이프라인, 수집함을 붙여 라이브러리 10종을 라이브로 올렸고 외부 에이전트 소비 검증을 통과했다.

AI에게 화면을 만들어 달라고 하면, 늘 같은 벽에 부딪힙니다. "좀 더 세련되게"라고 하면 AI는 자기가 아는 평균적인 세련됨을 내놓죠. 내가 원한 건 그게 아니라 저 사이트의 그 느낌인데요.

그래서 만들었습니다 — styles, 마음에 든 레퍼런스를 AI가 그대로 알아듣는 형태로 번역해 쌓아두는 개인 스타일 라이브러리입니다. 현재 10종이 올라가 있습니다.

스타일 하나 = 기계용 절반 + AI용 절반

처음 조사에서 배운 게 하나 있습니다. 포맷을 새로 발명하면 안 된다는 것. AI 도구들이 스타일을 소비하는 방식은 이미 두 갈래로 수렴해 있었습니다 — 색·폰트·radius 같은 값은 shadcn/ui 방언의 CSS 변수로, "왜 그렇게 생겼는가"는 DESIGN.md라는 마크다운 문서로.

그래서 스타일 하나는 두 층으로 서빙됩니다:

  • 토큰층: npx shadcn add https://web.ggum.uk/styles/r/neo-brutalist.json 한 줄이면 프로젝트에 테마가 설치됩니다.
  • 프로즈층: 에이전트에게 design.md 주소를 읽히면 do/don't까지 알아듣습니다. "그림자로 구획 나누기 금지 — 헤어라인을 써라" 같은 것들요.

정말 통하는지 실험했습니다. 아무 내부 자료도 접근 못 하는 에이전트에게 공개 URL 두 개만 주고 랜딩을 만들게 했더니, 제로블러 하드 섀도우와 스티커 배지까지 그 스타일의 문법이 그대로 재현됐습니다. 이 검증이 통과된 날이 이 사이트가 "갤러리"에서 "도구"가 된 날이라고 생각합니다.

레퍼런스 → 스타일, 반자동 파이프라인

라이브러리의 절반은 손으로 만들었지만, 나머지 절반은 파이프라인이 만들었습니다. Linear, Stripe, Vercel, Notion 스타일이 그 산출물입니다.

구조는 2단입니다. 1단은 측정: 헤드리스 브라우저로 실제 렌더된 페이지의 계산값(computed style)을 전수 조사합니다. CSS 파일을 파싱하는 게 아니라, 화면에 실제로 칠해진 색을 면적 가중치로 셉니다. 2단은 해석: 스크린샷을 AI가 직접 보면서 "측정된 색 중 무엇이 primary인가"를 정합니다.

왜 나눴냐면, 각자 잘하는 게 다르기 때문입니다. 값은 측정이 정확하고, 역할은 눈이 정확합니다. 실제로 Linear의 브랜드 인디고는 사용 빈도로는 한참 아래였습니다. 빈도 1위를 기계적으로 primary로 뽑았다면 회색 스타일이 나왔을 겁니다. 브랜드 색은 원래 아껴 쓰는 색이라서요.

한 가지 원칙을 지켰습니다: 해석층은 측정값 밖의 색을 지어낼 수 없습니다. AI가 "Stripe면 대충 이런 보라색이겠지"라고 기억으로 hex를 만드는 걸 막고, 반드시 측정된 팔레트 안에서 고르게 했습니다. 정확한 척하지 않기 위해, 각 필드에는 측정값인지 추론값인지 출처 표기가 남습니다. 스크린샷만으로 만든 스타일에는 갤러리에 approximate 배지가 붙고요.

수집함 — 스타일이 되기 전의 레퍼런스

마음에 든 걸 발견할 때마다 바로 스타일로 만들 수는 없으니, 그 앞 단계로 컬렉션을 붙였습니다. URL을 던지면 스크린샷이 썸네일이 되고, 이미지·PDF도 카드로 쌓입니다. 나중에 스타일로 승격되면 카드에 연결 칩이 붙습니다. 수집함이 파이프라인의 입구인 셈입니다.

만들다 배운 것 셋

HTML 파서는 관대하지 않게 관대합니다. 카드 전체를 링크로 만들고 그 안에 작은 링크 칩을 넣었더니, 렌더된 DOM에서 카드가 여섯 개로 불어나 있었습니다. <a> 안의 <a>는 에러가 나는 게 아니라 조용히 쪼개집니다. 겉보기는 멀쩡해서, DOM을 세어보기 전까지 몰랐습니다.

hidden 속성은 여러분의 CSS보다 약합니다. 필터 기능이 분명 속성은 바꾸는데 화면이 안 바뀌었습니다. .card { display: flex }가 브라우저 기본 [hidden] { display: none }보다 우선순위가 높아서요. 한 줄(.card[hidden] { display: none })이면 고쳐지지만, "속성이 바뀌었다"와 "화면이 바뀌었다"를 따로 검증하지 않았다면 놓쳤을 겁니다.

미완성물은 완성물과 다른 폴더에 두세요. 추출 파이프라인의 중간 산출물을 스타일 폴더에 바로 두면, 반쯤 채워진 초안이 빌드를 깨뜨립니다. 스테이징 폴더를 빌드가 쳐다보지 않는 곳에 분리하고, 스키마 검증을 통과한 것만 승격시키는 구조로 바꿨습니다. 게이트는 이번에도 밥값을 했습니다 — 검증을 일부러 깨뜨린 스타일을 넣어보니 빌드 전체가 멈추더군요. 멈춰야 게이트입니다.

써보기

  • 갤러리: web.ggum.uk/styles — 동일한 샘플 UI가 스타일마다 다르게 렌더된 걸 보는 재미가 있습니다
  • 설치: npx shadcn@latest add https://web.ggum.uk/styles/r/<스타일이름>.json
  • 에이전트용 입구: llms.txt

다음은 Figma 입력과, 폰에서 URL만 던지면 수집함에 쌓이는 흐름입니다. 그때 또 적겠습니다.

Learned 값은 측정이 정확하고 역할은 눈이 정확하다 — 추출을 측정층과 해석층으로 나누되, 해석층은 측정값 밖의 색을 지어낼 수 없게 가둬야 정확한 척하지 않는다. 속성이 바뀐 것과 화면이 바뀐 것은 따로 검증해야 하고, 미완성 산출물은 빌드가 쳐다보지 않는 폴더에 격리해야 게이트가 밥값을 한다.
tags  #design-tokens #shadcn #ai-tooling #playwright #astro #static-site #기고