04 TRACE무엇을 남겼는가

레이어 순서가 조용히 뒤집혀 있었다

에러도 경고도 없이 선언 몇 개가 적용을 멈췄다. 원인은 CSS 레이어 순서가 소스가 아니라 최초 등장 순서로 정해진다는 것.

히어로의 여백이 이상했다. 마진을 줬는데 붙어 있었다.

getComputedStyle(h1).marginBottom  // "0px"

클래스는 붙어 있는데 매칭되는 규칙이 없었다.

원인

쉽게 말하면 이렇다. 레이어는 줄을 서는 순서이고, 뒤에 선 쪽이 이긴다. 그런데 줄 세우는 기준이 내가 적은 명단이 아니라 먼저 도착한 순서였다.

내가 적은 명단 (layers.css)resettokensbasecomponents← 이 쪽이 이겨야 함번들러가 실제로 내보낸 순서componentsresettokensbasecomponents 가 앞줄로 밀려서, 뒤에 선 reset 의전역 margin 0 규칙이 컴포넌트 스타일을 이겼다.

CSS 레이어 순서는 @layer a, b, c; 선언문이 아니라 최초 등장 순서로 정해진다. 번들러가 @layer components { … } 블록을 styles/layers.css보다 먼저 내보내면, 선언한 순서는 폐기되고 등장 순서로 다시 만들어진다.

그 결과 componentsreset보다 아래로 갔다. * { margin: 0 }이 컴포넌트 스타일을 이기고 있었다.

Phase 0 페이지도 같은 상태였다. 스크린샷을 보고 승인까지 했는데 간격이 이미 죽어 있었다.

바꾼 것

CSS Module을 레이어 밖으로 뺐다. 이미 스코프돼 있고, unlayered 규칙은 모든 layered 규칙보다 우선한다. 번들러가 무슨 순서로 내보내든 상관없어진다.

그리고 pnpm check:layers를 추가했다. 실제 빌드된 CSS에서 첫 @layer 토큰이 순서 선언문인지, 순서가 소스와 같은지, 모듈이 다시 레이어로 들어가지 않았는지 확인한다.

침묵하는 실패는 가드가 없으면 영원히 침묵한다.

cssdebugging

기록

2026.08.24에 처음 커밋됐고, 이후 2번 더 손댔습니다.

  • 9cf738eContent: 어려운 대목 넷을 그림으로 바꿨다
  • 029cadaTopics: a controlled axis to browse by, and a filter that costs no JavaScript
  • 8987da1Phase 2: Personal System

전체 빌드 기록 →