📚 html & css

CSS의 Mask 기능 사용하기(border에 gradient color 적용하기) + tailwind

읏차 2024. 9. 17. 17:13

border에 gradient color를 적용해보며 css mask에 대해 알아보자.

 

 

CSS Mask로 뭘 할 수 있는데??

css mask를 사용하면 특정 요소를 그릴 때 source element와 destination element를 가지고 mask 속성값이 따라 무엇을 어떤식으로 그릴지에 대한 것을 정할 수 있다.

 

 

위 이미지의 경계선과 같은 효과를 주는 방법을 알아보자.

 

<div className="bg-transparent w-8/12 h-20 position: fixed top-5 left-1/2 -translate-x-1/2 rounded-full border-4 border-transparent font-bold text-lg" ref={navRef}>
      <div className="absolute inset-0 bg-gradient-to-r from-red-500 via-green-500 to-yellow-500 rounded-full color-morph-border p-[5px]"></div>
      // 내부 내용
</div>

 

코드를 간단히 설명하면

1. 먼저 최상위 element를 만들어 준다.

2. 자식 요소를 absolute를 사용하여 부모와 같은 크기로 만들어준다. (Mask 속성이 적용될 gradation border )

 

우리는 일단 2번 자식요소만 보면된다.

 

CSS Mask 속성

자식요소를 보면 중간에 color-morph-border라는 class가 보인다. tailwind에서 사용하기 위해 사용자 정의한 class인데 이에 적용된 css는 다음과 같다.

.color-morph-border {
  -webkit-mask: linear-gradient(#fff, #fff) content-box, linear-gradient(#fff, #fff);
  -webkit-mask-composite: destination-out;
  
  mask: linear-gradient(#fff, #fff) content-box, linear-gradient(#fff, #fff);
  mask-composite: exclude;
}

 

 

 

간단하게 말하면 요소의 content-box와 border-box 간 mask 연산을 한다.

mask 옵션은 exclude를 사용하여 두 마스킹 영역 사이에 제외되는 영역을 실제로 그린다는 뜻이다.

 

 

mask의 속성을 좀 더 살펴보면 첫번째 인자로 linear-gradient을 사용한 것을 볼 수 있다.

 

브라우저 탭을 살펴보면 우리가 설정했던 mask값이 mask-clip, mask-image 속성에 들어간 것으로 보인다.

여기서 mask-image에 linear-gradient가 들어가있는 것을 확인할 수 있다. image로 사용해야 하기 때문에 단일색상값(#fff와 같은)을 사용하지 못하는 것으로 보인다.

 

 

CSS Mask 핵심

mask의 인자에 들어가는 mask-clip(content-box, default는 border-box.. etc)와 linear-gradient 이미지의 alpha 속성, mask-composition의 설정값(add, subtract, exclude, intersect)을 제대로 알고 있어야 다양한 상황에서 Mask를 이용해서 간단하게 CSS효과를 줄 수 있다.

 

위에서 사용한 설정값과 이미지를 다시보면

.mask-style { 
  mask: linear-gradient(#fff, #fff) content-box, linear-gradient(#fff, #fff);
  mask-composite: exclude;
}

 

exclude를 사용했는데 이는 source와 destination의 두 영역이 겹치지 않는 부분으로 순서가 바뀌어도 결과가 동일했다.

 

.mask-style { 
  mask: linear-gradient(#fff, #fff), linear-gradient(#fff, #fff) content-box;
  mask-composite: exclude;
}

 

subtract는 source에서 destination을 제외한 영역을 보여준다. 이를 사용해서 동일한 결과를 보여줄 수 있다.

 

.mask-style {
  mask: linear-gradient(#fff, #fff), linear-gradient(#fff, #fff) content-box;
  mask-composite: subtract;
}

.mask-style {
  mask: linear-gradient(#fff, #fff) content-box, linear-gradient(#fff, #fff);
  mask-composite: subtract;
}

 

결과 이미지는 동일하다. 그리고 content-box를 앞에하던 뒤에하던 똑같은 결과를 보여준다.

해당 속성은 mask-clip의 값을 결정하는데 border-box(기본값), padding-box, content-box을 설정할 수 있다.

브라우저에서 이 중 넓은 속성을 source로 지정하고 내부 속성을 destination으로 지정하는 듯하다. ( 아님 )

 

 

그리고 alpha를 사용해서 오른쪽으로 갈수록 alpha가 0이 되도록 해보겠다.

.mask-style {
  mask: linear-gradient(to right, #fff, #fff0), linear-gradient(#fff, #fff) content-box;
  mask-composite: subtract;
}

이 동작이 이해가 된다면 이제 mask를 자유롭게 다룰 수 있다.

 

이해가 안간다면 다음 그림을 보자.

어느 정도 이해가 갔으리라 생각된다.

 

 

마지막으로 mask 인자의 순서에 따른 동작을 살펴보자.

.mask-style {
  mask: linear-gradient(#fff, #fff), linear-gradient(to right, #ffff, #fff0) content-box;
  mask-composite: exclude;
}

.mask-style {
  mask: linear-gradient(#fff, #fff), linear-gradient(to right, #ffff, #fff0) content-box;
  mask-composite: subtract;
}

둘의 결과 이미지는 동일하다. 우리가 원하는 이미지다.

 

 

 

그런데 여기서 subtract일때 mask에 두 인자 위치를 바꿔보겠다.

.mask-style {
  mask: linear-gradient(to right, #ffff, #fff0) content-box, linear-gradient(#fff, #fff);
  mask-composite: subtract;
}

 

mask인자의 순서를 source와 destination에 맞게 설정해줘야 원하는 결과값을 얻을 수 있다는 것을 알 수 있다.

 

 

 

+ gradient color animation

<div className="bg-transparent w-8/12 h-20 position: fixed top-5 left-1/2 -translate-x-1/2 rounded-full border-4 border-transparent font-bold text-lg" ref={navRef}>
      <div className="absolute inset-0 bg-gradient-to-r from-red-500 via-green-500 to-yellow-500 rounded-full color-morph-border p-[5px]"></div>
      /* 내부 내용 */
</div>
  useEffect(() => {
    const element = document.querySelector(".color-morph-border");
    let hue = 0;

    const changeColor = () => {
      if (!element) return;

      hue += 1;
      (element as HTMLElement).style.filter = `hue-rotate(${hue}deg)`;
      if (hue >= 360) hue = 0;
      requestAnimationFrame(changeColor);
    };

    changeColor();
  }, []);

 

맨 처음에 나왔던 최상위 요소에 filter 속성을 적용해주면 그라데이션의 색상이 일정하게 변하는 것을 볼 수 있다.