자식 요소에서 CSS 변수를 재정의해도 부모로부터 상속된 복합 사용자 지정 속성은 다시 구성되지 않습니다. 부모가 이미 중첩 참조를 계산했기 때문입니다. 자식 요소의 값을 사용하려면 해당 자식 요소에 복합 속성을 선언하거나 그곳에서 최종 속성을 구성하세요.

이 가이드는 이러한 동작을 설명하고 Mac의 Chrome에서 재현할 수 있는 데스크톱 실습을 제공합니다. 공유 기본값을 유지하면서 구성 요소의 테두리가 로컬 색상 재정의를 따르게 하는 것이 목표입니다.

상속된 복합 속성은 왜 부모의 색상을 유지하나요?

다음 CSS 조각을 살펴보세요. .sample-board 요소 안에 .sample-tile 요소가 있다고 가정합니다.

.sample-board {
  --sample-color: teal;
  --sample-border: 6px solid var(--sample-color);
}

.sample-tile {
  --sample-color: darkorange;
  border: var(--sample-border);
}

자식 요소의 테두리는 계속 청록색입니다. CSS Variables 명세에 따르면 사용자 지정 속성은 상속 전에 계산값 시점에 중첩된 var() 함수를 해석합니다. 따라서 자식 요소는 6px solid teal에 해당하는 복합 값을 상속하며, 자식 요소의 --sample-color를 대입할 표현식을 상속하는 것이 아닙니다. CSS Custom Properties for Cascading Variables Module Level 1.

여기에는 서로 별개의 속성이 두 개 있습니다. 자식 요소에서 --sample-color를 설정하면 해당 속성의 로컬 값이 바뀝니다. 그렇다고 --sample-border의 새 값도 선언되는 것은 아닙니다. MDN의 사용자 지정 속성 참고 자료에서는 일반적인 비등록 사용자 지정 속성이 상속되며, 계산값에 변수가 대입된다고 설명합니다. 사용자 지정 속성: CSS 변수.

이 예제의 순서는 다음과 같습니다.

  1. 부모 요소는 --sample-color를 teal으로 계산합니다.
  2. 부모 요소의 --sample-border는 해당 색상을 대입해 6px solid teal을 만듭니다.
  3. 자식 요소는 이 복합 값을 상속하고, 별도로 자체 색상 토큰을 darkorange로 설정합니다.
  4. 자식 요소의 border 선언은 상속된 청록색 복합 값을 사용합니다.

이것은 부모 값의 영구적인 스냅샷이 아닙니다. 부모의 색상이 바뀌면 부모의 복합 값도 갱신되어 다시 상속될 수 있습니다. 경계는 중첩 참조가 해석되는 요소입니다. 하위 요소의 재정의는 그 계산에 거슬러 올라가 영향을 주지 않습니다. CSS Variables: 종속성 해석.

Mac의 Chrome에서 동작 재현하기

기존 페이지 스타일의 간섭을 피하려면 빈 페이지를 사용하세요. Chrome에서 새 탭을 열고 주소 표시줄에 about:blank를 입력한 다음 Command + Option + J을 눌러 Console을 여세요. Google의 DevTools 가이드에 이 Mac 단축키가 나와 있습니다. Chrome DevTools 열기.

다음은 레이블이 있는 추상 도형 세 개와 해당 스타일시트를 만드는 완전한 브라우저 콘솔 프로그램입니다. 라이브러리, 이미지, 네트워크 요청 또는 기존 페이지 요소가 필요하지 않습니다. 빈 페이지에서 전체 블록을 한 번 실행하세요. 아래의 예상 결과는 문서화된 CSS 규칙에 근거하며, 실제 브라우저 측정 결과로 제시하는 것은 아닙니다.

(() => {
  const board = document.createElement("section");
  board.className = "sample-board";

  const style = document.createElement("style");
  style.textContent = `
    .sample-board {
      --sample-color: teal;
      --sample-border: 6px solid var(--sample-color);
      display: flex;
      flex-wrap: wrap;
      gap: 24px;
      padding: 32px;
      font: 16px system-ui;
    }

    .sample-board .sample-tile {
      --sample-color: darkorange;
      display: grid;
      place-items: center;
      width: 120px;
      height: 80px;
      border: var(--sample-border);
      border-radius: 24px;
      background: white;
      color: black;
    }

    .sample-board .sample-local {
      --sample-border: 6px solid var(--sample-color);
    }

    .sample-board .sample-direct {
      border: 6px solid var(--sample-color);
    }
  `;

  const samples = [
    ["Inherited", "sample-inherited"],
    ["Local", "sample-local"],
    ["Direct", "sample-direct"]
  ];

  const tiles = samples.map(([label, className]) => {
    const tile = document.createElement("div");
    tile.className = `sample-tile ${className}`;
    tile.textContent = label;
    board.append(tile);
    return tile;
  });

  document.head.append(style);
  document.body.append(board);

  function report(stage) {
    console.log(stage);
    console.table(tiles.map((tile) => {
      const computed = getComputedStyle(tile);
      return {
        sample: tile.textContent,
        localColor: computed
          .getPropertyValue("--sample-color").trim(),
        composite: computed
          .getPropertyValue("--sample-border").trim(),
        paintedBorder: computed.borderTopColor
      };
    }));
  }

  report("Initial values");
  board.style.setProperty("--sample-color", "rebeccapurple");
  report("After changing the parent color");
})();

