양방향 콘텐츠 공개

CSS 마스크와 등록된 맞춤 속성을 사용하여 사이트에 성능이 우수하고 상호작용이 가능한 공개 효과를 추가하세요. 방사형 그래디언트를 마스크로 사용하고 중지 값을 등록하면 최소한의 JavaScript로 사용자의 포인터를 따라가면서 진입과 종료를 부드럽게 전환할 수 있습니다.

구현

1. 맞춤 속성 등록

그라데이션 중지 값의 부드러운 보간을 사용 설정하려면 @property를 사용하여 변수를 등록해야 합니다. 이렇게 하면 브라우저 엔진에 데이터 유형이 전달되어 업데이트 중에 값 간에 전환할 수 있습니다.

/* Register the spotlight inner and outer sizes to enable interpolation */
 @property --inner-size{
  syntax: "<length-percentage>";
  inherits: true;
  initial-value: 0px;
}
@property --outer-size{
  syntax: "<length-percentage>";
  inherits: true;
  initial-value: 0px;
}

포인터 위치를 추적하는 맞춤 속성은 전환할 필요가 없으므로 등록할 필요가 없습니다.

2. 마스킹 레이어 정의

표시하려는 요소에 mask-image를 적용합니다. 등록된 속성을 참조하는 radial-gradient를 사용합니다.

.reveal-layer {
  /* Only transition the size properties, NOT the position variables */
  transition: --inner-size 0.2s ease-in-out, --outer-size 0.2s ease-in-out;  

  /* The spotlight is defined by the transparent center of the mask */
  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    black var(--inner-size, 0%),
    transparent var(--outer-size, 0%)
  );

  /* Ensure the mask doesn't repeat if the element is large */
  mask-repeat: no-repeat;

  /* Make the mask layer non-interactive */
  pointer-events: none;
}

/* Update the gradients stops on interaction */
.reveal-layer:hover {
  --inner-size: 100px;
  --outer-size: 120px;
}  

3. JavaScript로 좌표 업데이트하기

포인터 위치를 추적하고 CSS 변수를 업데이트합니다. 속성이 등록되어 있고 transition가 정의되어 있으므로 포인터 이벤트가 자주 발생하지 않더라도 스포트라이트가 원활하게 이동합니다.

const container = document.querySelector('.container');
// Store the container's bounding rect
let rect = container.getBoundingClientRect();
// Update the rect when the container is resized
const resizeObserver = new ResizeObserver(()=>{
  rect = container.getBoundingClientRect();
})
resizeObserver.observe(container);

container.addEventListener('pointermove', (e) => {
  // Calculate position as a percentage of the container.
  const x = ((e.clientX - rect.left) / rect.width) * 100;
  const y = ((e.clientY - rect.top) / rect.height) * 100;

  // Update the registered properties
  container.style.setProperty('--mouse-x', `${x}%`);
  container.style.setProperty('--mouse-y', `${y}%`);
});

4. 접근성 및 상호작용

필수 접근성 보장: 이 패턴은 포인터 상호작용을 사용하여 시각적 스포트라이트를 표시합니다. 스포트라이트 레이어를 포인터 사용자를 위한 비필수적인 시각적 개선 기능으로만 사용하여 기본 레이아웃에서 모든 기본 콘텐츠가 기본적으로 완전히 표시되고, 읽을 수 있고, 키보드로 독립적으로 접근할 수 있도록 보장해야 합니다(MUST). 키보드 전용 또는 보조 기술 사용자에게 필수 콘텐츠를 가리거나 차단하는 데 이 효과를 사용해서는 안 됩니다.

  • 포인터 이벤트: 마스크 오버레이 레이어에 pointer-events: none를 설정하여 표준 클릭 및 터치 상호작용이 아래의 컨트롤로 전달되도록 합니다.
  • 모션 감소 재정의: 모션 감소를 요청하는 사용자의 경우 부드러운 전환 보간을 사용 중지합니다.
/* MANDATORY Copy-Paste Safety: Disable transition scaling for motion-sensitive users */
@media (prefers-reduced-motion: reduce) {
  .reveal-layer {
    transition: none !important;
  }
}

대체 전략

등록되지 않은 속성 대체

mask-image는 지원하지만 @property는 지원하지 않는 브라우저에서는 스포트라이트가 계속 표시되지만, radial-gradient 내의 값을 보간할 수 없으므로 움직임이 켜짐 상태와 꺼짐 상태 사이를 오갑니다. var()를 사용할 때 대체 값을 제공합니다.

.reveal-layer {
  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    /* Use fallback values when using the `var()` function for browsers that don't get an initial value from the @property registration. */
    black var(--inner-size, 0%),
    transparent var(--outer-size, 0%)
  );
}

기본 마스크 지원

CSS 마스킹을 전혀 지원하지 않는 브라우저의 경우:

  1. 접두사가 있는 속성: 더 광범위한 브라우저 지원을 위해 -webkit-mask-image 접두사가 있는 속성을 사용합니다.
  2. 점진적 개선: UI의 기본 상태를 표시 효과 없이도 완전히 작동하고 읽을 수 있도록 디자인합니다. 이는 효과가 시각적 감각만 더하고 콘텐츠를 읽는 데 필요하지 않은 경우에 유용합니다.