design-refs — 작업 규칙 (rules)
상태·이력은
handoff.md, 규칙은 이 파일. 새 규칙이 생기면 그 답변에서 바로 🆕 신규에 1줄 append하고, 안정화되면 확립 규칙의 해당 §로 원문 이관한다.
📑 인덱스
- 🆕 신규 (최근 확립)
- §1 소스 수급 — 무엇을 받고 무엇을 안 받나
- §2 소스 추가 4단계
- §3 게시(publish)와 서빙 함정
- §4 프로젝트에서 가져다 쓸 때
🆕 신규
- 🔴🔑2026-09-19 [코디] 캔버스가 «비어 보이면» 좌표에 NaN 이 섞였는지부터 본다 —
fillRect(NaN,…)은 예외도 경고도 없이 아무것도 안 그린다. 샘플덱 파편화가 흩어진 구간만 통째로 빈 화면이었는데, 범인은updaters래퍼가run(p)로 인자를 하나만 넘겨 바람 계산의time이undefined→Math.sin(undefined)=NaN이었다. 집적 상태는 파편 루프를 건너뛰어 «멀쩡해» 보여서 증상이 반쪽으로만 나타났다. ⛒판정은 스크린샷이 아니라getImageData잉크 픽셀 수 — NaN 이면 0 으로 딱 떨어진다(부분 실패면 줄어들 뿐이다). 실물 =C:\dev\design-refs\scroll-motion-2026-09\sample-deck.htmlshatterInit. - 🔴2026-09-19 [코디] 한 파일 안에서 «연출용 클래스»와 «UI 용 클래스»는 이름을 겹치면 안 된다 — 조용히 높이를 물려받는다. 구간 편집 패널의 칩줄에
.chips를 썼더니 효과4(전후 와이프)의.chips{position:relative;height:210px}가 그대로 먹어 한 줄이 210px 로 부풀었다(#panel .chips는 height 를 «선언하지 않아» 특이도로도 못 이긴다). →.stops로 개명. 같은 뿌리 = [[bootstrap-class-collision-in-svg]]. - ⚠2026-09-19 [코디] 「유리판 밖으로 잘렸나」를
inner.scrollHeight > stick.clientHeight로 재면 오탐이다 — CSS 는 변형(transform)된 자식의 영역까지 스크롤 오버플로에 넣는다. 방이 Z로 밀린 만큼.rooms가 440 → 936px 로 잡혀 「잘림 2건」이 나왔지만 실제로는 0건이었다. ⛒진짜 판정 = 보이는 요소의getBoundingClientRect()가.stick사각형 밖으로 나가는가(opacity>0.05인 것만). - ⚠2026-09-19 [코디] JS 가 인라인
transform을 매 프레임 덮어쓰는 요소에는 CSS transform 을 걸지 마라 — 조용히 무시된다. 컨셉06 속도반응이--skew를 «쓰기만» 하고 CSS 가 안 읽어 여태 아무 일도 안 일어났는데(var(--skew)0건), 고치면서.scard/.layer/.room에도 걸었다가 같은 함정을 밟을 뻔했다. 소비처는 JS 가 안 건드리는.glass하나로 한정한다. - ⚠2026-09-19 [코디] Playwright MCP 는 브라우저 «한 대»를 공유한다 — 브라우저 쓰는 서브에이전트와 동시에 쓰면 그쪽이 멈춘다. web-designer 에이전트가 8분+ 무활동으로 죽었고(생존감시가 잡았다), 원인은 내가 같은 시각에 다른 페이지를 열어 페이지를 뺏은 것이다. ⛒브라우저 에이전트를 띄웠으면 그게 끝날 때까지 내가 Playwright 를 안 잡는다.
- ⚠2026-09-19 [코디] 패널·목록처럼 «저장할 때마다 다시 그리는» UI 는 검증 스크립트가 노드를 캐시하면 안 된다. 구간 편집 검증에서
row를 캡처해 두고 나중에.click()했더니 그 노드는 이미 detach 돼 이벤트가 문서까지 못 올라갔고 「기본값이 안 먹는다」로 오판했다. 다시 조회하니 정상. ⛒매 단계 새로querySelector한다. - ⚠2026-09-18 [코디] 캡처는 «성공(OK)» 이어도 실패일 수 있다 — 프리로더를 찍는다.
capture_refs.py기본 대기(2.5초)로는 무거운 사이트가 로딩화면인 채로 찍힌다(zajno = 8.8KB · 평균밝기 251.8 · 편차 7.5 = 사실상 흰 화면). 🔑판정은 파일 크기가 아니라 평균밝기 + 표준편차다(PIL.ImageStat— 편차 10 미만이면 단색 화면). 재캡처는networkidle뒤 9초 + 스크롤 + 4초면 대개 잡힌다(재캡처 후 밝기 17.4 · 편차 37.4). - 🔴2026-09-18 [코디] 외부 사이트 기술스택은 «실브라우저»로만 확인된다 — WebSearch·WebFetch 는 JS 를 안 돌려 script 태그가 통째로 사라진다. 서브에이전트 조사 14곳 중 절반이 「미확인」으로 돌아왔는데, 실브라우저 프로브로는 14곳 전부 잡혔다. 볼 것 5가지 = ①
window전역(__THREE__·gsap·Lenis·Matter) ②performance.getEntriesByType('resource')의 실제 파일명 ③canvas 컨텍스트(webgl2/2d) ④body.overflow+scrollHeight(휠을 가로채는가 — lusion 900px 고정 vs oryzo 56,691px 네이티브) ⑤[class*=word]·[class*=char]span 수(어절 분할인지 글자 분할인지). ⚠URL 부분일치 판정은 오탐이다 —googletagmanager안의ogl이 OGL 로 잡힌다. 전역 객체와 파일명만 근거로 쓴다. - 🔴2026-09-18 [코디] 프론트 라이브러리는 「당연히 MIT」가 아니다 — 받기 전에 라이선스를 본다. 실측 = ScrollReveal 4.0.9 = GPL-3.0(상업 별도 유료) · Animate.css 4.1.1 = Hippocratic-2.1(구버전 3.x 만 MIT) · Hover.css 2.2.0+ 상업 유료. 셋 다 「무료 CSS 애니 모음」으로 널리 소개되는 것들이다. 판정은 npm
package.json의license필드가 1차 근거(curl https://cdn.jsdelivr.net/npm/<pkg>@latest/package.json) — 배포본에 LICENSE 파일이 없는 경우도 있다(OGL). 그땐LICENSE.NOTE.txt로 근거를 남긴다. - 🔑2026-09-18 [코디] devplan 페이지에 «다크 연출»을 보여줄 땐 색을 페이지 밖으로 뺀다.
check_theme.py는 파일 전체의 색 선언을 훑어 차가운 어두운 배경 3개면 FAIL 이다 → 다크 데모를 인라인<style>·srcdoc으로 넣으면 그 자리에서 죽는다. 해법 = 데모를C:\dev\design-refs\...아래 독립 HTML 로 두고<iframe src>로 띄운다(게이트의 VENDOR 예외가/design-refs/를 이미 제외한다). 데이터도 페이지가 아니라 JSON 에 둔다 —refs.json의 다크 토큰이 통과하는 이유가 그거다. - 🔑2026-09-18 [코디] 한글 파편화(캔버스 입자) 는 «음절 분할 금지» 규칙의 유일한 예외다. 글자를 쪼개는 게 아니라 그려진 픽셀을 쪼개기 때문이다(
getImageData알파>128 격자 샘플). 단 함정 3개 = ①입자 오프셋은 한 번만 뽑아 고정(매 프레임 랜덤이면 지글거리고 되감기가 깨진다) ②measureText로 폭을 맞춰야 한다(한글은 글자폭이 라틴의 2배라 그냥W/11로 잡으면 화면 밖으로 잘린다) ③입자만으론 도트가 성겨 흐려 보인다 → 거의 모였을 때 진짜 글자를 알파로 겹쳐 그린다. 구현 =C:\dev\design-refs\scroll-motion-2026-09\demo.htmlshatter(). - 🔑2026-09-18 [페페] 한 출처에서 색이 여러 벌 나오면
variants[]로 넣는다 — 카드가 «기본 3초»마다 페이드하며 순환한다. 각 항목 =name+ 덮어쓸 색 키만(bg·ink·accent…tokens에 있는 키만 허용,--check가 오타를 잡는다). 2개 미만이면 FAIL. 주기·정지는 상단 고정헤더 조종판(슬라이더 1~10초 + 일시정지,localStorage에 남는다). 🔑페이드는 CSS 변수가 아니라 «그 변수를 쓰는 속성»에 transition 을 건다 —background-color·color·border-color에 걸면 변수를 바꿔도 부드럽게 넘어간다(변수 자체를 애니메이션하려면@property등록이 필요한데 그럴 이유가 없다). 순환은 탭 숨김·상세화면에선 멈추고,prefers-reduced-motion이면 끄는 게 아니라 «멈춘 채» 시작한다(페페가 켜면 된다). 상세페이지엔 변형 전량이 스와치 표로 한 번에 깔린다. - ⚠2026-09-18 [코디] 허브 쉘 네비바에 조종판을 붙일 땐 ①색을
var(--on-accent)로 쓰고 ②우측 여백을 비운다.#fff를 직접 쓰면check_theme.py가 「밝은 글자색」으로 FAIL(테라코타 네비 위 흰 글자가 맞는데도 — 토큰이--on-accent다). 우상단은 qahelper 토글이 덮으므로padding-right:96px(모바일 48px). 390px 에서 넘치면 버튼 라벨을<span class="cyc-t">로 감싸 숨기고 아이콘만 남긴다 — 실측으로 401→390 이 됐다. - 🔑2026-09-18 [코디] 카드 설명은
--cc-muted가 아니라--cc-ink로 그린다. 다크 에디터 테마의 muted(예 Tokyo Night#565f89, Dracula#6272a4)를 13px 본문에 쓰면 대비가 3:1 언저리로 떨어져 안 읽힌다 — 페페 규칙(연회색 보조 금지·본문 진하게)과도 충돌. ink 는 그 테마의 «본문색»이라 컨셉 재현도 안 깨진다. 위계는 크기·굵기로 낸다. - 🔑2026-09-18 [코디] 레퍼런스 콜렉션(외부 사이트)과 오프라인 자산(내려받은 프레임워크)은 페이지가 다르다 — 상위
/design-refs.html= 컨셉 카드 콜렉션(refs.json구동), 하위/design-refs-assets.html= 부트스트랩·아이콘 카탈로그(catalog.json구동). 추가 =C:\dev\design-refs\refs.json의refs[]1항목 +python C:/dev/design-refs/capture_refs.py <id>→ 페이지는 안 고친다. 새 항목 넣고capture_refs.py --check를 돌린다(필수키·토큰 17키·폰트프리셋·분류값 검증 — 안 돌리면 카드가 조용히 깨진다). 카드는tokens를 카드 루트 CSS 변수로 받아 그 사이트 컨셉처럼 보인다(손CSS 0건) — 사조를 가르는 건blur·backdrop이다(둘이 0/none 이면 글래스모피즘이 미니멀과 똑같아진다). 폰트는fonts프리셋 6종(전부 윈도우 기본 탑재 조합)만 쓴다 — 안 그러면 카드 전부 devplan 기본폰트로 보인다. - 🔴2026-09-18 [코디] 8799 는 이제
app.pyMOUNTS 로 마스터C:\dev\design-refs\를 «직접» 서빙한다 — 새 파일은 마스터에 둔다. 실측 = 게시본(C:\dev\devplan\design-refs\)에만 넣은refs.json은 404, 마스터로 옮기니 200. 26.8.3 에 「정션 못 넘음 → 게시(복사)」로 적어둔 함정은 그 뒤 서버가 재시작되면서 마운트 쪽이 우선 적용된 상태다(옛 절은 그대로 두되 이 줄이 현행). 게시본도publish_devplan.pyPUBLISH에refs.json·shots를 넣어 동기시킨다(어느 쪽이 뜨든 같게). - ⚠2026-09-18 [코디] 외부 사이트 iframe 임베드는 기본 금지 —
X-Frame-Options/CSP 차단이 예외가 아니라 빈 흰 박스로 조용히 실패하고, 그 실패를 JS 로 감지할 방법이 없다(코덱스 확인). 캡처(shots/<id>-<n>.jpg)가 기본이고,embed:"try"를 명시한 항목만 상세에 iframe 절이 붙는다(안 보일 수 있다는 안내문 동반). 캡처는 로컬 열람 전용 + 원출처 링크 상시 노출 전제다 — devplan 을 밖으로 열 땐 이 전제를 다시 본다. - 2026-08-03 [페페] 디자인 레퍼런스는
C:\dev\design-refs\최상위에 모은다 — devplan 하위·tools 하위 아님. "dev 전체가 참고하는 자산"이라는 성격 때문이고, 소스가 늘어도 같은 틀(폴더 + 카탈로그 1항목)로 받는다. → §1·§2 - 2026-08-03 [코디] devplan 8799 서버는 코디가 재시작 못 하고 정션을 못 넘는다 — 그래서 devplan 밖 자산은 게시(복사)로 붙인다. → §3
§1 소스 수급 — 무엇을 받고 무엇을 안 받나
- 상업적 사용이 자유로운 라이선스만: MIT · Apache-2.0 · CC-BY(표기 조건 확인). 유료 테마·NC(비상업)·평가판은 받지 않는다.
- 각 소스 폴더에 업스트림
LICENSE를 그대로 둔다(나중에 출처를 되짚을 수 있어야 한다). - 받는 단위 = 오프라인으로 완결되는 것. CDN 링크만 적어두는 건 자산이 아니다(사무실 PC·오프라인에서 안 열린다).
- 원본 zip은
_raw\에 남긴다 — 재해제·버전 대조·무결성 확인에 쓴다. - 업스트림 폴더 구조를 바꾸지 않는다. 예제가 상대경로(
../assets/dist/css/...)로 서로를 참조하므로, 정리한다고 옮기면 그 순간 깨진다.
§2 소스 추가 4단계
C:\dev\design-refs\<소스-버전>\에 압축 해제(구조 그대로 + LICENSE). 원본 zip은_raw\.build_catalog.py의SOURCES에 항목 1개 추가 — 이름·버전·라이선스·홈페이지·태그·요약·assets(경로+설명+게시여부). 예제 페이지가 있으면<id>-examples.json리스트로 넣는다(slug·title_ko·category·what·components·best_for·path).PYTHONUTF8=1 python build_catalog.pyPYTHONUTF8=1 python publish_devplan.py
- 파일 수·용량·아이콘 수를 수기로 적지 않는다 —
build_catalog.py가 실측해 채운다(수기면 버전 올릴 때 반드시 어긋난다. devplan의 "건수 드리프트" 규칙과 같은 이유). - 뷰어 페이지(
devplan/design-refs.html)는 고치지 않는다 —catalog.json을 fetch해 그리므로 새 소스가 자동으로 뜬다. 페이지를 고쳐야 한다면 그건 카탈로그 스키마를 바꾼 것이다. - 예제 분류는 파일을 실제로 열어보고 쓴다(서브에이전트에 맡길 때도 "이름만 보고 쓰지 마라"를 명시).
components는 HTML에서 확인한 실제 클래스명만.
§3 게시(publish)와 서빙 함정
- 게시본
C:\dev\devplan\design-refs\는 산출물이다. 직접 편집 금지(다음 publish에 덮어쓴다). 차이 확인 =publish_devplan.py --check. - 게시 대상 = 브라우저가 실제로 요청하는 것만(
dist·examples· 아이콘 폰트/CSS/JSON ·catalog.json). SCSS·소스맵(.map)·아이콘 SVG 2078개는 게시하지 않는다(아이콘은 웹폰트 CSS로 전부 렌더된다). - ⚠devplan 8799 서버(
app.py)는 상승권한 프로세스라 코디가 재시작할 수 없다 — 같은 계정인데taskkill이 "액세스 거부".app.py를 고쳐도 즉시 반영되지 않는다(재시작이 필요한 변경은 페페 몫이거나 다음 부팅까지 대기). - ⚠그 서버는 정션(
mklink /J)을 못 넘어간다 — 실측: 같은 깊이 실디렉터리 200 / 정션 404. 새로 띄운 Python 3.12 http.server는 같은 정션을 200으로 서빙 → 서버 프로세스 권한 문제다. devplan 밖 폴더를 정션으로 붙이는 방법은 쓰지 마라. app.py에는/design-refs/*→C:\dev\design-refs매핑 라우트가 들어 있다(경로 탈출 방어 포함). 서버가 재시작되면 그쪽이 우선인데 상대 경로가 같아 페이지는 어느 쪽이든 그대로 뜬다 — 게시 복사와 라우트는 충돌이 아니라 이중화다.
§4 프로젝트에서 가져다 쓸 때
- 부트스트랩 전체 사용 =
dist\css\bootstrap.min.css+dist\js\bootstrap.bundle.min.js링크. devplan 페이지는 복사하지 말고 게시본 URL을 그대로 쓴다(/design-refs/bootstrap-5.3.8/dist/...) — 사본이 늘면 버전 올릴 때 어긋난다. - 아이콘만 사용 =
bootstrap-icons.min.css+fonts\폴더를 같이 복사(fonts없으면 네모로 보인다). devplan은 게시본 링크. - ~~⚠devplan 페이지 자체에는 부트스트랩 CSS를 링크하지 않는다~~ 2026-08-03 페페 지시로 폐기 — devplan UI 자체가 부트스트랩으로 간다. 테라코타는
devplan/assets/theme.css의--bs-*브리지가 지키고(링크 순서 부트스트랩 → theme.css → 페이지 style),check_theme.py는 벤더 CSS를 판정에서 제외한다. (원래 근거: 부트스트랩 팔레트가 웜톤을 덮어써 게이트 FAIL. 남의 예제 원본을 보여줄 땐 지금도 iframe 격리가 맞다.) - 조각을 베꼈으면 어떤 예제에서 가져왔는지 한 줄 남긴다(다음에 같은 화면을 다시 만들 때 시간을 번다).
- 2026-08-03
examples/dashboard(navbar +offcanvas-md사이드바 +col-md-9 ms-sm-auto col-lg-10본문) → devplan 메인 허브index.html(생성기build_hub.py). 첫 소비처. 사이드바 배경은 바깥 col에 줘야 한다(예제가 두 군데 다 배경 클래스를 준 이유 = md↑에서 offcanvas가background:transparent !important).