프로그램은 표 두 개를 출력합니다. 예상되는 테두리 색상은 다음과 같습니다.

샘플 처음 부모 변경 후 이유
Inherited 청록색 보라색 부모의 계산된 복합 값을 사용함
Local 주황색 주황색 자식 요소에 복합 값을 선언함
Direct 주황색 주황색 border에서 로컬 색상을 직접 대입함

세 자식 요소 모두 로컬 색상 토큰으로 darkorange를 유지합니다. 유용한 진단 사례는 Direct 샘플입니다. 이 샘플의 상속된 복합 값에는 여전히 부모의 색상이 들어 있지만, 테두리 선언이 복합 값을 사용하지 않기 때문에 실제로 그려지는 테두리에는 로컬 토큰이 적용됩니다.

getComputedStyle()는 활성 스타일시트가 적용된 뒤 해석된 속성 값을 읽습니다. 따라서 작성된 표현식만 보여 주는 대신 요소가 실제로 받는 값을 확인할 수 있습니다. 색상은 원래 색상 이름 대신 rgb() 값으로 직렬화되어 표시될 수 있습니다. Window: getComputedStyle() 메서드.

실습을 초기화하려면 빈 탭을 새로고침하세요. 실제 페이지에서는 선택한 CSS 수정 사항을 스타일시트에 넣으세요. 콘솔 변경은 진단에 유용하지만 소스 파일을 수정하지는 않습니다.

어떤 수정 방법을 선택해야 하나요?

구성 요소 경계에 복합 값을 선언하기

Local 샘플은 재사용 가능한 테두리 토큰을 유지하면서, 재정의를 제공하는 요소에 그 조합 규칙을 선언합니다. 프로그램에서 관련된 CSS 조각은 다음과 같습니다.

.sample-board .sample-local {
  --sample-color: darkorange;
  --sample-border: 6px solid var(--sample-color);
  border: var(--sample-border);
}

여러 선언에서 같은 조합 값을 사용해야 할 때 이 패턴을 선택하세요. 기본 원시 값을 상위 요소에 두고, 사용자 지정 값을 소유하는 구성 요소에 복합 선언을 둡니다. 사용자 지정 속성은 특정 요소에 선언하고 하위 요소에 상속시킬 수 있으므로 반드시 :root에 둘 필요는 없습니다. CSS 사용자 지정 속성 사용하기.

이 배치는 더 아래쪽 트리에서도 중요합니다. 손자 요소에서 원시 값을 다시 재정의해도 이 구성 요소에서 상속된 복합 값은 여전히 이 구성 요소의 계산 결과를 반영합니다. 다른 결과를 구성해야 하는 하위 요소의 경계마다 규칙을 다시 선언하세요.

사용되는 위치에서 최종 속성 구성하기

Direct 샘플은 테두리 선언에서 중간 토큰을 제거합니다. 핵심 규칙은 다음 CSS 조각입니다.

.sample-board .sample-direct {
  --sample-color: darkorange;
  border: 6px solid var(--sample-color);
}

조합이 필요한 선언이 하나뿐일 때 이 방법을 선택하세요. 너비, 스타일, 색상 토큰은 각각 공유하면서 구성 요소의 border 선언에서 조합할 수 있습니다. MDN은 var()가 다른 속성 값의 전부 또는 일부를 대체할 수 있다고 설명합니다. CSS 사용자 지정 속성 사용하기.

완성된 복합 값이 공개 제어 항목이라면 전체 값을 재정의하기

때로는 구성 요소에서 사용자 지정하려는 대상이 테두리 전체일 수 있습니다. 이 경우 이전 입력값만 바꾸는 대신 6px solid darkorange와 같이 --sample-border를 로컬에서 대체하세요.

