DisclosureBadge
근거를 지금 열 수 있는지, 열 수 없다면 어떤 조건인지를 표시하는 정적 배지 컴포넌트
목차
하나의 근거 자료를 지금 열어볼 수 있는지 한 단어로 표시하는 정적 배지입니다. 채움 없이 외곽선과 텍스트만 쓰며, 누를 수 있는 것처럼 보이지 않도록 호버·포커스·커서 피드백을 일절 갖지 않습니다.
세 가지 kind 가 같은 질문에 서로 다르게 답합니다.
| kind | 지금 열 수 있나 | 외곽선 | 마커 점 |
|---|---|---|---|
open | 열 수 있다 | solid | 없음 |
commitment | 지금은 아니지만 약속됐다 | solid | 있음 |
private | 열 수 없고 앞으로도 안 열린다 | dashed | 없음 |
외곽선 패턴과 마커 점은 CoreDisclosureKind 에 붙어 있어 어떤 테마도 지울 수 없습니다. 색맹 독자와 흑백 출력물에서도 세 상태가 구분되어야 하기 때문입니다 (WCAG 1.4.1).
기본 사용법 (Basic Usage)#
// 지금 열 수 있는 근거
DisclosureBadge(
kind: CoreDisclosureKind.open,
label: '근거 공개',
)
// 아직 못 열지만 계약 시점에 넘긴다
DisclosureBadge(
kind: CoreDisclosureKind.commitment,
label: '계약 시 제공',
)
// 열 수 없고 앞으로도 안 열린다 — 라벨에 사유 필수
DisclosureBadge(
kind: CoreDisclosureKind.private,
label: '비공개 · NDA',
)
// 지금 열 수 있는 근거
DisclosureBadge(
kind: CoreDisclosureKind.open,
label: '근거 공개',
)
// 아직 못 열지만 계약 시점에 넘긴다
DisclosureBadge(
kind: CoreDisclosureKind.commitment,
label: '계약 시 제공',
)
// 열 수 없고 앞으로도 안 열린다 — 라벨에 사유 필수
DisclosureBadge(
kind: CoreDisclosureKind.private,
label: '비공개 · NDA',
)
Live Preview#
코드 보기
class DisclosureBadgeDefaultExample extends StatelessComponent {
const DisclosureBadgeDefaultExample({super.key});
@override
Component build(BuildContext context) {
return const DisclosureBadge(
kind: CoreDisclosureKind.open,
label: '근거 공개',
);
}
}
class DisclosureBadgeDefaultExample extends StatelessWidget {
const DisclosureBadgeDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return const DisclosureBadge(
kind: CoreDisclosureKind.open,
label: '근거 공개',
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 실적·레퍼런스 목록에서 각 항목의 근거 자료를 지금 열 수 있는지 표시할 때
- 근거를 아직 못 열지만 계약·킥오프 시점에 제공한다고 약속할 때
- NDA·보안 등의 이유로 영원히 비공개인 항목을 그 사유와 함께 밝힐 때
- 실사 담당자가 흑백으로 출력해 검토하는 문서에 공개 상태를 표시할 때
대신 다른 컴포넌트를 사용하세요:
- Link: 근거를 실제로 여는 동작이 필요할 때. 이 배지는 상태 표시일 뿐 누를 수 없습니다.
- Chip: 선택·제거 가능한 필터·태그일 때
- Badge: 공개 상태 kind 없이 일반 상태 문자열·개수·점을 표시할 때
- ValidationBadge: 폼 입력값의 유효성 검증 결과를 표시할 때
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
kind |
CoreDisclosureKind |
필수 |
세 공개 상태 중 하나 (
open
/
commitment
/
private
). 외곽선 패턴과 마커 점을 함께 나르므로 색 없이도 구분되는 근거가 됩니다.
|
label |
String |
필수 |
공개 상태를 나타내는 텍스트. 빈 문자열은 assert 로 막힙니다 — 글자 없는 외곽선은 의미를 색과 모양에만 맡기는 것이라서입니다.
private
는 여기에 사유를 적습니다.
|
labelMaxLines |
int? |
null |
라벨이 넘칠 때까지 허용하는 줄 수. null 은 현행대로 줄 수를 제한하지 않습니다 |
labelOverflow |
CoreTextOverflow? |
null |
labelMaxLines
에 도달한 라벨의 넘침 처리 (
clip
/
fade
/
ellipsis
/
visible
)
|
disclosureBadgeStyle |
CoreDisclosureBadgeStyle? |
null |
모든 chrome 이 지나는 단일 슬롯 (아래 표 참고) |
스타일 시스템 (Style System)#
CoreDisclosureBadgeStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
foregroundColor |
CoreColor? |
Label + marker colour override.
null
defers to
defaultsByKind[kind]!.foreground
. This field
deliberately has no defaultForegroundColor
, and the absence is the design. A flat default would have to answer for all three kinds at once, and the moment it does,
open
/
commitment
/
private
render in one colour — which is the exact failure this component exists to prevent (WCAG 1.4.1: the kinds must stay distinguishable without colour, and colour is the
first
signal a reader reaches for). The per-kind map is the default; it is keyed by [CoreDisclosureKind] so no theme can collapse the three.
|
borderColor |
CoreColor? |
Outline colour override. null defers to defaultsByKind[kind]!.borderColor. |
borderWidth |
double? |
Outline stroke width override. null defers to [defaultBorderWidth]. |
borderRadius |
CoreBorderRadius? |
Corner radius override. null defers to [defaultBorderRadius]. |
padding |
CoreEdgeInsets? |
Padding around the label. null defers to [defaultPadding]. |
labelStyle |
CoreTextStyle? |
Label typography override. null defers to [defaultLabelStyle]. |
markerSize |
double? |
Diameter of the [CoreDisclosureKind.commitment] marker dot in logical px.
null
defers to [defaultMarkerSize]. Only consumed by the kinds whose [CoreDisclosureKind.hasMarker] is
true
.
|
markerRadius |
CoreBorderRadius? |
Marker corner radius override. null defers to [defaultMarkerRadius] — a full circle. |
markerSpacing |
double? |
Gap between the marker dot and the label in logical px.
null
defers to [defaultMarkerSpacing].
|
이 슬롯이 일부러 제공하지 않는 것#
세 가지 부재는 누락이 아니라 설계입니다.
-
backgroundColor없음 — 이 배지는 외곽선과 단어일 뿐입니다. 채움은Chip을 누를 수 있는 컨트롤로 읽히게 만드는 요소이고, 이 컴포넌트는 누를 수 없습니다. - hover / pressed / focus 슬롯 없음 — 같은 이유입니다. 정적 표시에 포인터 피드백을 주면 존재하지 않는 인터랙션을 약속하게 됩니다.
-
외곽선 패턴·마커 점 슬롯 없음 — 두 값은
CoreDisclosureKind에 살아서 어떤 프로젝트 테마도 세 상태를 색 하나로 뭉갤 수 없습니다 (WCAG 1.4.1).
또한 foregroundColor 에는 짝이 되는 defaultForegroundColor 가 없습니다. 평평한 기본값 하나는 세 kind 를 한 색으로 답하게 되므로, 기본값은 kind 별 표(defaultsByKind)가 소유합니다.
빠른 오버라이드 (Chain)#
이미 만든 DisclosureBadge 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
foregroundColor 는 라벨과 마커 점을 함께 칠하므로 한 번의 오버라이드로 단어와 점이 어긋나지 않습니다.
Badge 가 가진 .primary / .surface 같은 색 단축 getter 는 의도적으로 없습니다 — 그 getter 들은
배경을 칠하는데, 이 컴포넌트에는 칠할 배경이 없습니다.
class DisclosureBadgeChainExample extends StatelessWidget {
const DisclosureBadgeChainExample({super.key});
@override
Widget build(BuildContext context) {
return const DisclosureBadge(
kind: CoreDisclosureKind.commitment,
label: '킥오프 시 제공',
).withStyle(
const CoreDisclosureBadgeStyle(
foregroundColor: CoreColor.token(CoreColors.primary),
borderColor: CoreColor.token(CoreColors.primary),
borderWidth: CoreStrokeWidth.stroke2,
borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
padding: CoreEdgeInsets.symmetric(
horizontal: CoreSpace.space8,
vertical: CoreSpace.space4,
),
labelStyle: CoreTextStyle.token(CoreTextStyles.labelMedium),
markerSize: CoreSize.size6,
markerSpacing: CoreSpace.space6,
),
);
}
}
class DisclosureBadgeChainExample extends StatelessComponent {
const DisclosureBadgeChainExample({super.key});
@override
Component build(BuildContext context) {
return const DisclosureBadge(
kind: CoreDisclosureKind.commitment,
label: '킥오프 시 제공',
).withStyle(
const CoreDisclosureBadgeStyle(
foregroundColor: CoreColor.token(CoreColors.primary),
borderColor: CoreColor.token(CoreColors.primary),
borderWidth: CoreStrokeWidth.stroke2,
borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
padding: CoreEdgeInsets.symmetric(
horizontal: CoreSpace.space8,
vertical: CoreSpace.space4,
),
labelStyle: CoreTextStyle.token(CoreTextStyles.labelMedium),
markerSize: CoreSize.size6,
markerSpacing: CoreSpace.space6,
),
);
}
}
프로젝트 단위 오버라이드#
CoreDisclosureBadgeTheme 은 style(전 배지 공통)과 kindStyles(kind 별) 두 슬롯만 가집니다. 해석 순서는 아래와 같습니다.
kind 별 디자인 시스템 기본값
→ CoreDisclosureBadgeTheme.style // 프로젝트 공통
→ CoreDisclosureBadgeTheme.kindStyles[kind]
→ widget.disclosureBadgeStyle // 인스턴스별
kind 자체는 테마가 건드릴 수 없습니다 — 프로젝트 테마가 모든 "비공개" 를 조용히 "공개" 로 바꿔 말할 수 있으면 안 되기 때문입니다.
변형 (Variants)#
Open (기본)#
지금 근거를 열어볼 수 있는 상태. solid 외곽선에 primary 계열 색을 씁니다 — 실제로 링크로 이어지는 유일한 kind 라서 이 색을 가질 자격이 있습니다.
DisclosureBadge(
kind: CoreDisclosureKind.open,
label: '근거 공개',
)
Commitment#
아직 열 수 없지만 계약 체결·킥오프 시점에 넘기기로 약속된 상태. solid 외곽선(약속은 확정됐다)에 마커 점(아직 도착하지 않았다)을 더합니다. 마커 점이 open
과 구분해 주는 두 번째 비-색상 신호입니다.
DisclosureBadge(
kind: CoreDisclosureKind.commitment,
label: '계약 시 제공',
)
Private#
열 수 없고 앞으로도 열리지 않는 상태. dashed 외곽선이며 — 끊긴 획의 틈 자체가 신호입니다 — 색은 on-surface-variant 로, 절대 primary
계열을 쓰지 않습니다. 못 여는 것을 열 수 있는 것과 같은 색으로 칠하면 독자는 눌러 본 뒤에야 그 거짓말을 알게 됩니다.
DisclosureBadge(
kind: CoreDisclosureKind.private,
label: '비공개 · NDA',
)
동작 스펙 (Behavior)#
인터랙션#
- 없음. 표시 전용입니다. 탭·클릭·호버·포커스·커서 변화가 모두 없고,
Clickable위에 서 있지 않습니다. -
Flutter 는 제스처도 상태도 없는
StatelessWidget, Web 은role/tabindex가 없는UiComponent입니다.
상태 전환#
kind를 바꾸면 외곽선 패턴·마커 점·색이 함께 바뀝니다. 셋은 같은 enum 값에서 나오므로 따로 어긋날 수 없습니다.label은 상태와 별개로 바뀔 수 있습니다 —private의 사유 문구가 항목마다 다르기 때문입니다.
애니메이션#
- 없음. 전환 효과를 주면 정적 표시가 상태 변화를 스스로 일으키는 것처럼 읽힙니다.
사용 가이드라인 (Usage Guidelines)#
✅ Do#
private 라벨에 사유를 함께 적기
DisclosureBadge(kind: CoreDisclosureKind.private, label: '비공개 · NDA')
DisclosureBadge(kind: CoreDisclosureKind.private, label: '비공개 · 고객사 요청')
왜 못 여는지가 함께 적혀야 독자가 그 판단을 검증할 수 있습니다.
❌ Don't#
맨 "비공개" 만 적기 금지
// ❌ 사유가 없으면 회피로 읽힌다
DisclosureBadge(kind: CoreDisclosureKind.private, label: '비공개')
사유 없는 "비공개" 는 실사 담당자에게 숨기는 것으로 읽힙니다. 이 컴포넌트가 막으려는 바로 그 상태입니다.
✅ Do#
약속된 자료는 commitment 로, 제공 시점을 라벨에
DisclosureBadge(kind: CoreDisclosureKind.commitment, label: '계약 시 제공')
DisclosureBadge(kind: CoreDisclosureKind.commitment, label: '킥오프 시 제공')
open 도 private 도 아닌 중간 상태를 정직하게 표현하고, 마커 점이 "아직 아니다" 를 색 없이 전달합니다.
❌ Don't#
지금 못 여는 자료를 open 으로 표시 금지
// ❌ 눌러도 아무것도 열리지 않는다
DisclosureBadge(kind: CoreDisclosureKind.open, label: '근거 공개')
open 은 "지금 링크가 살아 있다" 는 주장입니다. 열 수 없는 자료에 쓰면 독자는 클릭한 뒤에야 알게 되고, 그 시점엔 다른 표시도 믿지 않게 됩니다.
❌ Don't#
테마로 세 kind 를 같은 색으로 맞추기 금지
// ❌ 세 kind 를 한 색으로 — 외곽선 차이에만 의존하게 된다
CoreDisclosureBadgeTheme(
style: CoreDisclosureBadgeStyle(
foregroundColor: CoreColor.token(CoreColors.onSurface),
borderColor: CoreColor.token(CoreColors.outline),
),
)
외곽선 패턴과 마커 점은 남으므로 완전히 무너지지는 않지만, 색은 독자가 가장 먼저 읽는 신호입니다. 색 구분은 보강이 아니라 첫 번째 단서로 남겨 두세요.
접근성 (Accessibility)#
역할#
역할 없는 정적 텍스트 배지입니다. 버튼·링크·선택 컨트롤이 아니며, 상태 의미는
필수 label 문구가 전달합니다.
색 이외의 수단 (WCAG 1.4.1)#
-
세 kind 는 외곽선 패턴(solid / dashed)과 마커 점(있음 / 없음)으로 구분됩니다. 두 값은
CoreDisclosureKind에 붙어 있어 style 슬롯이나 테마로 지울 수 없습니다. label은 필수이며 빈 문자열이 금지됩니다. 상태는 항상 글자로도 적혀 있습니다.- 흑백 출력에서도 세 상태가 그대로 구분됩니다 — 실사 담당자가 실제로 그렇게 검토합니다.
키보드#
| 키 | 동작 |
|---|---|
| — | 없음. 포커스 대상이 아닙니다 (tabindex 없음, focusNode 없음). |
정적 표시이므로 탭 순서에 들어가지 않습니다. 근거를 여는 동작이 필요하면 배지 옆에 Link 를 따로 둡니다.
스크린 리더#
-
Flutter: 마커 점은
ExcludeSemantics로 감싸 읽히지 않습니다.label텍스트가 상태를 그대로 전달합니다. -
Web: 마커
<span>에aria-hidden="true"가 붙습니다. 점은 모양으로 반복하는 신호일 뿐이라, 읽히면 외곽선과 상태 문구 사이에 빈 정거장이 생깁니다. - 라벨 텍스트는 선택·복사가 가능합니다 (
select-none없음) — 공개 상태는 독자가 인용해 갈 수 있어야 할 정보입니다.
포커스 관리#
포커스를 받지 않으므로 진입·이탈·트랩·복귀 대상이 없습니다. 근거 링크는 호출부가 별도로 배치하고 그 링크가 포커스를 소유합니다.
알려진 제약#
kind 변경을 알리는 live region 은 없습니다. 외곽선 패턴·마커 점은 스크린
리더에 상태 이름으로 변환되지 않으므로 label 이 공개 상태와 사유를 말해야 합니다.
배지 자체로 근거를 열 수는 없습니다.
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | DisclosureBadge | DisclosureBadge |
| 루트 | DecoratedBox / CustomPaint |
<span> (UiComponent) |
| dashed 외곽선 |
BorderStyle
에 dash 가 없어
CustomPainter
가 직접 그림 (
CoreDashedBorder
토큰)
|
CSS border-dashed (Tailwind 유틸) |
| 마커 숨김 | ExcludeSemantics | aria-hidden="true" |
| chrome emit | scheme.resolve(...).toValue() 로 build 시점 해석 |
토큰은 Tailwind class, 오버라이드는 inline (
border-width
는
var(--coui-stroke-width-N)
)
|
관련 컴포넌트 (Related Components)#
- Badge: 톤 · 세기 · 꼴로 상태 · 개수 · 분류를 표시하는 범용 배지. 톤은 일반 상태(info · success …)라 공개 정도를 말할 kind 가 없고, 공개/비공개가 같은 모양이 될 수 있습니다.
- Chip: 채움과 호버를 가진 누를 수 있는 태그. DisclosureBadge 는 누를 수 없어서 이 실루엣을 빌리지 않습니다.
- ValidationBadge: 폼 입력값 검증 결과. 도메인이 다릅니다.
- Link: 근거를 실제로 여는 동작.
open배지와 함께 둡니다.
조합 예제#
// 실적 항목 한 줄 — 제목 + 공개 상태 + (열 수 있으면) 링크
Row(
children: [
Text('2025 리브랜딩 프로젝트').bodyMedium.onSurface,
Gap(size: CoreSpace.space8),
DisclosureBadge(
kind: CoreDisclosureKind.private,
label: '비공개 · NDA',
),
],
)