민혜린 민혜린 08-19
디자인시스템 FoxStatusIndicator 추가
@47ca2b94111b2907fa11a2a7b0235648e600ba87
 
@fox/core/components/fox-status-indicator/fox-status-indicator.tsx (added)
+++ @fox/core/components/fox-status-indicator/fox-status-indicator.tsx
@@ -0,0 +1,71 @@
+import type { ReactNode, Ref } from "react";
+import { cx } from "../../utils";
+
+/** `FoxAlert`·`FoxHelperText`와 같은 다섯 계열이다 — 시안이 같은 축을 쓴다. */
+export type FoxStatusIndicatorType =
+  | "default"
+  | "information"
+  | "success"
+  | "warning"
+  | "danger";
+
+export interface FoxStatusIndicatorProps {
+  /** 점과 글자의 색이 여기서 갈린다. */
+  type?: FoxStatusIndicatorType;
+  /** 상태를 나타내는 글자. */
+  label: ReactNode;
+  /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
+  hidden?: boolean;
+  id?: string;
+  /** 배치 조정용. 모양이 달라야 하면 여기 말고 `type`을 쓴다. */
+  className?: string;
+  ref?: Ref<HTMLSpanElement>;
+}
+
+/** `Record`로 고정해 계열을 추가하면 항목 누락이 타입 에러가 되게 한다. */
+const TYPE_CLASS: Record<FoxStatusIndicatorType, string> = {
+  default: "fox-status-indicator--default",
+  information: "fox-status-indicator--information",
+  success: "fox-status-indicator--success",
+  warning: "fox-status-indicator--warning",
+  danger: "fox-status-indicator--danger",
+};
+
+/**
+ * @fox 상태 표시. 작은 점 하나와 글자로 상태를 알린다. 크기 수식어가 없다 — 시안에 하나뿐이다.
+ *
+ * **점은 장식이다**(`aria-hidden`). 상태의 의미는 글자가 전부 담는다 — 색만으로 뜻을 전하면
+ * 색을 구분하지 못하는 사용자에게는 아무것도 전달되지 않기 때문이고, 그래서 `label`이
+ * 선택값이 아니라 필수값이다.
+ *
+ * 값이 바뀌는 것을 소리로 알려야 하는 자리(예: 처리 중 → 완료)라면 감싼 쪽이 live region을
+ * 만든다 — 이 조각은 상태를 갖지 않으므로 스스로 알리지 않는다.
+ *
+ * 상호작용이 없어 `"use client"`가 아니다 — 서버 컴포넌트로 렌더된다.
+ *
+ * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
+ * (또는 개별 파티셜)로 한 번 불러와야 한다.
+ */
+export function FoxStatusIndicator({
+  type = "default",
+  label,
+  hidden = false,
+  id,
+  className,
+  ref,
+}: FoxStatusIndicatorProps) {
+  if (hidden) {
+    return null;
+  }
+
+  return (
+    <span
+      ref={ref}
+      id={id}
+      className={cx("fox-status-indicator", TYPE_CLASS[type], className)}
+    >
+      <span className="fox-status-indicator__dot" aria-hidden="true" />
+      <span className="fox-status-indicator__label">{label}</span>
+    </span>
+  );
+}
 
@fox/core/components/fox-status-indicator/index.ts (added)
+++ @fox/core/components/fox-status-indicator/index.ts
@@ -0,0 +1,5 @@
+export {
+  FoxStatusIndicator,
+  type FoxStatusIndicatorProps,
+  type FoxStatusIndicatorType,
+} from "./fox-status-indicator";
@fox/core/components/index.ts
--- @fox/core/components/index.ts
+++ @fox/core/components/index.ts
@@ -39,6 +39,7 @@
 export * from "./fox-select-option-item";
 export * from "./fox-select-text";
 export * from "./fox-spinner";
+export * from "./fox-status-indicator";
 export * from "./fox-tab";
 export * from "./fox-tab-item";
 export * from "./fox-table";
@fox/dev-test/component-registry.tsx
--- @fox/dev-test/component-registry.tsx
+++ @fox/dev-test/component-registry.tsx
@@ -53,6 +53,7 @@
 import { FoxTab } from "../core/components/fox-tab";
 import { FoxTabItem } from "../core/components/fox-tab-item";
 import { FoxSpinner } from "../core/components/fox-spinner";
+import { FoxStatusIndicator } from "../core/components/fox-status-indicator";
 import { FoxChip, type FoxChipItem } from "../core/components/fox-chip";
 import { FoxChipArea } from "../core/components/fox-chip-area";
 import { FoxSelectText } from "../core/components/fox-select-text";
@@ -3418,4 +3419,32 @@
       },
     ],
   },