구성 요소의 사용 규약을 정할 때 다음 기준을 참고하세요.

의도한 사용자 지정 적합한 패턴
하나의 로컬 색상이 하나의 테두리를 제어함 border에서 직접 구성
여러 선언이 하나의 조합 값을 공유함 구성 요소에 복합 값 선언
호출자가 테두리 전체를 제공함 복합 값 자체를 재정의
하위 요소가 부모의 조합 값을 그대로 사용해야 함 복합 값 상속 유지

구체성, 대체값 또는 상속 키워드로는 왜 값이 다시 구성되지 않나요?

먼저 원시 값 재정의가 실제로 적용되었는지 확인하세요. 자식 요소의 해석된 --sample-color가 이미 darkorange라면, 같은 선언의 구체성을 높여도 별도로 상속된 --sample-border는 바뀌지 않습니다. 두 속성 모두 이미 값을 가지고 있으며, 문제는 복합 값을 구성한 위치입니다.

마찬가지로 --sample-border: inherit은 부모의 계산된 값을 가져옵니다. 일반적인 상속은 계산된 값을 전달하며, 부모가 작성한 선언을 자식 요소의 맥락에서 다시 실행하지 않습니다. 상속.

var(--sample-border, 6px solid darkorange) 같은 대체값도 유효한 청록색 테두리를 그대로 둡니다. var() 대체값은 사용할 수 있는 값이 없는 사용자 지정 속성을 처리합니다. 하위 요소가 선호하는 입력값과 그 값을 비교하지는 않습니다. var() CSS 함수.

유효하지 않은 값은 이 상속 문제와 구분하세요. 예를 들어 등록되지 않은 토큰에 12px가 들어 있을 수 있지만, 이를 border-color에 대입하면 유효하지 않은 결과가 될 수 있습니다. 해당 var() 호출 안의 대체값은 일반적인 형식 검사 대안이 아닙니다. 사용자 지정 속성 대입으로 인해 선언이 계산값 시점에 유효하지 않게 될 수 있는 이유는 MDN 설명을 참고하세요. CSS 사용자 지정 속성 사용하기.

@property로 변수를 등록하면 해결되나요?

@property를 사용하면 사용자 지정 속성의 구문, 초기값, 상속 동작을 지정할 수 있습니다. 하지만 부모에서 상속된 복합 값이 자식 요소의 재정의된 입력값을 기준으로 다시 계산되지는 않습니다. 원시 값을 inherits: false로 등록하면 해당 원시 값을 가져오는 방식이 바뀌고, 복합 값을 그렇게 등록하면 일반적인 상속이 중단됩니다. 어느 방법도 누락된 로컬 규칙을 제공하지는 않습니다. @property CSS at-rule.

등록 여부에 따라 지원 범위도 다릅니다. MDN에 따르면 일반 사용자 지정 속성은 2017년 4월부터 브라우저 전반에서 널리 지원된 반면, @property는 2024년 7월에 Baseline 지원 상태가 되었으며 더 오래된 브라우저에서는 사용할 수 없을 수 있습니다. 위에서 설명한 두 가지 수정 방법에는 등록이 필요하지 않습니다. 사용자 지정 속성: CSS 변수, @property CSS at-rule.

스타일시트에서 같은 문제를 진단하는 방법

실습에서 사용한 세 열 비교를 모양이 잘못된 요소에 적용하세요.

  1. --sample-color 같은 원시 토큰을 확인합니다.
  2. --sample-border 같은 복합 토큰을 확인합니다.
  3. border-top-color 같은 렌더링 속성을 확인합니다.
  4. 복합 선언이 적용되는 요소를 찾습니다.
  5. 해당 선언을 의도한 사용자 지정 경계로 옮기거나 그곳에 반복해서 선언하거나, 최종 속성에서 직접 조합합니다.

Chrome에서 관련 규칙을 찾으려면 도형을 마우스 오른쪽 버튼으로 클릭하고 Inspect를 선택하세요. DevTools는 Elements에서 요소를 선택하고 Styles 탭에 적용된 CSS 규칙을 표시합니다. Chrome DevTools 열기.

원시 토큰에는 원하는 로컬 값이 표시되지만 복합 토큰에는 상위 요소의 값이 계속 들어 있다면, 먼저 복합 선언이 있는 위치를 살펴보세요. 입력값을 읽어야 하는 위치에 규칙을 정의한 뒤 해석된 토큰과 실제로 그려진 결과를 모두 확인하세요. 이렇게 하면 구성 요소에 명시적이고 예측 가능한 사용자 지정 경계가 생깁니다.

출처