DisclosureBadge | CoUI
본문으로 건너뛰기
LogoCoUI

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#

사용 시기 (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)#

항목FlutterWeb
클래스명DisclosureBadgeDisclosureBadge
루트 DecoratedBox / CustomPaint <span> (UiComponent)
dashed 외곽선 BorderStyle 에 dash 가 없어 CustomPainter 가 직접 그림 ( CoreDashedBorder 토큰) CSS border-dashed (Tailwind 유틸)
마커 숨김ExcludeSemanticsaria-hidden="true"
chrome emit scheme.resolve(...).toValue() 로 build 시점 해석 토큰은 Tailwind class, 오버라이드는 inline ( border-width 는 var(--coui-stroke-width-N) )
  • 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',
    ),
  ],
)