브랜드 일관성을 유지하는 양식

웹 기능: accent-color

체크박스, 라디오 버튼과 같은 표준 HTML 양식 요소를 맞춤설정하는 것은 역사적으로 어려웠습니다. 개발자는 브라우저 기본값을 사용하거나 처음부터 맞춤 구성요소를 빌드하는 것 중에서 선택해야 하는 경우가 많았습니다. 맞춤 컨트롤을 빌드하는 데는 시간이 많이 걸리고 접근성 문제나 누락된 상태 (예: 체크박스의 불확정 상태)가 발생하기 쉽습니다.

CSS 속성 accent-color를 사용하면 접근성이나 내장 브라우저 기능을 희생하지 않고도 한 줄의 CSS로 내장 HTML 양식 입력에 브랜드 색상을 간단하게 적용할 수 있습니다.

구현 방법

양식 컨트롤에 브랜드 색상을 적용하려면 다음 단계를 따르세요.

  1. 브랜드 색상 식별: 브랜드를 나타내는 색상을 선택합니다.
  2. accent-color 속성 적용: CSS의 요소 또는 컨테이너 요소 (예: body 또는 특정 양식)에 accent-color를 추가합니다.
  3. 어두운 모드 지원 (선택사항이지만 권장됨): color-scheme를 사용하여 사이트에서 어두운 모드를 지원한다는 것을 브라우저에 알리고, 필요한 경우 대비를 개선하기 위해 accent-color를 조정합니다.

예시 코드: 브랜드 일관성을 유지하는 양식 컨트롤

:root {
  --brand-color: #6200ee;
}

/* Apply accent-color to the body or a specific container */
body {
  accent-color: var(--brand-color);
}

/* Optional: Adjust for dark mode if needed */
@media (prefers-color-scheme: dark) {
  :root {
    --brand-color: #bb86fc; /* A lighter shade for dark mode */
  }
}
<form>
  <!-- Checkbox -->
  <label for="subscribe">
    <input type="checkbox" id="subscribe" checked>
    Subscribe to newsletter
  </label>

  <!-- Radio Buttons -->
  <label for="plan-monthly">
    <input type="radio" id="plan-monthly" name="plan" value="monthly">
    Monthly
  </label>
  <label for="plan-yearly">
    <input type="radio" id="plan-yearly" name="plan" value="yearly" checked>
    Yearly
  </label>

  <!-- Range Slider -->
  <label for="volume">Volume:</label>
  <input type="range" id="volume" min="0" max="100" value="70">

  <!-- Progress Bar -->
  <label for="file">Upload Progress:</label>
  <progress id="file" max="100" value="70">70%</progress>
</form>

전략적 구현 및 권장사항

  • accent-color를 사용하여 브랜드에 맞게 양식 컨트롤의 테마를 쉽게 지정하세요.
  • 브라우저가 대비를 처리할 것이라고 맹목적으로 신뢰하지 마세요. 브라우저는 적절한 대비 색상을 자동으로 결정해야 하지만, Safari (WebKit 버그 244233) 및 Android Chrome (Chromium 버그 343503163)과 같은 구현의 알려진 버그로 인해 체크표시 색상이 반전되지 않을 수 있습니다. 이로 인해 배경과 대비가 충분하지 않은 색상 (예: 밝은 모드의 밝은 색상 또는 어두운 모드의 어두운 색상)을 사용할 때 컨트롤이 보이지 않거나 보기 어려울 수 있습니다.
  • accent-color를 color-scheme: light dark와 결합하여 밝은 테마와 어두운 테마 모두에서 양식 컨트롤이 제대로 표시되도록 해야 합니다.
  • 배경색과 너무 유사한 색상을 사용하지 마세요. 브라우저가 대비를 보장하려고 하지만 기본 대비가 좋은 색상을 제공하는 것이 가장 좋습니다.
  • accent-color가 모든 양식 요소에서 작동한다고 가정하지 마세요. 현재는 checkbox, radio, range, progress 요소만 색조를 조정합니다.

대체 전략

accent-color를 지원하지 않는 브라우저의 경우 양식 컨트롤은 브라우저의 기본 모양으로 대체됩니다. 모든 환경에서 완전한 브랜드 일관성과 높은 안정성을 보장하려면 확립된 '시각적으로 숨겨진 입력' 기법을 사용하여 맞춤 대체 전략을 구현해야 합니다(MUST).

@supports not를 사용한 점진적 개선

accent-color가 지원되지 않는 경우에만 @supports not 규칙을 사용하여 맞춤 대체 스타일을 적용해야 합니다(MUST). 이렇게 하면 최신 브라우저에서 accent-color의 단순성을 활용하면서 이전 브라우저에서 일관된 브랜드 환경을 보장할 수 있습니다.

