안녕하세요! 프론트엔드 강사입니다. 이번에는 웹 디자인에서 이미지를 자르고 다듬는 데 아주 강력한 도구인 CSS 마스크 속성(CSS mask properties)에 대해 알아보겠습니다.
클리핑(clip-path)이 도형의 윤곽선을 따라 싹둑 잘라내는 가위질이라면, 마스킹(mask)은 투명한 레이어를 덧대어 원하는 부분만 은은하게 비춰주는 셀로판지 필터 같은 느낌이에요. 공식 문서의 내용을 바탕으로 실무에서 어떻게 쓰이는지, 복잡한 속성들은 어떻게 작동하는지 제가 아주 쉽게 풀어서 설명해 드릴게요!
CSS 마스킹(masking)은 요소에 마스크를 적용하여 요소의 보이는 부분을 정의하는 기술입니다. 이 마스크는 적용된 마스크 이미지의 알파 채널(투명도)이나 색상에 따라 요소의 특정 부분을 선택적으로 드러내거나 숨깁니다.
마스킹 소개 가이드에서는 다양한 유형의 마스크 이미지와 그 모드에 대해 소개하고 있습니다. 다중 마스크 선언하기 가이드에서는 마스크 레이어(mask layers)와 mask 단축(shorthand) 속성에 대해 논의하며, 단축 속성을 구성하는 세부 속성들을 간단히 소개했습니다.
이번 가이드에서는 이러한 세부(component) 속성들을 훨씬 더 깊이 있게 탐구하고, 그것들이 서로 어떻게 상호작용하는지 살펴볼 것입니다. 또한 여러 개의 마스크 이미지가 선언되었을 때, 마스크 레이어들이 어떻게 합성(composited)되거나 결합되는지에 대해서도 설명해 드릴게요.
CSS 마스크는 하나 이상의 마스크 레이어로 구성됩니다. mask 또는 mask-image 값에 쉼표로 구분된 목록을 주면, 그 값이 이미지든, 마스크 소스이든, 심지어 none이라는 키워드이든 상관없이 각 값마다 하나의 마스크 레이어가 생성됩니다.
모든 mask-image는 기준점(origin) 박스를 기준으로 위치가 지정(positioned)됩니다. 마스크 이미지들은 크기가 조절(sized)되고, 반복(repeated)되며, 잘려(clipped)질 수 있습니다. 그런 다음 이전 레이어들과 함께 합성(composited)되어 요소의 최종적인 시각적 마스크를 만들어냅니다.
mask-image 속성 (The mask-image property)mask-mode 속성 (The mask-mode property)mask-position 속성 (The mask-position property)mask-origin 속성 (The mask-origin property)mask-clip 속성 (The mask-clip property)mask-size 속성 (The mask-size property)mask-repeat 속성 (The mask-repeat property)mask-composite 속성 (The mask-composite property)mask-image 속성 (The mask-image property)마스크를 만들기 위한 가장 기본적인 조건은 mask-image 속성에 none이 아닌 값을 설정하는 것입니다.
마스크 소스 목록에서 none 키워드도 하나의 마스크 레이어를 생성하긴 합니다. 하지만 mask-image의 값이 오직 none 하나뿐이라면 아무런 마스킹도 일어나지 않습니다.
마스크 이미지로는 CSS 그라디언트, PNG나 SVG 같은 외부 이미지, 또는 SVG의 <mask> 요소를 사용할 수 있습니다.
아래 예제에서는 1개의 외부 이미지, 2개의 그라디언트, 이미지가 없는 레이어(none), 그리고 SVG <mask> 소스 등 총 5개의 마스크 레이어를 만듭니다.
.masked-element {
mask-image:
url("alphaImage.png"), linear-gradient(to right, black, transparent),
radial-gradient(circle, white 50%, transparent 75%), none, url("#svg-mask");
}
마스크 이미지 중 하나가 none으로 지정되었기 때문에, 실제로는 5개의 마스크 레이어가 생성되지만 요소(.masked-element)에 마스킹을 적용하는 이미지는 4개뿐입니다.
none의 중요성 (The importance of none)none 레이어 자체는 어떤 시각적인 효과도 주지 않습니다. (이게 나중에 합성되는 마스크에 어떤 영향을 미치는지 보려면 mask-composite 속성을 확인하세요.)
하지만 쉼표로 구분된 mask-* 속성 목록의 각 값은 각각의 개별 마스크 레이어에 1:1로 적용되기 때문에, none 값은 최종 마스크 모양에 당장 영향을 주지 않더라도 자리(인덱스)를 차지하는 아주 중요한 역할을 합니다.
우리가 만든 5겹의 레이어 구조에서 4번째에 있는 이 none 레이어는, 다른 mask-* (예: mask-repeat, mask-position) 속성의 쉼표로 구분된 목록 중 4번째 값과 짝을 이룹니다.
앞서 언급했듯, 레이어의 개수는 값이 none이더라도 무조건 mask-image 속성에 쉼표로 구분된 값의 개수에 의해 결정됩니다.
나머지 모든 mask-* 값들은 이 mask-image 값들과 순서대로 짝이 지어집니다.
mask-* 속성의 값 개수가 마스크 레이어(mask-image) 개수보다 많다면, 남는 값들은 무시됩니다. 만약 mask-* 속성에 단 하나의 값만 있다면, 그 값은 모든 레이어에 똑같이 적용됩니다. 만약 5개의 값이 있다면, 4번째 값은 none 레이어에, 5번째 값은 SVG <mask> 소스 레이어에 적용되겠죠. 만약 2개의 값만 쉼표로 적혀 있다면, 첫 번째 값은 1, 3, 5(홀수 번째) 레이어에 적용되고, 두 번째 값은 2, 4(짝수 번째) 레이어에 적용될 겁니다.
예를 들어 볼까요? 각 mask-* 속성은 이렇게 서로 다른 개수의 값을 가질 수 있습니다.
.masked-element {
mask-image:
url("alphaImage.png"), linear-gradient(to right, black, transparent),
radial-gradient(circle, white 50%, transparent 75%), none, url("#svg-mask");
mask-repeat: repeat-x, repeat-y;
mask-position:
center,
top left,
bottom right;
}
이 코드에서 mask-repeat는 2개뿐이므로, 홀수 번째 레이어(1, 3, 5번)는 X축으로 반복(repeat-x)되고, 짝수 번째 레이어(2, 4번)는 Y축으로 반복(repeat-y)됩니다.
mask-position은 3개뿐이므로 1, 4번째 레이어 이미지는 정중앙(center), 2, 5번째는 좌측 상단(top left) 모서리, 3번째는 우측 하단(bottom right) 모서리에 배치됩니다. 즉, 5번째 레이어인 #svg-mask 이미지는 좌측 상단 모서리(top left)에서 시작해 X축으로 반복(repeat-x)된다는 뜻입니다.
더 자세한 내용은 마스크 레이어와 none 키워드(mask layers and the none keyword) 문서를 참고해 보세요.
mask-mode 속성 (The mask-mode property)mask-mode 속성은 각 마스크 레이어가 요소를 어떻게 가릴지 방식을 결정합니다. 값으로는 alpha 또는 luminance를 사용할 수 있으며, 기본값인 match-source를 사용해 마스크 원본(source)의 기본 방식을 따르게 할 수도 있습니다.
대부분의 mask-* 속성은 배경 이미지의 background-* 속성(예: mask-image와 background-image)과 쌍을 이루지만, 이 mask-mode와 나중에 배울 mask-composite는 배경 속성에는 존재하지 않는 마스크만의 독특한 속성입니다.
alpha와 luminance (Mask types: alpha and luminance)모든 마스크는 alpha 마스크이거나 luminance 마스크 둘 중 하나로 동작합니다.
alpha 마스크: 각 마스크 픽셀의 알파(투명도) 채널만이 중요합니다. 마스크가 불투명(opaque)한 곳은 요소의 해당 부분이 그대로 보입니다. 마스크가 투명(transparent)한 곳은 요소가 숨겨집니다. 마스크가 반투명(semi-opaque)하다면, 요소 역시 그만큼 반투명해집니다. 이 방식에서는 마스크의 색상이 무슨 색인지는 전혀 중요하지 않고, 오직 그 색상의 '투명도'만이 영향을 미칩니다.luminance 마스크: 알파 채널뿐만 아니라, 마스크 색상의 밝기(brightness)도 함께 고려하여 마스킹 된 영역의 불투명도를 결정합니다. (밝을수록 잘 보이고, 어두울수록 투명해집니다.)👨🏫 강사의 팁:
"투명한 부분은 뚫리고, 안 투명한 부분은 보인다!" 이게alpha마스크의 핵심이에요. 실무에서 포토샵으로 누끼를 딴 PNG 이미지를 마스크로 쓸 때 가장 많이 씁니다. 반면luminance는 흑백 이미지를 활용할 때 많이 쓰는데, 흰색은 완전히 보여주고 검은색은 완전히 투명하게 날려버린다고 생각하시면 편해요!
참고:
앞으로 나오는 모든 예제는 마스크가 적용될 요소의 background-image로 아래의 무지개 깃발 이미지를 사용합니다.
이 예제는 alpha와 luminance 마스크의 차이를 보여줍니다. 둘 다 완전히 똑같은 그라디언트를 마스크로 사용했어요.
alpha 마스크 예제에서는 그라디언트 마스크 색상의 알파 투명도(alpha-transparency)만이 영향을 미칩니다. 하지만 luminance 예제에서는 색상의 R, G, B, A(빨, 초, 파, 투명도) 값이 모두 종합적으로 계산되어 영향을 줍니다.
(마지막 빈 컨테이너는 우리가 mask-image로 사용한 그라디언트 원본이 어떻게 생겼는지 보여주기 위해 넣은 것입니다.)
<div class="alpha">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="luminance">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="gradient"></div>
body {
display: flex;
gap: 20px;
padding: 15px;
background-image: conic-gradient(
transparent 90deg,
rgb(0 0 0 / 0.05) 90deg 180deg,
transparent 180deg 270deg,
rgb(0 0 0 / 0.05) 270deg
);
background-size: 30px 30px;
}
div,
svg,
img {
width: 220px;
aspect-ratio: 1;
}
div {
border: 1px solid black;
}
img {
mask-image: repeating-linear-gradient(
to bottom right,
red 0 20px,
#ff000055 20px 40px,
transparent 40px 60px
);
}
.gradient {
background: repeating-linear-gradient(
to bottom right,
red 0 20px,
#ff000055 20px 40px,
transparent 40px 60px
);
}
이제 각 이미지에 다른 mask-mode 속성 값을 설정해 보겠습니다.
.alpha img {
mask-mode: alpha;
}
.luminance img {
mask-mode: luminance;
}
alpha의 경우, 오직 그라디언트 색상의 투명도(transparency)만이 중요합니다. 그라디언트가 '불투명한 빨간색(opaque red)'인 부분은 원본 이미지(무지개)도 완벽하게 보입니다. 그라디언트가 '투명한(transparent)' 부분에서는 이미지가 완전히 숨겨집니다. 그라디언트가 '50% 불투명(#ff000055)'한 곳에서는 이미지도 50% 불투명하게 보이죠.
반면 luminance의 경우에는 색상의 밝기(brightness)가 중요해집니다! 빨간색은 흰색보다 어둡기 때문에 불투명한 빨간색이어도 약간 투명하게 변하는 걸 볼 수 있어요. 마스크의 불투명도를 결정할 때 색상의 R, G, B, A 채널을 모두 사용하는 방정식에 대해 알아보려면 알파 투명도 대 광도(Alpha transparency versus luminance) 문서를 참고하세요.
mask-mode의 기본값: match-source (The mask-mode default value: match-source)mask-mode 속성의 기본값은 match-source입니다. 이 값은 mask-mode가 마스크 원본(source)의 기본 모드 유형을 알아서 따라가도록 만듭니다. 마스크 소스가 SVG <mask...> 요소인 경우를 제외하고, 다른 모든 일반적인 마스크(PNG, 그라디언트 등)에 대해 match-source는 무조건 alpha로 작동합니다.
만약 SVG <mask...> 요소를 마스크 소스로 사용했다면, match-source는 해당 <mask...> 요소에 설정된 mask-type 속성의 값을 따라갑니다. 만약 해당 요소에 CSS mask-type이 정의되지 않았다면, 그다음엔 SVG 자체의 HTML 속성인 mask-type 값을 찾습니다. 만약 이것마저 생략되어 있다면, 최종적으로 match-source는 luminance 방식으로 동작하게 됩니다.
맨 처음 보았던 masked-element 예제로 돌아가 보죠. 만약 우리가 mask-mode 속성을 명시적으로 설정하지 않는다면, 각 레이어는 기본값인 match-source가 되어 마치 아래와 같이 설정한 것과 같아집니다.
.masked-element {
mask-image:
url("alphaImage.png"), linear-gradient(to right, black, transparent),
radial-gradient(circle, white 50%, transparent 75%), none, url("#svg-mask");
mask-mode: match-source;
}
또는 mask 단축 속성을 사용한다면 이렇게 표현되겠죠.
.masked-element {
mask:
url("alphaImage.png") match-source,
linear-gradient(to right, black, transparent) match-source,
radial-gradient(circle, white 50%, transparent 75%) match-source,
none match-source,
url("#svg-mask") match-source;
}
url("alphaImage.png")는 이미지 파일입니다. SVG의 <mask...>가 아니므로, mask-mode는 alpha로 결정됩니다.alpha가 됩니다.none입니다. 여기서 none은 '투명한 검은색' 마스크를 의미합니다. 만약 mask-mode 값이 쉼표로 5개 적혀있었다면 4번째 값이 이 none 레이어에 적용되었을 겁니다.id가 svg-mask인 SVG <mask...> 요소를 참조합니다. 이 경우 다른 레이어들과 달리 <mask...> 요소의 자체 속성을 따르게 되며, 별도로 설정되지 않았다면 기본적으로 luminance가 적용됩니다.즉, 아무것도 적지 않았을 때 실제 브라우저가 해석하는 결과는 다음과 같습니다.
.masked-element {
mask-image:
url("alphaImage.png"), linear-gradient(to right, black, transparent),
radial-gradient(circle, white 50%, transparent 75%), none, url("#svg-mask");
mask-mode: alpha, alpha, alpha, match-source, luminance;
}
mask-position 속성 (The mask-position property)배경 이미지의 위치를 잡는 background-position 속성과 완벽하게 유사한 mask-position 속성은 마스크 레이어의 기점(origin box, mask-origin에 의해 정의됨)을 기준으로 마스크 이미지의 '초기 위치'를 설정합니다. 문법 역시 background-position의 구문을 그대로 따르며, 1개, 2개, 또는 4개의 위치 값(<position>)을 사용해 상대적 또는 절대적 위치 오프셋을 정의합니다.
키워드(top, bottom 등)를 1개만 입력하면, 그 값은 마스크가 찰싹 달라붙을 기준 가장자리를 지정하며, 나머지 반대축 방향은 자동으로 center(중앙)가 됩니다. (예: top -> 위쪽 중앙)
만약 1개의 퍼센트(%)나 길이(px) 값만 입력하면, 이 값은 마스크 기준 박스의 왼쪽 가장자리로부터의 X 좌표를 의미하며, Y 좌표는 자동으로 50%(중앙)로 설정됩니다.
2개의 값을 입력할 때, 키워드와 길이 값이 섞여 있다면 center라는 키워드가 쓰일 때만 순서가 중요해집니다.
마스크의 위치는 꼭 왼쪽 위(top left) 모서리를 기준으로 할 필요가 없습니다. 4개 값 구문을 사용하면 네 모서리 중 아무 곳에서나 마스크를 띄워놓을(offset) 수 있어요. 값은 두 쌍의 '기준 가장자리 + 오프셋 거리'로 이루어집니다.
예를 들어 mask-position: right 10px bottom 20px라고 쓰면 "오른쪽 가장자리에서 왼쪽으로 10px, 바닥 가장자리에서 위로 20px 떨어진 곳"에 마스크를 위치시키라는 뜻이 됩니다. 여기서 가로를 먼저 쓰든 세로를 먼저 쓰든 상관없습니다. 기준이 되는 키워드가 앞에 명확히 붙어있기 때문이죠!
오프셋 위치를 퍼센트(%)로 설정할 때의 동작 방식은 아주 독특합니다. background-position에서 퍼센트가 작동하는 방식과 마찬가지로, 요소의 전체 치수(크기)에서 마스크 자체의 치수(크기)를 뺀 나머지 여백을 기준으로 퍼센트가 계산됩니다.
mask-position 속성은 마스크 이미지의 '초기 위치(initial position)'를 정의합니다. 여기서 '초기 위치'란, 만약 마스크가 반복(repeat)되도록 설정되어 있다면, 브라우저가 이 mask-position으로 지정된 위치에 '첫 번째' 마스크(원본)를 쿵 하고 내려놓는다는 뜻입니다. 그리고 이 원본을 기준으로 상하좌우로 패턴이 뻗어나가며 반복되는 것이죠.
이 예제에서는 첫 번째 이미지의 위치를 bottom right(우측 하단)로 설정했습니다. 즉, 첫 번째 마스크가 마스크 기준 박스의 맨 오른쪽 아래 모서리에 딱 맞게 배치됩니다. 마스크 이미지는 기본적으로 무한 반복되도록 설정되어 있기 때문에, 이 첫 번째 마스크의 위쪽과 왼쪽 공간을 향해 반복되는 패턴이 채워져 나갑니다.
<div class="keywords">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="twoValue">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="fourValue">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
div::before {
content: 'class="' attr(class) '"';
display: block;
text-align: center;
font-family: monospace;
}
body {
flex-flow: row wrap;
}
div,
img {
width: calc(30vw - 20px);
}
img {
mask-image: url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)");
}
.keywords img {
mask-position: bottom right;
}
.twoValue img {
mask-position: -20px -10px;
}
.fourValue img {
mask-position: right -20px bottom -10px;
}
만약 mask-repeat 속성의 기본값인 repeat를 no-repeat(반복 안 함)으로 바꾸면, 이 첫 번째 마스크(원본) 하나만 덩그러니 남게 되어 위치의 개념이 더욱 명확해집니다.
img {
mask-repeat: no-repeat;
}
mask-origin 속성 (The mask-origin property)요소에 패딩(padding)이나 테두리(border)가 있는 경우, mask-origin 속성은 마스크 이미지가 배치되는 '기준점 박스(마스크 포지셔닝 영역)'를 이 패딩, 테두리, 혹은 콘텐츠 영역 중 어디로 할지 결정합니다.
예를 들어, mask-position이 top left일 때, 이게 요소의 테두리 바깥쪽 모서리를 기준으로 하는 건지, 패딩 바깥쪽인지, 아니면 실제 내용물이 있는 콘텐츠 박스의 바깥쪽 모서리인지를 지정해 주는 거죠.
앞선 mask-position 예제에서는 기준이 border-box(기본값)였습니다. 하지만 <img> 태그에 별도의 테두리나 패딩이 없었기 때문에 content-box, padding-box, border-box가 사실상 모두 똑같은 크기였죠.
이번 예제에서는 마스크가 확연히 보이도록 두꺼운 테두리(15px)와 패딩(15px)을 주고, 배경을 초록색으로 칠했습니다. 그리고 라디오 버튼을 눌러 mask-origin 값을 바꿔보면, 첫 번째 별 마스크(기준점)가 어디서부터 시작하는지 확연히 이동하는 것을 볼 수 있습니다!
<div class="border-box">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<fieldset>
<legend>Set the <code>mask-origin</code> value</legend>
<label
><input type="radio" name="origin" id="origin_border-box" checked />
border-box</label
>
<label
><input type="radio" name="origin" id="origin_padding-box" />
padding-box</label
>
<label
><input type="radio" name="origin" id="origin_content-box" />
content-box</label
>
</fieldset>
div {
all: unset;
}
legend {
align-self: baseline;
}
label {
display: block;
}
img {
mask-image: url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)");
mask-position: top left;
padding: 15px;
border: 15px solid;
background-color: green;
}
:has(#origin_border-box:checked) img {
mask-origin: border-box;
}
:has(#origin_padding-box:checked) img {
mask-origin: padding-box;
}
:has(#origin_content-box:checked) img {
mask-origin: content-box;
}
기본값은 border-box입니다. 이 상태에서는 첫 번째 마스크가 테두리의 가장 바깥쪽 좌측 상단 모서리에 배치되며 잘리지 않습니다. 하지만 padding-box나 content-box로 기준점을 안쪽으로 밀어 넣게 되면, 기준점보다 위쪽이나 왼쪽에 배치된 반복 마스크들은 요소의 바깥쪽 영역(테두리 등)에 걸려 잘려 나가게(clip) 됩니다.
👨🏫 강사의 팁:
background-origin속성을 아신다면 완벽하게 같은 개념입니다! 배경 이미지가 테두리를 덮게 할지, 패딩부터 시작하게 할지 정하는 것과 동일하게 마스크가 시작되는 캔버스의 크기를 정하는 거예요.
mask-clip 속성 (The mask-clip property)mask-clip 속성은 요소에서 실제로 마스크의 영향을 받을(마스킹 처리가 될) 최종 구역을 결정합니다. 지정한 박스 바깥으로 나가는 부분은 가차 없이 싹둑 잘라내(clip) 버리죠.
mask-clip과 방금 배운 mask-origin은 값의 종류(border-box 등)가 같고 기본값마저 동일해서 비슷해 보이지만, 역할은 완전히 다릅니다.
mask-origin: 마스크 원본(패턴)을 어디에 배치(시작)할 것인가?mask-clip: 마스크가 적용된 최종 결과를 어디까지 살려두고(자르고) 나머지를 버릴 것인가?만약 두 속성의 값을 다르게 설정하면 재미있는 일이 생깁니다. 예를 들어, 요소에 테두리와 패딩이 빵빵하게 들어있는데, mask-origin은 border-box(테두리 끝에서부터 널찍하게 마스크를 그려라)로 설정하고, mask-clip은 content-box(근데 실제 그리는 건 가장 안쪽의 콘텐츠 박스 영역까지만 그리고 나머진 다 잘라버려라!)로 설정하면, 마스크 이미지는 넓게 펼쳐져 있지만 바깥쪽 테두리와 패딩 영역에 있는 마스크 패턴들은 무참히 잘려 나가는 것을 볼 수 있습니다.
아래 예제에서 라디오 버튼을 눌러가며, 방금 배운 origin과 이번에 배운 clip이 서로 어떤 영향을 주고받는지 확인해 보세요! no-clip이라는 특별한 값을 주면 마스크를 전혀 자르지 않고 요소 밖으로 삐져나가는 것을 허용할 수도 있습니다.
<div class="border-box">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<fieldset>
<legend>Set the <code>mask-clip</code> value</legend>
<label
><input type="radio" name="clip" id="clip_border-box" checked />
border-box</label
>
<label
><input type="radio" name="clip" id="clip_padding-box" /> padding-box</label
>
<label
><input type="radio" name="clip" id="clip_content-box" /> content-box</label
>
<label><input type="radio" name="clip" id="clip_no-clip" /> no-clip</label>
</fieldset>
<fieldset>
<legend>Set the <code>mask-origin</code> value</legend>
<label
><input type="radio" name="origin" id="origin_border-box" checked />
border-box</label
>
<label
><input type="radio" name="origin" id="origin_padding-box" />
padding-box</label
>
<label
><input type="radio" name="origin" id="origin_content-box" />
content-box</label
>
</fieldset>
:has(#clip_border-box:checked) img {
mask-clip: border-box;
}
:has(#clip_padding-box:checked) img {
mask-clip: padding-box;
}
:has(#clip_content-box:checked) img {
mask-clip: content-box;
}
참고로, 마스크 소스가 SVG <mask...> 요소일 경우에는 이 mask-clip 속성이 아무런 힘을 쓰지 못합니다. 이때는 <mask...> 요소 자체의 x, y, width, height 등의 속성이 마스킹 영역을 직접 결정하게 됩니다.
mask-size 속성 (The mask-size property)mask-size 속성은 마스크 레이어의 크기를 조절하는 데 사용됩니다. background-size와 완벽하게 똑같이 동작하고 값도 똑같습니다!
mask-size를 선언하는 세 가지 방법이 있습니다:
cover (꽉 차게 덮기) 또는 contain (안에 다 들어가게 맞추기) 키워드auto 값 1개 (너비만 지정, 높이는 원본 비율에 맞게 auto)auto의 조합으로 된 값 2개 (너비와 높이 각각 지정)만약 2개의 값을 원본 비율과 다르게 마음대로 주면, 마스크 이미지가 찌그러지게(distorted) 됩니다.
contain을 쓰면, 마스크 이미지가 잘리지 않는 선에서 요소(마스크 기준 박스) 안에 들어갈 수 있는 가장 큰 크기로 확대/축소됩니다.cover를 쓰면, 요소의 전체 영역을 빈틈없이 덮을 수 있는 가장 작은 크기로 확대/축소됩니다. 이 과정에서 마스크의 일부가 화면 밖으로 잘려 나갈 수 있습니다.아래 데모에서 별 모양 마스크의 크기를 조절해 보세요!
<div class="border-box">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<fieldset>
<legend>Set the <code>mask-origin</code> value</legend>
<label
><input type="radio" name="origin" id="border-box" checked />
border-box</label
>
<label
><input type="radio" name="origin" id="padding-box" /> padding-box</label
>
<label
><input type="radio" name="origin" id="content-box" /> content-box</label
>
</fieldset>
img {
mask-image: url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)");
mask-position: top left;
padding: 15px;
border: 15px solid;
background-color: green;
}
img {
mask-size: 100%;
}
:has(#border-box:checked) img {
mask-origin: border-box;
}
:has(#padding-box:checked) img {
mask-origin: padding-box;
}
:has(#content-box:checked) img {
mask-origin: content-box;
}
기본값은 auto이며, 이는 마스크 이미지 고유의 크기(intrinsic size) 그대로 렌더링한다는 뜻입니다. 만약 CSS 그라디언트처럼 고유 크기가 없는 이미지라면, auto는 mask-origin으로 지정된 박스의 전체 크기(100%)와 동일하게 꽉 차게 렌더링됩니다.
mask-repeat 속성 (The mask-repeat property)mask-repeat 속성은 초기 마스크 이미지가 mask-size와 mask-position으로 자리를 잡은 후에, 그 이미지를 가로(X축) 및 세로(Y축) 방향으로 어떻게 반복해서 바둑판처럼 이어 붙일지(tile) 정의합니다. 이전 예제들에서 별 마스크가 가득 차 있었던 이유는 이 속성의 기본값이 repeat (상하좌우 모두 반복)이기 때문입니다.
background-repeat와 똑같이 repeat-x, repeat-y, no-repeat, space, round 등의 값을 사용할 수 있습니다.
mask-composite 속성 (The mask-composite property)드디어 가장 마법 같은 기능입니다! mask 단축 속성에 포함된 mask-composite 속성은, 여러 개의 마스크 레이어가 겹쳐 있을 때 이것들을 어떻게 섞어서 하나의 최종 마스크를 만들 것인지를 정의합니다.
값으로는 아래 4가지가 있습니다. 위층의 마스크가 그 아래층에 있는 마스크들과 어떻게 반응할지 정하는 거예요.
add (더하기 - 기본값): 두 마스크가 합쳐져서 둘 중 하나라도 불투명한 곳은 다 보입니다.subtract (빼기): 아래층 마스크에서 위층 마스크의 모양만큼 구멍을 파버립니다(제외시킵니다).intersect (교차): 두 마스크가 서로 겹치는(교차하는) 공통된 부분만 남깁니다.exclude (배제): 두 마스크가 겹치지 않는 부분만 남기고, 겹치는 부분은 투명하게 날려버립니다.👨🏫 강사의 팁:
일러스트레이터나 파워포인트에 있는 '도형 병합(패스파인더)' 기능과 완벽하게 똑같은 원리예요! 여러 도형을 더하고 빼고 교차해서 복잡한 모양을 만들어낼 수 있죠.
아래 예제는 무지개 깃발 이미지 위에 두 개의 마스크를 겹친 것입니다. 하나는 사선으로 그어진 줄무늬 그라디언트 패턴이고, 다른 하나는 큰 별 모양 마스크입니다. 이 둘이 4가지 속성에 따라 어떻게 합성되는지 확인해 보세요! (단축 속성에서는 / 슬래시 뒤에 mask-composite 값을 적어줄 수 있습니다.)
<div class="add">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="subtract">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="intersect">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="exclude">
<img
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
div::before {
content: "mask-composite: " attr(class);
display: block;
text-align: center;
font-family: monospace;
}
body {
flex-flow: row wrap;
}
img {
mask-image:
repeating-linear-gradient(
to bottom right,
red 0 20px,
#ff000055 20px 40px,
transparent 40px 60px
),
url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)");
}
.add img {
mask-composite: add;
}
.subtract img {
mask-composite: subtract;
}
.intersect img {
mask-composite: intersect;
}
.exclude img {
mask-composite: exclude;
}
만약 마스크 레이어의 선언 순서를 뒤바꾸면(별을 먼저 깔고 줄무늬를 나중에 올리면), subtract(빼기) 같은 계산에서 기준이 달라지기 때문에 완전히 다른 결과가 나오는 것도 확인할 수 있습니다!
<div class="subtract">
<img
class="gradientFirst"
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
<div class="subtract">
<img
class="starFirst"
src="[https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg](https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg)"
alt="Pride flag" />
</div>
.gradientFirst {
mask-image:
repeating-linear-gradient(
to bottom right,
red 0 20px,
#ff000055 20px 40px,
transparent 40px 60px
),
url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)");
}
.starFirst {
mask-image:
url("[https://mdn.github.io/shared-assets/images/examples/mask-star.svg](https://mdn.github.io/shared-assets/images/examples/mask-star.svg)"),
repeating-linear-gradient(
to bottom right,
red 0 20px,
#ff000055 20px 40px,
transparent 40px 60px
);
}
CSS 마스킹의 무궁무진한 활용법을 더 알아보고 싶다면 아래 링크들을 꼭 확인해 보세요!
CSS Mask 속성은 여러 장의 이미지(특히 그라디언트와 SVG)를 레고 블록처럼 쌓고, 더하고, 빼면서 포토샵 못지않은 환상적인 그래픽 효과를 만들어낼 수 있는 최고의 무기입니다. 오늘 배운 내용들을 바탕으로 텍스트나 이미지에 멋진 효과를 직접 실험해 보세요! 질문이 있으시면 언제든 편하게 남겨주시고요. 수고하셨습니다!