From 18c4c3aad2461f6edb5ae693b733ead5b4228be5 Mon Sep 17 00:00:00 2001 From: touyou <465697+touyou@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:38:34 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20refactor:=20Input=20/=20Textarea?= =?UTF-8?q?=20/=20Tag=20=E3=82=92=20Figma=20=E6=BA=96=E6=8B=A0=E3=81=AE?= =?UTF-8?q?=E6=96=B0=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E3=81=B8=E7=A7=BB?= =?UTF-8?q?=E8=A1=8C=EF=BC=88beta=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit goodpatch/sparkle-design#298 のフェーズ2。Figma に刷新済みページがある コンポーネントのうち、Input / Textarea / Tag を移行する。値はすべて Figma の variable binding から取得している。 Input / Textarea(枠線・背景。**値はすべて据え置き**): - `border-neutral-500` → `border-border-neutral-extra-high-enabled` - `hover:border-neutral-600` → `hover:border-border-neutral-extra-high-hover` - `border-neutral-200` → `border-border-neutral-extra-high-disabled` - `bg-neutral-50`(disabled) → `bg-surface-neutral-middle-disabled` - negative 側も同じ構造で `border/negative/extra-high/{enabled,hover,disabled}` へ - Figma 2876:24123 / 24151 / 24137 / 24130 / 24158 / 24144 準拠 Input / Textarea(文字色。**色が変わる**): - Textarea の placeholder: `text-base-400` → `text-text-neutral-low` (gray-400 → gray-500 相当。Figma が `text/neutral/low` を bind) - Input の disabled 文字: `text-neutral-400` → `text-text-neutral-disabled` (gray-400 → gray-300。placeholder 側は既に `text-neutral-disabled` だったので統一される) Tag(15 パターンすべて Figma 準拠に置換): - solid: 背景 `object/*` + 文字 `text/inverse` - outline: 枠線 `object/*` + 文字 `text/*` + 背景 `surface/base/0` - subtle: 背景 `surface/*/low`(neutral と negative のみ `middle/enabled`)+ 文字 `text/*` - **`status="info"` が primary 追従をやめ、info トークン(青系固定)になる**。 Figma が `object/info` / `text/info` を bind しているため。primary を変更した テナントでも info タグは青のままになる - Figma 558:29099〜29275 の 15 ノードをそれぞれ確認 検証: - 旧新トークンの解決値を突き合わせ、Input / Textarea の枠線・背景 8 ペアが **完全一致**(見た目不変)であることを確認。変化する 2 ペアは上記のとおり - `tsc --noEmit` pass - テスト 611 passed・10 skipped・19 todo(アサーションも追従) Refs: goodpatch/sparkle-design#298 / goodpatch/sparkle-design-internal#247 Co-Authored-By: Claude Opus 5 --- .../ui/input-password/index.test.tsx | 8 +++-- src/components/ui/input/index.test.tsx | 8 +++-- src/components/ui/input/index.tsx | 17 ++++++---- src/components/ui/tag/index.test.tsx | 19 ++++++----- src/components/ui/tag/index.tsx | 33 ++++++++++--------- src/components/ui/textarea/index.test.tsx | 4 ++- src/components/ui/textarea/index.tsx | 13 ++++---- 7 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/components/ui/input-password/index.test.tsx b/src/components/ui/input-password/index.test.tsx index 1e34f1a8..edf18957 100644 --- a/src/components/ui/input-password/index.test.tsx +++ b/src/components/ui/input-password/index.test.tsx @@ -246,7 +246,9 @@ describe("InputPassword", () => { const container = testContainer.getContainer().firstElementChild; // Then: invalid状態のクラスが適用されている(実際のCVAクラス名) - expect(container?.className).toContain("border-negative-500"); + expect(container?.className).toContain( + "border-border-negative-extra-high-enabled" + ); }); it("maintains invalid state styling when visibility is toggled", () => { @@ -259,7 +261,9 @@ describe("InputPassword", () => { EventHelpers.click(button); // Then: invalid状態のスタイリングが維持される(実際のCVAクラス名) - expect(container?.className).toContain("border-negative-500"); + expect(container?.className).toContain( + "border-border-negative-extra-high-enabled" + ); }); }); diff --git a/src/components/ui/input/index.test.tsx b/src/components/ui/input/index.test.tsx index 4f4923c5..f68d3ac8 100644 --- a/src/components/ui/input/index.test.tsx +++ b/src/components/ui/input/index.test.tsx @@ -255,7 +255,9 @@ describe("Input", () => { const container = testContainer.getContainer().firstElementChild; // Then: invalid状態のクラスが適用される(実際のCVAクラス名) - expect(container?.className).toContain("border-negative-500"); + expect(container?.className).toContain( + "border-border-negative-extra-high-enabled" + ); }); it("maintains invalid state with icon button", () => { @@ -264,7 +266,9 @@ describe("Input", () => { const container = testContainer.getContainer().firstElementChild; // Then: invalid状態のクラスが保持される(実際のCVAクラス名) - expect(container?.className).toContain("border-negative-500"); + expect(container?.className).toContain( + "border-border-negative-extra-high-enabled" + ); }); }); diff --git a/src/components/ui/input/index.tsx b/src/components/ui/input/index.tsx index 39230306..989cf5c1 100644 --- a/src/components/ui/input/index.tsx +++ b/src/components/ui/input/index.tsx @@ -21,8 +21,8 @@ const inputVariants = cva( lg: "h-12 character-4-regular-pro", }, isInvalid: { - true: "border-negative-500", - false: "border-neutral-500", + true: "border-border-negative-extra-high-enabled", + false: "border-border-neutral-extra-high-enabled", }, isDisabled: { true: "cursor-not-allowed", @@ -38,26 +38,29 @@ const inputVariants = cva( { isInvalid: false, isDisabled: false, - className: "border-neutral-500 hover:border-neutral-600", + className: + "border-border-neutral-extra-high-enabled hover:border-border-neutral-extra-high-hover", }, // エラー状態 { isInvalid: true, isDisabled: false, className: - "border-negative-500 hover:border-negative-600 bg-surface-base-0", + "border-border-negative-extra-high-enabled hover:border-border-negative-extra-high-hover bg-surface-base-0", }, // 無効状態 { isInvalid: false, isDisabled: true, - className: "border-neutral-200 bg-neutral-50", + className: + "border-border-neutral-extra-high-disabled bg-surface-neutral-middle-disabled", }, // エラー+無効状態 { isInvalid: true, isDisabled: true, - className: "border-negative-200 bg-neutral-50", + className: + "border-border-negative-extra-high-disabled bg-surface-neutral-middle-disabled", }, ], defaultVariants: { @@ -360,7 +363,7 @@ function Input({ "w-full h-full bg-transparent border-none outline-hidden focus:outline-hidden", "text-text-neutral-high placeholder:text-text-neutral-low px-2", isInputDisabled && - "cursor-not-allowed text-neutral-400 placeholder:text-text-neutral-disabled" + "cursor-not-allowed text-text-neutral-disabled placeholder:text-text-neutral-disabled" )} onChange={handleChange} onFocus={handleInputFocus} diff --git a/src/components/ui/tag/index.test.tsx b/src/components/ui/tag/index.test.tsx index 1b3ea87a..d48df91b 100644 --- a/src/components/ui/tag/index.test.tsx +++ b/src/components/ui/tag/index.test.tsx @@ -58,9 +58,12 @@ describe("Tag", () => { describe("Variant Styling", () => { const variants = [ - { variant: "solid" as const, expected: "bg-neutral-500" }, + { variant: "solid" as const, expected: "bg-object-neutral-middle" }, { variant: "outline" as const, expected: "bg-surface-base-0" }, - { variant: "subtle" as const, expected: "bg-neutral-100" }, + { + variant: "subtle" as const, + expected: "bg-surface-neutral-middle-enabled", + }, ]; variants.forEach(({ variant, expected }) => { @@ -94,11 +97,11 @@ describe("Tag", () => { describe("Status Variants", () => { const statuses = [ - { status: "neutral" as const, expected: "bg-neutral-500" }, - { status: "info" as const, expected: "bg-primary-500" }, - { status: "success" as const, expected: "bg-success-500" }, - { status: "warning" as const, expected: "bg-warning-500" }, - { status: "negative" as const, expected: "bg-negative-500" }, + { status: "neutral" as const, expected: "bg-object-neutral-middle" }, + { status: "info" as const, expected: "bg-object-info" }, + { status: "success" as const, expected: "bg-object-success" }, + { status: "warning" as const, expected: "bg-object-warning" }, + { status: "negative" as const, expected: "bg-object-negative-enabled" }, ]; statuses.forEach(({ status, expected }) => { @@ -125,7 +128,7 @@ describe("Tag", () => { // Then: すべてのプロパティが適用される expect(tag).toHaveClass("border", "min-w-14", "custom"); - expect(tag.className).toContain("border-primary-500"); + expect(tag.className).toContain("border-object-info"); }); }); diff --git a/src/components/ui/tag/index.tsx b/src/components/ui/tag/index.tsx index 41ce0c4f..4a20d54d 100644 --- a/src/components/ui/tag/index.tsx +++ b/src/components/ui/tag/index.tsx @@ -35,31 +35,31 @@ const tagVariants = cva( { variant: "solid", status: "neutral", - className: "bg-neutral-500 text-white", + className: "bg-object-neutral-middle text-text-inverse", }, // info + solid { variant: "solid", status: "info", - className: "bg-primary-500 text-white", + className: "bg-object-info text-text-inverse", }, // success + solid { variant: "solid", status: "success", - className: "bg-success-500 text-white", + className: "bg-object-success text-text-inverse", }, // warning + solid { variant: "solid", status: "warning", - className: "bg-warning-500 text-white", + className: "bg-object-warning text-text-inverse", }, // negative + solid { variant: "solid", status: "negative", - className: "bg-negative-500 text-white", + className: "bg-object-negative-enabled text-text-inverse", }, // === OUTLINE バリアント === @@ -67,31 +67,33 @@ const tagVariants = cva( { variant: "outline", status: "neutral", - className: "border-neutral-500 text-neutral-500 bg-surface-base-0", + className: + "border-object-neutral-middle text-text-neutral-middle bg-surface-base-0", }, // info + outline { variant: "outline", status: "info", - className: "border-primary-500 text-primary-500 bg-surface-base-0", + className: "border-object-info text-text-info bg-surface-base-0", }, // success + outline { variant: "outline", status: "success", - className: "border-success-500 text-success-500 bg-surface-base-0", + className: "border-object-success text-text-success bg-surface-base-0", }, // warning + outline { variant: "outline", status: "warning", - className: "border-warning-500 text-warning-500 bg-surface-base-0", + className: "border-object-warning text-text-warning bg-surface-base-0", }, // negative + outline { variant: "outline", status: "negative", - className: "border-negative-500 text-negative-500 bg-surface-base-0", + className: + "border-object-negative-enabled text-text-negative-enabled bg-surface-base-0", }, // === SUBTLE バリアント === @@ -99,31 +101,32 @@ const tagVariants = cva( { variant: "subtle", status: "neutral", - className: "bg-neutral-100 text-neutral-600", + className: "bg-surface-neutral-middle-enabled text-text-neutral-middle", }, // info + subtle { variant: "subtle", status: "info", - className: "bg-primary-100 text-primary-600", + className: "bg-surface-info-low text-text-info", }, // success + subtle { variant: "subtle", status: "success", - className: "bg-success-100 text-success-600", + className: "bg-surface-success-low text-text-success", }, // warning + subtle { variant: "subtle", status: "warning", - className: "bg-warning-100 text-warning-600", + className: "bg-surface-warning-low text-text-warning", }, // negative + subtle { variant: "subtle", status: "negative", - className: "bg-negative-100 text-negative-600", + className: + "bg-surface-negative-middle-enabled text-text-negative-enabled", }, ], defaultVariants: { diff --git a/src/components/ui/textarea/index.test.tsx b/src/components/ui/textarea/index.test.tsx index 470b83fa..d2317726 100644 --- a/src/components/ui/textarea/index.test.tsx +++ b/src/components/ui/textarea/index.test.tsx @@ -82,7 +82,9 @@ describe("Textarea", () => { testContainer.render(