+  {
+    id: "fox-status-indicator",
+    name: "FoxStatusIndicator",
+    description:
+      "Figma 시안(status-indicator 404:6328) 글자로 상태를 알립니다. 점은 장식이라 aria-hidden이고 뜻은 글자가 전부 담습니다 — 그래서 label이 선택값이 아니라 필수값입니다. 색만으로 상태를 전하면 색을 구분하지 못하는 사용자에게는 아무것도 전달되지 않습니다.",
+    variants: [
+      {
+        label: "계열 5종",
+        node: (
+          <>
+            <FoxStatusIndicator label="시작전, 중립적" />
+            <FoxStatusIndicator type="danger" label="중단, 실패, 위험" />
+            <FoxStatusIndicator type="information" label="진행 중" />
+            <FoxStatusIndicator type="warning" label="잠재적 위험, 주의" />
+            <FoxStatusIndicator type="success" label="진행중, 처리중간단계" />
+          </>
+        ),
+      },
+      {
+        label: "inline-flex라 글줄 안에 그대로 놓입니다",
+        node: (
+          <p style={{ margin: 0 }}>
+            현재 상태는 <FoxStatusIndicator type="success" label="정상" /> 입니다.
+          </p>
+        ),
+      },
+    ],
+  },
 ];
 
@fox/styles/_fox-status-indicator.scss (added)
+++ @fox/styles/_fox-status-indicator.scss
@@ -0,0 +1,88 @@
+// FoxStatusIndicator — 시안: 통합관리자페이지 디자인시스템 Figma status-indicator
+//
+// 작은 점 하나와 글자로 상태를 알린다. 크기 수식어가 없다 — 시안에 하나뿐이다.
+//
+// 마크업 계약 (React 밖 소비자용):
+//   <span class="fox-status-indicator fox-status-indicator--success">
+//     <span class="fox-status-indicator__dot"></span>
+//     <span class="fox-status-indicator__label">완료</span>
+//   </span>
+//
+// 점은 비어 있는 요소다 — 배경색만으로 그린다. 뜻은 글자가 담고 점은 장식이라
+// 마크업에서 `aria-hidden`이 붙는다.
+//
+// 계열은 **점의 배경과 글자색을 각각 다른 토큰으로** 바꾼다. 점은 `element-*`(칠하는 면),
+// 글자는 `font-*`(읽는 글자)다 — 같은 계열이라도 명도가 달라 시안이 둘을 나눠 준다.
+// 기본은 점이 `element-neutral-default`(회색 면), 글자가 `font-neutral-default`다.
+//
+// 토큰이 아닌 값:
+//   - line-height / letter-spacing: 시안 텍스트 스타일 label/md의 값이나 Figma가 변수로
+//     export하지 않는다. 시안의 -0.375px은 15px 기준이라 `-0.025em`으로 적는다
+//     (다른 컴포넌트와 같은 근거). line-height는 시안이 100%라 `1`이다 — 점과 글자가 한 줄에
+//     서는 조각이라 줄 간격을 두지 않는다.
+//   - display / align-items / aspect-ratio / flex-shrink: 구조 규칙.
+
+@use "@fox/styles/abstracts" as fox;
+
+.fox-status-indicator {
+  display: inline-flex;
+  align-items: center;
+  gap: fox.gap(2);
+
+  color: fox.color(font-neutral-default);
+  font-family: fox.font-family(body);
+  font-size: fox.font-size(label-md);
+  font-weight: fox.font-weight(regular);
+  line-height: 1;
+  letter-spacing: -0.025em;
+
+  // 6px 정원. 글자가 길어 줄이 좁아져도 찌그러지지 않게 크기를 못박는다.
+  &__dot {
+    flex-shrink: 0;
+    inline-size: fox.number(4);
+    block-size: fox.number(4);
+    aspect-ratio: 1 / 1;
+    border-radius: fox.radius(max);
+    background: fox.color(element-neutral-default);
+  }
+
+  // ── 계열 ────────────────────────────────────────────────────────────────────
+  // 글자색은 블록에서 상속되므로 루트에 주고, 점은 자기 배경을 따로 받는다.
+  //
+  // `default`는 블록 규칙이 곧 그 계열의 모양이라 따로 덮을 것이 없다.
+  &--default {
+    // 시안 그대로 — 블록에 있는 값이 전부다.
+  }
+
+  &--information {
+    color: fox.color(font-system-information);
+  }
+
+  &--information &__dot {
+    background: fox.color(element-system-information-strong);
+  }
+
+  &--success {
+    color: fox.color(font-system-success);
+  }
+
+  &--success &__dot {
+    background: fox.color(element-system-success-strong);
+  }
+
+  &--warning {
+    color: fox.color(font-system-warning);
+  }
+
+  &--warning &__dot {
+    background: fox.color(element-system-warning-strong);
+  }
+
+  &--danger {
+    color: fox.color(font-system-danger);
+  }
+
+  &--danger &__dot {
+    background: fox.color(element-system-danger-strong);
+  }
+}
@fox/styles/components.scss
--- @fox/styles/components.scss
+++ @fox/styles/components.scss
@@ -52,5 +52,6 @@
 @use "fox-tab";
 @use "fox-tab-item";
 @use "fox-spinner";
+@use "fox-status-indicator";
 @use "fox-toast";
 @use "fox-toggle-quantity";
(파일 끝에 줄바꿈 문자 없음)
Add a comment
List