임동욱 임동욱 08-18
feat: 체크박스·라디오에 hideLabel 추가
표의 선택 열처럼 네모(동그라미)만 필요한 자리가 있는데 글자를 감출 방법이 없었다.
표 예시에서는 카탈로그 전용 CSS로 가려 두었던 것을 컴포넌트가 갖게 한다.

`display: none`으로 지우지 않는다 — 그러면 접근성 트리에서도 빠져 이름 없는 조작기가
된다. 자리만 없애고 DOM에는 남긴다. 루트가 `
@e04e73e0747ea28a1176a82975255c593062fae5
@fox/core/components/fox-checkbox/fox-checkbox.tsx
--- @fox/core/components/fox-checkbox/fox-checkbox.tsx
+++ @fox/core/components/fox-checkbox/fox-checkbox.tsx
@@ -39,6 +39,11 @@
    * 걷어내고 그 자리를 대신한다(`FoxInput`과 같은 방식).
    */
   onChange?: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;
+  /**
+   * 글자를 눈에서만 감춘다 — 표의 선택 열처럼 네모(동그라미)만 필요한 자리에 쓴다.
+   * DOM에는 남으므로 스크린리더는 그대로 읽는다. `description`도 함께 감춰진다.
+   */
+  hideLabel?: boolean;
   /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
   hidden?: boolean;
   /** 배치 조정용. */
@@ -68,6 +73,7 @@
   description,
   indeterminate = false,
   onChange,
+  hideLabel = false,
   hidden = false,
   className,
   ref,
@@ -103,7 +109,7 @@
   };
 
   return (
-    <label className={cx("fox-checkbox", SIZE_CLASS[size], className)}>
+    <label className={cx("fox-checkbox", SIZE_CLASS[size], hideLabel && "fox-checkbox--hide-label", className)}>
       {/* 형제 선택자(`~`)가 뒤따르는 표시와 본문에 닿아야 하므로 맨 앞에 둔다. */}
       <input
         {...rest}
@fox/core/components/fox-radio/fox-radio.tsx
--- @fox/core/components/fox-radio/fox-radio.tsx
+++ @fox/core/components/fox-radio/fox-radio.tsx
@@ -25,6 +25,11 @@
    * 그 자리를 대신한다(`FoxInput`과 같은 방식).
    */
   onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
+  /**
+   * 글자를 눈에서만 감춘다 — 표의 선택 열처럼 네모(동그라미)만 필요한 자리에 쓴다.
+   * DOM에는 남으므로 스크린리더는 그대로 읽는다. `description`도 함께 감춰진다.
+   */
+  hideLabel?: boolean;
   /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
   hidden?: boolean;
   /** 배치 조정용. */
@@ -54,6 +59,7 @@
   label,
   description,
   onChange,
+  hideLabel = false,
   hidden = false,
   className,
   ...rest
@@ -71,7 +77,7 @@
   };
 
   return (
-    <label className={cx("fox-radio", SIZE_CLASS[size], className)}>
+    <label className={cx("fox-radio", SIZE_CLASS[size], hideLabel && "fox-radio--hide-label", className)}>
       {/* 형제 선택자(`~`)가 뒤따르는 표시와 본문에 닿아야 하므로 맨 앞에 둔다. */}
       <input {...rest} type="radio" className="fox-radio__input" onChange={handleChange} />
       <FoxRadioIcon size={size} />
@fox/dev-test/component-registry.tsx
--- @fox/dev-test/component-registry.tsx
+++ @fox/dev-test/component-registry.tsx
@@ -490,7 +490,7 @@
         <FoxTableCell width={64}>
           <FoxCheckbox
             size="md"
-            className={styles.checkboxOnly}
+            hideLabel
             label="전체 선택"
             checked={allOn}
             indeterminate={selected.length > 0 && !allOn}
@@ -514,7 +514,7 @@
           <FoxTableCell label="선택">
             <FoxCheckbox
               size="md"
-              className={styles.checkboxOnly}
+              hideLabel
               label={`${n}번째 줄 선택`}
               checked={selected.includes(n)}
               onChange={() => toggle(n)}
@@ -1312,6 +1312,16 @@
       "Figma 시안(radio 180:278)  동그란 표시 + 라벨 + 부가설명을 묶은 라디오 한 칸입니다.",
     variants: [
       {
+        label: "글자 감추기 (hideLabel) — 표의 선택 열처럼 상자만 필요할 때",
+        node: (
+          <div className={styles.demoRow}>
+            <FoxRadio hideLabel label="라디오 선택" />
+            <FoxRadio hideLabel label="라디오 선택 (켜짐)" defaultChecked />
+            <FoxRadio label="비교용 — 글자가 보입니다" />
+          </div>
+        ),
+      },
+      {
         label: "size-lg",
         node: (
           <div className={styles.demoStack}>
@@ -1928,6 +1938,16 @@
       "Figma 시안(checkbox 108:469) 네모 표시 + 라벨 + 부가설명을 묶은 체크박스 한 칸입니다. 루트가 <label>이라 라벨이나 설명을 눌러도 켜집니다. 라디오와 달리 name이 같아도 서로를 끄지 않아 여러 개를 동시에 고를 수 있고, 폼 전송 시 고른 것들이 함께 나갑니다. ⚠️ 부분선택은 HTML 속성이 아니라 DOM 프로퍼티라 마크업만으로는 켤 수 없습니다 — 컴포넌트가 대신 넣어 줍니다.",
     variants: [
       {
+        label: "글자 감추기 (hideLabel) — 표의 선택 열처럼 상자만 필요할 때",
+        node: (
+          <div className={styles.demoRow}>
+            <FoxCheckbox hideLabel label="체크박스 선택" />
+            <FoxCheckbox hideLabel label="체크박스 선택 (켜짐)" defaultChecked />
+            <FoxCheckbox label="비교용 — 글자가 보입니다" />
+          </div>
+        ),
+      },
+      {
         label: "여러 개를 동시에 고를 수 있다 (같은 name이어도 서로 끄지 않는다)",
         node: (
           <div className={styles.demoStack}>
@fox/dev-test/dev-test.module.scss
--- @fox/dev-test/dev-test.module.scss
+++ @fox/dev-test/dev-test.module.scss
@@ -510,13 +510,3 @@
     color: fox.color(icon-neutral-default);
   }
 }
-
-// 표의 체크 열처럼 상자만 필요한 자리. FoxCheckbox에 라벨을 감추는 프롭이 없어
-// 카탈로그에서만 이렇게 가린다 — 이름은 남으므로 스크린리더는 그대로 읽는다.
-.checkboxOnly :global(.fox-checkbox__body) {
-  position: absolute;
-  inline-size: fox.border(1);
-  block-size: fox.border(1);
-  overflow: hidden;
-  clip-path: inset(50%);
-}
@fox/styles/_fox-checkbox.scss
--- @fox/styles/_fox-checkbox.scss
+++ @fox/styles/_fox-checkbox.scss
@@ -121,3 +121,18 @@
 .fox-checkbox__input:disabled ~ .fox-checkbox__body .fox-checkbox__description {
   color: fox.color(font-neutral-disabled);
 }
+
+// ── 글자 감추기 ───────────────────────────────────────────────────────────────
+// `display: none`으로 지우면 접근성 트리에서도 빠져 이름 없는 조작기가 된다. 그래서
+// 자리만 없애고 DOM에는 남긴다 — 루트가 `<label>`이라 이 글자가 곧 이름이다.
+.fox-checkbox--hide-label {
+  gap: 0;
+
+  > .fox-checkbox__body {
+    position: absolute;
+    inline-size: fox.border(1);
+    block-size: fox.border(1);
+    overflow: hidden;
+    clip-path: inset(50%);
+  }
+}
@fox/styles/_fox-radio.scss
--- @fox/styles/_fox-radio.scss
+++ @fox/styles/_fox-radio.scss
@@ -113,3 +113,18 @@
 .fox-radio__input:disabled ~ .fox-radio__body .fox-radio__description {
   color: fox.color(font-neutral-disabled);
 }
+
+// ── 글자 감추기 ───────────────────────────────────────────────────────────────
+// `display: none`으로 지우면 접근성 트리에서도 빠져 이름 없는 조작기가 된다. 그래서
+// 자리만 없애고 DOM에는 남긴다 — 루트가 `<label>`이라 이 글자가 곧 이름이다.
+.fox-radio--hide-label {
+  gap: 0;
+
+  > .fox-radio__body {
+    position: absolute;
+    inline-size: fox.border(1);
+    block-size: fox.border(1);
+    overflow: hidden;
+    clip-path: inset(50%);
+  }
+}
Add a comment
List