1. HTML 구조

CSS에서 동위 선택자를 허용하도록 라벨이 <span>의 텍스트를 래핑하는지 확인합니다.

<label for="subscribe-fallback">
  <input type="checkbox" id="subscribe-fallback" class="visually-hidden" checked>
  <span>Subscribe to newsletter</span>
</label>

2. CSS 대체

@supports not 블록 내에 맞춤 스타일을 적용합니다.

/* Fallback for older browsers without accent-color */
@supports not (accent-color: var(--brand-color)) {
  /* Visually hide the native input using the canonical accessible recipe */
  form input[type="checkbox"].visually-hidden {
    position: absolute !important;
    clip-path: inset(50%) !important;
    overflow: hidden !important;
    width: 1px !important;
    height: 1px !important;
    margin: -1px !important;
    padding: 0 !important;
    border: 0 !important;
    white-space: nowrap !important;
  }

  /* Style the wrapper label */
  label {
    position: relative;
    padding-left: 2rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
  }

  /* Custom box for checkbox */
  input[type="checkbox"] + span::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1.2rem;
    height: 1.2rem;
    border: 2px solid #ccc;
    background: white;
    border-radius: 4px;
    box-sizing: border-box;
    transition: all 0.2s ease;
  }

  /* Ensure custom checkbox shows focus for keyboard users */
  input[type="checkbox"]:focus-visible + span::before {
    outline: 2px solid #000;
    outline-offset: 2px;
  }

  /* Checked State */
  input[type="checkbox"]:checked + span::before {
    background-color: var(--brand-color, #6200ee);
    border-color: var(--brand-color, #6200ee);
  }

  /* Checkmark (Unicode) */
  input[type="checkbox"]:checked + span::after {
    content: "✓";
    position: absolute;
    left: 0.25rem;
    top: 50%;
    transform: translateY(-50%);
    color: white;
    font-weight: bold;
    font-size: 0.9rem;
  }

  /* Fallback for Range Slider */
  input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
  }

  /* Webkit (Chrome, Safari, Edge) */
  input[type="range"]::-webkit-slider-runnable-track {
    width: 100%;
    height: 8px;
    /* Use gradient to show progress for a static value (e.g., 70%) or update with JS */
    background: linear-gradient(to right, var(--brand-color, #6200ee) 70%, #ccc 70%);
    border-radius: 4px;
  }

  input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    height: 16px;
    width: 16px;
    border-radius: 50%;
    background: var(--brand-color, #6200ee);
    cursor: pointer;
    margin-top: -4px; /* Center vertically */
  }

  /* Firefox */
  input[type="range"]::-moz-range-track {
    width: 100%;
    height: 8px;
    background: #ccc;
    border-radius: 4px;
  }

  input[type="range"]::-moz-range-thumb {
    height: 16px;
    width: 16px;
    border-radius: 50%;
    background: var(--brand-color, #6200ee);
    cursor: pointer;
  }

  /* Firefox specific progress bar */
  input[type="range"]::-moz-range-progress {
    background-color: var(--brand-color, #6200ee);
    height: 8px;
    border-radius: 4px;
  }

  /* Fallback for Progress Bar */
  progress {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border: none;
    background: #ccc;
    height: 8px;
    border-radius: 4px;
  }

  progress::-webkit-progress-bar {
    background-color: #ccc;
    border-radius: 4px;
  }

  progress::-webkit-progress-value {
    background-color: var(--brand-color, #6200ee);
    border-radius: 4px;
  }

  progress::-moz-progress-bar {
    background-color: var(--brand-color, #6200ee);
    border-radius: 4px;
  }
}

Webkit 대체에서 동적 범위 진행 상황

Webkit 브라우저에서 범위 슬라이더의 엄지손가락으로 진행률 채우기를 이동하려면(accent-color 없이) CSS 변수와 소량의 JavaScript를 사용하면 됩니다.

  1. CSS 업데이트: 그라데이션 중지에 CSS 변수를 사용합니다.

    input[type="range"]::-webkit-slider-runnable-track {
      background: linear-gradient(to right, var(--brand-color) var(--progress, 0%), #ccc var(--progress, 0%));
    }
    
  2. JavaScript 추가: input 이벤트에서 변수를 업데이트합니다.

if (!CSS.supports('accent-color')) {
  const slider = document.getElementById('volume');
  slider.addEventListener('input', (e) => {
    e.target.style.setProperty('--progress', `${e.target.value}%`);
  });
}