Mask
자식을 지정된 모양으로 클리핑하는 마스크 컴포넌트
목차
Mask는 자식을 circle, squircle, heart, hexagon,
star, triangle 등 15종 모양으로 클리핑합니다. Flutter는 ClipPath와 커스텀 CustomClipper로 경로를 계산하고, Web은 daisyUI
mask-* CSS 클래스를 사용합니다.
기본 사용법#
SizedBox(
width: CoreSpace.space64,
height: CoreSpace.space64,
child: Mask(
shape: .hexagon,
child: Container(color: Theme.of(context).colorScheme.primary),
),
)
Mask(
shape: .hexagon,
child: div(
[],
classes: 'w-${CoreSpace.scale.space64} h-${CoreSpace.scale.space64} '
'bg-\${cs.primary}',
),
)
Live Preview#
코드 보기
class MaskDefaultExample extends StatelessComponent {
const MaskDefaultExample({super.key});
@override
Component build(BuildContext context) {
final cs = context.theme.colorScheme;
return Mask(
child: div(
[],
classes: 'w-${CoreSpace.scale.space64} h-${CoreSpace.scale.space64} '
'bg-${cs.primary}',
),
);
}
}
class MaskDefaultExample extends StatelessWidget {
const MaskDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: CoreSpace.space64,
height: CoreSpace.space64,
child: Mask(
child: Container(color: Theme.of(context).colorScheme.primary.toValue()),
),
);
}
}
사용 시기#
- 아바타처럼 이미지를 특정 모양으로 잘라야 할 때
- 장식용 도형 배경
- 아이콘/배지에 독특한 모양을 적용할 때
Props / Parameters#
W 는 Flutter의 Widget, Web의 Jaspr Component 입니다.
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | W | 필수 | 클리핑 대상 콘텐츠 |
shape |
CoreMaskShape |
squircle |
마스크 모양 |
half |
CoreMaskHalf |
none |
절반 잘라내기 (none / first / second) |
스타일 시스템 (Style System)#
CoreMaskStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
shape |
CoreMaskShape? |
Mask shape. null defers to [defaultShape]. |
half |
CoreMaskHalf? |
Half-crop. null defers to [defaultHalf]. |
지원되는 shape#
circle, squircle, square, heart, hexagon,
hexagon2, decagon, pentagon, diamond, star,
star2, triangle, triangle2, triangle3, triangle4.
동작 스펙 (Behavior)#
- shape로 child의 그리기 영역을 클립합니다. square + half=none은 Flutter에서 child를 바로 반환하고, 다른 조합은 모양 클립에 선택적 절반 클립을 겹칩니다.
- half의 first는 위쪽 절반, second는 아래쪽 절반입니다. 클립은 새 콘텐츠를 만들거나 상태·레이아웃 크기를 절반으로 바꾸는 기능이 아닙니다.
- 자체 콜백·포커스·비활성 상태는 없습니다. 사용자 이미지·이니셜과 폴백까지 필요하면 Avatar, 임의 콘텐츠의 모양만 자르면 Mask입니다.
사용 가이드라인 (Usage Guidelines)#
✅ Do#
순수 장식용 클리핑에 사용
Mask(
shape: .circle,
child: Image.network(avatarUrl),
)
Mask 는 child 의 semantics 를 그대로 통과시킵니다 (자체 role/aria 없음) — 아바타 같은 이미지 크롭에 적합하며, 스크린 리더는 마스킹 여부와 무관하게 child 를 동일하게 안내합니다.
❌ Don't#
마스크 모양을 클릭 가능 영역의 경계로 신뢰하지 않기
// ❌ Web에서는 별 모양 바깥 영역도 그대로 클릭됩니다
Mask(
shape: .star,
child: Button(onPressed: handleTap, child: const Text('Play')),
)
Flutter ClipPath 는 모양 바깥 포인터를 거부하지만, Web 은 mask-image 로 paint 만 클리핑하므로(half 가 아닌 한) 사각형 전체가 클릭 가능합니다. 마스크된 인터랙티브 요소는 플랫폼별로 히트 영역이 달라집니다.
접근성 (Accessibility)#
역할#
양 플랫폼 모두 자체 role 이나 semantics 를 전혀 내보내지 않습니다. Flutter 는 ClipPath + 커스텀
CustomClipper 만 반환하고 Semantics 호출이 없으며, Web 은 co-mask 클래스와 inline mask CSS 만 실은 평범한
<div> 로 configureAttributes 를 override 하지 않아 role / aria-*
가 하나도 붙지 않습니다.
즉 child 자신의 semantics 가 안내의 전부입니다. 마스크된 영역에 이름이나 설명이 필요하면 Mask
가 아니라 child 쪽에 붙여야 합니다.
키보드#
처리하는 키가 없습니다. Web 생성자는 onKeyDown / onKeyUp 조차 노출하지 않고 id / classes
/ css / attributes / eventHandlers 만 받습니다.
포커스 관리#
포커스 대상이 아닙니다. Flutter 에 FocusNode / Focus 가 없고 Web 에 tabindex 가 없어 어느 플랫폼에서도 traversal 정지점이 되지 않습니다.
스크린 리더#
child 가 그대로 통과합니다. 클리핑은 Web 에서 paint 단계, Flutter 에서 render 단계에 일어날 뿐 접근성 트리를 건드리지 않으므로, 마스킹된 아바타·이미지는 마스킹하지 않았을 때와
똑같이 안내됩니다.
알려진 제약#
-
hit-test 가 플랫폼마다 다릅니다 — 마스크된 자식이 인터랙티브할 때 실제 문제가 됩니다. Flutter 의
ClipPath는 모양 바깥 포인터를 거부하지만, Web 은mask-image로 잘라내므로 paint 전용이라 사각형 전체가 그대로 클릭 가능합니다 (half일 때만clip-path가 함께 붙습니다). 같은 마스크된 버튼이라도 포인터/터치 타겟이 플랫폼별로 달라지므로, 마스크 모양을 히트 영역의 경계로 신뢰하지 마세요. -
장식으로 표시할 수단이 없습니다 —
aria-hidden도, decorative 플래그도, alt 텍스트 슬롯도 없습니다. 순수 장식 도형이라면 소비자가child쪽에서 접근성 트리 밖으로 빼야 하고, 정보를 담은 마스크 이미지라면child에 직접 이름을 붙여야 합니다.
이 컴포넌트가 직접 다루지 않는 대비·모션 등 전역 축은 전역 접근성 축 에서 한 곳으로 관리됩니다.
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 모양 클립 | CustomClipper의 Path | resolver의 CSS mask / clip-path |
| 절반 | 모양 클립 밖 ClipRect | CSS 마스크·클립 |
| 정사각 기본 경로 | child 그대로 | 속성 전달을 위한 div 래퍼 |
| 콘텐츠 의미 | child 의미 유지 | child DOM 의미 유지 |