OutlinedContainer
border·radius·padding·shadow·blur 토큰 chrome 으로 child 를 감싸는 박스 프리미티브 (인프라)
목차
child 를 토큰 기반 chrome(border + radius + optional padding + shadow + blur)으로 감싸는 박스 프리미티브(인프라)입니다.
Card·Window·Alert·ChipInput 등 다수 컴포넌트가 이 박스 chrome 위에 쌓입니다. Web 은
<div> + Tailwind 유틸리티, Flutter 는 동일 CoreOutlinedContainerContract 필드를 읽어 양 플랫폼이 default 를 공유합니다.
Live Preview#
코드 보기
class OutlinedContainerDefaultExample extends StatelessComponent {
const OutlinedContainerDefaultExample({super.key});
@override
Component build(BuildContext context) {
return OutlinedContainer(
containerStyle: const CoreOutlinedContainerStyle(
padding: CoreEdgeInsets.all(CoreSpace.space16),
),
child: Text('테두리 컨테이너').bodyMedium,
);
}
}
class OutlinedContainerDefaultExample extends StatelessWidget {
const OutlinedContainerDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return OutlinedContainer(
containerStyle: const CoreOutlinedContainerStyle(
padding: CoreEdgeInsets.all(CoreSpace.space16),
),
child: Text('테두리 컨테이너').bodyMedium,
);
}
}
사용법#
OutlinedContainer(
containerStyle: const CoreOutlinedContainerStyle(
padding: CoreEdgeInsets.all(CoreSpace.space16),
),
child: Text('테두리 컨테이너').bodyMedium,
)
Props / Parameters#
W는 Flutter의 Widget, Web의 Component를 뜻하는 합성 슬롯 타입입니다.
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child |
W? |
null |
chrome 안에 배치되는 콘텐츠 |
containerStyle |
CoreOutlinedContainerStyle? |
null |
backgroundColor·borderColor·borderWidth·borderRadius·padding·shadow·blur 단일 진입점 |
refraction |
CoreSurfaceRefraction? |
null |
표면 굴절 요청. model 에 띠가 빛을 휘는 법칙(snell 기본 · paraxial)을 담습니다 |
role |
CoreSurfaceRole |
defaultRole |
표면 재질을 선택하는 역할. 접근성 role이 아님 |
스타일 시스템 (Style System)#
CoreOutlinedContainerStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
surfaceStyle |
CoreSurfaceStyleId? |
Material override for this surface; null inherits the surrounding material. The material — translucency, backdrop blur, chroma lift, backdrop dimming, edge sheen and refraction — is a theme axis that reaches every surface at once. A value here replaces it for this instance alone: [CoreSurfaceStyleId.defaultStyle] is an opaque, stroke-bounded face inside a glass theme, [CoreSurfaceStyleId.liquidGlass] a glass face inside an opaque one. Palette, typography, geometry and density stay the surrounding theme's. [surfaceOpacity] and [surfaceBlur] tune the fill and the blur within whichever material applies. They cannot remove one, because neither reaches the sheen, the chroma lift or the backdrop dimming. Deliberately no default: null inherits the active theme material axis; a fixed default would override that axis before the resolver sees it. |
backgroundColor |
CoreColor? |
Background fill colour override. |
backgroundGradient |
CoreGradient? |
Background gradient fill override — painted instead of [backgroundColor] when set. The border and the shadow are unaffected; [surfaceOpacity] scales every stop's alpha. No default — absence is the design. Null means a solid [backgroundColor] fill, which both platforms branch on; a constant here would turn every surface built on this primitive into a gradient. |
borderColor | CoreColor? | Border colour override. |
borderRadius |
CoreBorderRadius? |
Border radius override. |
borderWidth |
double? |
Border width override (logical px, pre-scaling). |
preserveTransparentBorder |
bool? |
Whether a fully transparent border still reserves its stated width. The default collapses an invisible border. Set this when another state paints a border and its layout must not move during the transition. For dashed and dotted borders, opting in also reserves the width when the color is visible. A
none
border ignores this option.
|
borderStyle |
CoreOutlinedContainerBorderStyle? |
Border style override (solid / dashed / dotted / none). |
dashLength |
double? |
Dash segment length when [borderStyle] is dashed (logical px).
null
→ [defaultDashLength].
|
dashSpacing |
double? |
Gap between dash segments (logical px). null → [defaultDashSpacing]. |
elevation |
CoreOutlinedContainerElevation? |
Shadow elevation override. When [boxShadow] is also set, [boxShadow] wins. |
boxShadow |
List<CoreShadowLayer>? |
Explicit shadow stack, overriding the [elevation]-derived one. The elevation enum can only name three prepared stacks, all of them soft ambient shadows. Some surface treatments are defined by shadows it cannot describe at all: a hard offset shadow has zero blur, and a raised-from-the-page look needs two layers going in opposite directions. This slot takes the stack directly so those are expressible without inventing a new enum member per look. |
shadowBaseColor |
CoreColor? |
Shadow base colour override — the tint every layer of the shadow stack is drawn in, each layer's own opacity scaling its alpha.
null
defers to [defaultShadowBaseColor]. A token follows the active scheme. A raw colour pins the cast: it is the same in every mode, which is what a surface that stands for a physical object wants. On Web the pinned cast is written as the stack's literal layers instead of through the
--shadow-*
variables a surface style bends, so a style's shadow geometry reaches it on Flutter only.
|
shadowStrengthens |
bool? |
Whether a dark theme strengthens the stack this style states — the [boxShadow] or the [elevation] preset — by
CoreShadow.darkStrength
(capped at 1).
null
defers to [defaultShadowStrengthens].
false
pins the stack's alpha at what each layer states, in every brightness: the cast is drawn the same on a light page and a dark one. That is for a surface that stands for a physical object photographed under one light (
MockupDevice
), not for a panel lifted off the page, whose cast has to read on a dark face too. The geometry a surface style bends is untouched — only the brightness's multiplier is left out. A cast the surface style contributes to a surface that states none (
controlShadow
) is the style's, not this stack, and keeps it.
|
padding |
CoreEdgeInsets? |
Inner padding override. Falls back to [defaultPadding] when null. |
surfaceOpacity |
double? |
Background opacity multiplier (0.0 – 1.0). Multiplied into the alpha channel of [backgroundColor]. |
surfaceBlur |
double? |
Backdrop blur sigma (logical px). When > 0, applies a
BackdropFilter
(Flutter) /
backdrop-filter: blur
(Web).
|
width |
double? |
Explicit width override (logical px).
No default, and must not have one.
Null is not "width unknown", it is "size to the child" — both platforms branch on it rather than reading a value. Flutter hands the null straight to the container's
width:
, where it means unconstrained; Web only writes the
width
declaration inside
if (width != null)
. A constant here would pin every surface built on this primitive —
Card
,
Alert
,
Window
, every picker panel — to one width, and nothing in the tree asks for that.
|
height |
double? |
Explicit height override (logical px). No default, and must not have one — same branch as [width], on the block axis. |
duration |
Duration? |
Animation duration for chrome property changes (color / radius / width / height).
null
or [Duration.zero] means no animation.
|
clipBehavior |
CoreClipBehavior? |
Clip behaviour override. |
동작 스펙 (Behavior)#
이 프리미티브는 child를 둘러싼 chrome만 렌더하며 선택·클릭·비활성 상태를 소유하지 않습니다. 명시적 width·height는 논리 px 단위이고 생략하면 부모 제약과 자식 크기를 따릅니다.
duration은 chrome 변경의 전환 시간(Duration)이며 clipBehavior는 모서리 밖 콘텐츠의 클리핑을 정합니다. dashed/dotted 테두리와 표면 흐림은 스타일에 따라 별도의 페인트 레이어로 합성됩니다.
표면 재질 (Material Override)#
표면의 재질(반투명 채움·배경 블러·채도·배경 어둡힘·가장자리 빛띠·굴절, 그리고 앱이 켠 서리와 몸체 음영)은 평소 테마 축이 모든 표면에 한 번에 정합니다. containerStyle.surfaceStyle
은 이 컨테이너 하나의 재질만 바꿉니다 — defaultStyle 은 유리 테마 안의 불투명한 면(평소의 외곽선 유지), liquidGlass
는 불투명 테마 안의 유리 면입니다. 팔레트·타이포·기하·밀도는 둘러싼 테마 그대로이고, null 은 둘러싼 재질을 그대로 씁니다. surfaceOpacity
/ surfaceBlur 는 재질 안에서 채움과 블러를 조절할 뿐 재질을 없애지 못합니다. 접근성 설정(투명도 줄이기·고대비)은 이 값보다 우선합니다.
OutlinedContainer(
containerStyle: const CoreOutlinedContainerStyle(
surfaceStyle: .defaultStyle,
),
child: Text('불투명한 면'),
)
이 컨테이너를 합성하는 컴포넌트(예: Card)는 자기 스타일의 surfaceStyle 을 그대로 넘깁니다.
refraction 은 이 컨테이너 하나의 굴절 요청이고 법칙도 그 요청의 model 입니다. 앱의 모든 표면이 같은 법칙으로 휘게 하려면
surfaceTuning 의 refractionModel 로 정합니다 — 자세한 규칙과 두 법칙의 차이는 서페이스 스타일
가이드에 있습니다. 굴절은 요청이라서, 그리지 못하는 엔진에서는 법칙을 정해도 표면이 휘지 않고 나머지 재질만 그립니다.
사용 가이드라인 (Usage Guidelines)#
제목·본문·미디어·액션 슬롯을 가진 완성된 콘텐츠 카드가 필요하면 Card를 선택합니다. 임의의 자식에 테두리와 표면만 얹는 저수준 조합에는 OutlinedContainer를 사용합니다.
✅ Do#
모든 chrome을 containerStyle 하나로 지정하기
OutlinedContainer(
containerStyle: const CoreOutlinedContainerStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius12),
padding: CoreEdgeInsets.all(CoreSpace.space16),
),
child: Text('내용').bodyMedium,
)
background·border·radius·padding·shadow·blur는 모두 containerStyle 단일 슬롯 하나로 흐릅니다 — 바깥에 또 다른 Container/<div>로 같은 chrome을 덧그리면 박스가 이중으로 그려집니다.
❌ Don't#
role/이름이 필요한 자리에 그대로 쓰지 않기
// ❌ role/aria 없이 대화상자·영역처럼 쓰는 컨테이너로 오용
OutlinedContainer(
child: importantDialogContent,
)
OutlinedContainer는 role도 이름도 포커스 대상도 내보내지 않는 순수 chrome 프리미티브입니다. 영역/대화상자 의미가 필요하면 소비자가 직접 Semantics/attributes로 감싸야 하고, 포커스 표시가 필요하면 FocusOutline을 별도로 합성해야 합니다.
접근성 (Accessibility)#
역할#
자체 role 이 없습니다. Flutter에는 role을 부여하는 Semantics 래퍼가 없으며 장식용 CustomPaint는 별도의 의미 노드를 만들지 않습니다. Web은
<div>에 호출자 속성만 싣고 dashed/dotted 테두리용 <svg>에 aria-hidden="true"를 붙입니다. landmark·region·group·aria-label
중 어느 것도 양 플랫폼에서 자동으로 나오지 않습니다.
읽는 사람에게 이는 이 박스가 접근성 트리에 존재하지 않는 것과 같다는 뜻입니다 — 이 표면에 이름이나 영역 의미가 필요하면 소비자가 직접 부여해야 합니다.
키보드#
처리하는 키가 없습니다. Web 은 호출자 eventHandlers 를 그대로 전달하지만 자체적으로는 아무 핸들러도 설치하지 않습니다.
포커스 관리#
포커스 대상이 아니며 어느 플랫폼에서도 traversal 정지점이 되지 않습니다. Flutter 에 FocusNode / Focus /
autofocus 가 없고 Web 에 tabindex 가 없습니다. 포커스 링도 그리지 않습니다 — 이 컴포넌트의 style 은 border·radius·shadow·blur 만 해석합니다. 포커스 표시가 필요하면
FocusOutline 을 함께 쓰세요.
스크린 리더#
자기 몫으로 안내되는 것이 없고, 리더는 child로 통과합니다. Flutter의 장식용 CustomPaint는 의미 노드를 만들지 않고 Web의 SVG는
aria-hidden="true"이므로 테두리 스타일이 읽히지 않습니다.
알려진 제약#
- 순수 chrome 이라 role·이름·설명·포커스 가능성이 전부 없습니다. 이 표면을 region / group / dialog 로 만들어야 한다면 소비자가 직접 주입해야 합니다.
-
주입 통로가 플랫폼마다 다릅니다 — Web은
attributes:passthrough로 접근성role/aria-*를 얹을 수 있습니다. Flutter에서는Semantics로 감싸야 합니다. 생성자의CoreSurfaceRole role은 재질 선택용이며 접근성 역할을 부여하지 않습니다. -
전환 애니메이션에
aria-busy나 live region 처리가 없습니다 — Flutter 의AnimatedContainerchrome 전환은 보조기술에 아무것도 알리지 않습니다. 전환의 모션 축 자체는 전역 접근성 축 에서 다룹니다.
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| chrome 전환 | AnimatedContainer | 루트 CSS 전환 |
| dashed/dotted 테두리 | CustomPaint 레이어 |
aria-hidden SVG 테두리 레이어 |
| 표면 흐림 | 합성된 SurfaceBlur | CSS backdrop-filter |
| 접근성 주입 | 호출자가 Semantics로 감쌈 | 루트 attributes로 전달 |
| 재질 오버라이드 | surfaceStyle 의 재질을 컨테이너가 직접 그림 |
루트가 문서 마커 대신 coui-material-surface coui-material-<id> 를 달아 그 프리셋의 규칙만 선택됨 |
| 서브트리 단위 재질 | SurfaceTheme |
대응 없음 — 인스턴스 단위 surfaceStyle 만 있음 |
| 품질 적응 | 앱 루트의 adaptiveQuality 가 정한 단계가 재질에 접혀 들어옴 — 컨테이너는 단계를 읽지 않음 |
문서 속성
data-coui-quality
를 스타일시트(
solid
의 불투명 채움)와 굴절 호스트(
noRefraction
에서 굴절 필터를 뗌)가 읽음
|
Flutter와 Web 모두 dashed/dotted 테두리의 장식만 접근성 트리에서 제외하고 자식 콘텐츠는 유지합니다.
호출자가 refraction 을 직접 진술한 컨테이너는 품질 단계를 따르지 않습니다. 스타일의 진술이 아니라 호출자의 진술이라서입니다. 단계가 solid
이면 backdrop 패스가 없어 구부릴 뒤도 없습니다. 자세한 것은 서페이스 스타일 의 품질 적응을 보세요.