디자인 토큰 반응성

배경 및 개요

작성자는 구성요소의 디자인을 상황에 맞게 변경해야 하는 경우가 많습니다. 이전에는 작성자가 이러한 변경사항을 적용하려면 선택기를 사용해야 했습니다. 이는 많은 디자인 토큰이 맞춤 속성으로 존재할 수 있지만, 상위 디자인 토큰은 선택기 패턴 (즉, 클래스 이름 또는 속성 규칙 사용) 또는 JavaScript 프레임워크의 props/컨텍스트로만 인코딩될 수 있음을 의미했습니다.

컨테이너 스타일 쿼리를 사용하면 작성자가 상위 요소의 계산된 맞춤 속성 값을 기반으로 요소의 스타일을 지정할 수 있습니다. 즉, 작성자는 프레젠테이션을 위해 마크업이나 JavaScript에 의존하는 대신 스타일시트 내에 의미 있는 디자인 토큰 값을 작성할 수 있습니다.

구현 방법

컨테이너 스타일 쿼리를 사용하여 반응형 디자인 토큰을 구현하는 방법은 매우 간단합니다.

  1. 상위 디자인 토큰을 컨테이너의 맞춤 속성으로 설정합니다. 등록된 맞춤 속성일 필요는 없습니다.
  2. @container style() 규칙을 사용하여 해당 맞춤 속성의 값을 쿼리합니다.
  3. 컨테이너 내의 하위 요소에 적절한 스타일을 적용합니다.

주의사항:

  • 스타일 쿼리로 쿼리되는 컨테이너에는 container-type 또는 container-name가 설정되어 있지 않아도 되지만, container-name를 사용하면 더 구체적인 쿼리가 가능합니다.
  • 컨테이너 자체는 컨테이너 스타일 쿼리로 스타일을 지정할 수 없습니다.

다음은 위 구현 단계의 기본 예입니다.

<div class="features">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
.features {
  --density: spacious;
}

.bugs {
  --density: compact;
}

@container style(--density: compact) {
  .card {
    padding: 8px;
  }
}

@container style(--density: spacious) {
  .card {
    padding: 24px;
  }
}

대체 전략

컨테이너 스타일 쿼리에 대한 Baseline 지원이 제공되기 전까지는 모든 브라우저에서 사용할 수 있어야 하는 핵심 기능에 컨테이너 스타일 쿼리를 사용하지 않는 것이 좋습니다. 컨테이너 스타일 쿼리의 이점을 해치지 않거나 자체적인 제한사항이 없는 대체 기능을 만드는 것이 간단하지 않기 때문입니다. 예를 들어 UI 밀도 사용자 환경설정이 모든 환경에서 사용할 수 있어야 하는 핵심 기능으로 간주되지 않는 경우 컨테이너 스타일 쿼리를 사용하여 대체 없이 기능을 구현할 수 있습니다.

대신 선택기 사용

핵심 기능의 경우 선택기를 사용하는 대체 접근 방식을 사용해야 합니다. 이 예에서는 data-density 속성을 사용하여 밀도 디자인 토큰을 맞춤 속성이 아닌 마크업으로 인코딩합니다.

<div class="features" data-density="spacious">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs" data-density="compact">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
/* This example uses `:where()` to avoid increasing specificity */
:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

이 대체 접근 방식의 주요 제한사항은 data-density 속성이 설정된 중첩 요소를 지원하지 않는다는 점입니다. 선택기 특이성이 동일하므로 표시 순서에 따라 스타일이 결정됩니다 (즉, [data-density="spacious"]가 항상 [data-density="compact"]보다 우선 적용됨).

점진적 개선으로 스타일 쿼리 사용

권장되지는 않지만 핵심 기능의 점진적 개선으로 스타일 쿼리를 사용하려는 경우 중복을 방지하기 위해 맞춤 속성을 만든 다음 스타일 쿼리를 포함할 수 있습니다. 대체 접근 방식에서 컨테이너 요소를 선택할 때 :where()를 사용하여 특수성이 증가하지 않도록 합니다.

.card {
  --card-padding-compact: 8px;
  --card-padding-spacious: 24px;
}

:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

/* Use style queries as a progressive enhancement: same specificity, so order of appearance is used */

@container style(--density: compact) {
  .card {
    padding: var(--card-padding-compact);
  }
}

@container style(--density: spacious) {
  .card {
    padding: var(--card-padding-spacious);
  }
}

CSS만으로 기능 확인

컨테이너 스타일 쿼리를 사용하는 UI를 조건부로 표시하기 위해 컨테이너 스타일 쿼리를 기능 검사해야 하는 경우 스타일 쿼리를 사용하면 됩니다.

:root {
  --style-queries-supported: check;
}

.density-toggle {
  display: none;
}

@container style(--style-queries-supported) {
  .density-toggle {
    display: revert;
  }
}

JavaScript를 사용한 기능 확인

JavaScript를 사용한 기능 확인의 경우 컨테이너 스타일 쿼리가 추가되기 전에 CSSContainerRule 인터페이스가 존재했기 때문에 약간 더 복잡하므로 이 용도로는 신뢰할 수 없습니다. 대신 스타일 쿼리로 설정되는 알려진 속성의 계산된 값을 확인해야 합니다.

이 예시에서는 시각적 효과가 없으므로 맞춤 속성을 사용합니다.

:root {
  --style-queries-supported: check;
}

@container style(--style-queries-supported: check) {
  body {
    --style-queries-supported: yes;
  }
}

그런 다음 다음과 같이 JavaScript에서 계산된 값을 확인합니다.

if (getComputedStyle(document.body).getPropertyValue("--style-queries-supported") === "yes") {
  // Use container style queries
} else {
  // Use fallback strategy
}