Skip to content

refactor(ui): #176 B 案 PR 1 — ui/* simple 11 ファイル inline style 撤去 + 基礎工事 - #256

Merged
fumtas1k merged 22 commits into
developfrom
feature/issue-176-b1-foundation-and-ui-simple
May 3, 2026
Merged

refactor(ui): #176 B 案 PR 1 — ui/* simple 11 ファイル inline style 撤去 + 基礎工事#256
fumtas1k merged 22 commits into
developfrom
feature/issue-176-b1-foundation-and-ui-simple

Conversation

@fumtas1k

@fumtas1k fumtas1k commented May 3, 2026

Copy link
Copy Markdown
Owner

サマリ

#176 B 案(style-src 'unsafe-inline' 撤去)の PR 1。src/components/ui/ の simple 11 ファイルから JSX style={{}} および CSSOM 直接 mutation を完全除去し、@layer components semantic class の foundation を整える。

  • 設計書: docs/superpowers/specs/2026-05-03-issue-176-b1-foundation-and-ui-simple-design.md
  • 実装計画: docs/superpowers/plans/2026-05-03-issue-176-b1-foundation-and-ui-simple.md
  • バッチ全体: memory project_b_plan_progress.md(PR 1.5 → PR 6 の順で直列進行予定)

CSP 改善の責務分離(reviewer I-2 補強)

  • : JSX style={{}} 撤去が 'unsafe-inline' 削除可能化の本質。React SSR で style="..." HTML 属性として吐かれるため、PR 6 で style-src 'unsafe-inline' 削除時に block される側だった
  • 副次: element.style.X = setProperty() への変更(ClearButton の hover、ToggleGroup の動的 grid columns)は CSSOM API として CSP profile 上は等価(どちらも 'unsafe-inline' 削除後 OK)。本 PR では migration tracker (/\.style\.[a-zA-Z]+\s*=(?!=)/g で検出、setProperty( は filter で許容) の検出パターン一貫性のための整備

主な変更

  • src/styles/global.css 末尾に @layer components ブロックを追加(typography 2 + 色補完 7 + alias 2 + elevation 1 + component 9)
  • src/utils/__tests__/inline-style-migration.test.ts 新規追加(progressive migration tracker、陽性対照 3 件込み)
  • 11 ファイル migration: ActionButton / BareInput / ClearButton / CopyButton / CountInput / DownloadButton / ErrorMessage / OutputField / Section / Select / ToggleGroup
  • ClearButton: onMouseEnter/Leave の CSSOM mutation を .btn-clear:hover (CSS) に置換
  • ToggleGroup: 動的 gridTemplateColumns JSX inline を .toggle-grid + setProperty('--toggle-cols', N) に置換
  • 既存 unit test(ActionButton/DownloadButton/BareInput/Section)の inline-style assertion を className 検査に翻訳(jsdom が @layer components CSS を解釈しないため、視覚検証は VRT/E2E 委譲)
  • docs/ui-conventions.md Section 2.1 / 2.2 を class-based ルールに更新
  • .claude/skills/dads-design-system/SKILL.md および references/components.md 冒頭に「issue security: CSP の script-src/style-src から 'unsafe-inline' を削減する #176 B 案 移行中」status banner
  • visual-regression.yml: 失敗 spec 名を PR comment に直接表示するよう改修(artifact download に依存しない)+ continue-on-error 撤去で VRT step 自体が ❌ 表示

スコープ外(後続 PR)

  • ResultTable.tsx / InputField.tsx の API redesign → PR 1.5
  • qr-ticket/* migration → PR 2
  • tools 系 migration → PR 3-5
  • _headersstyle-src 'unsafe-inline' 削除 / stripMetaStyleSrc() 撤去 / src/utils/styles.ts 削除 → PR 6
  • docs/decisions.md への B 案シリーズ依存記録 [067] → PR 6 で一括(reviewer I-3 defer 容認)

ローカル検証結果

  • npm run test: 690 passed (43 files)
  • npx astro check: 0 errors / 0 warnings / 10 hints (pre-existing)
  • npm run test:e2e: 144 passed / 1 skipped
  • npm run test -- inline-style-migration: 25 passed (11 files × 2 spec + 陽性対照 3)
  • grep -c "style={{" src/components/ui/*.tsxInputField.tsx:4ResultTable.tsx:8 のみ(PR 1.5 スコープ)
  • grep -nE "\.style\.[a-zA-Z]+ ?=[^=]" src/components/ui/*.tsx | grep -v setProperty → 空
  • ✅ 11 ファイルから @/utils/styles import 完全撤去

VRT 取り扱い

  • VRT (visual-regression.yml) は non-required check。意図的差分があれば PR ブランチで gh workflow run "Update Visual Regression Baseline" --ref feature/issue-176-b1-foundation-and-ui-simple で baseline 更新
  • 11 ファイル migration で Tailwind utility 近似(px-2 py-1.50.4rem 0.5remtracking-wide0.025em 等)の影響で qr-ticket / gs1-databar に 1-3px の layout shift が発生 → bot commit 935c9db で baseline 再生成済み

レビュー時に確認してほしい点

  1. @layer components の class 名が既存 .text-link / .tool-info-* 等と衝突していないか
  2. ToggleGroup の setProperty('--toggle-cols', N) が CSP3 strict 下で問題ないか
  3. .btn-copy のルール順序(base → is-compact → is-copied)が compact + copied 時に success 色を出すか
  4. unit test の className assertion が旧 inline-style assertion と等価な検証を保っているか

Follow-up issues(本 PR 範囲外、別 issue として起票予定)

  • ToggleGroup の layout 切替時 --toggle-cols クリーンアップ追加(removeProperty
  • ClearButton / CopyButton に type="button" 追加(form 内配置時の submit 暴発回避)
  • .btn-action--danger:disabled の border-color 一貫化(現状は赤ボーダーが残る、元実装からの faithful translation)
  • className 構築方式の clsx 統一(M-1: template literal + .trim() / 静的 / 連続空白などが混在)

🤖 Generated with Claude Code

fumtas1k added 17 commits May 3, 2026 21:52
- A-1 (#249) + VRT 基盤 (#254) 完了後の PR 1 spec を新規起草
- 旧 PR #253 で close された設計を proper sequencing で再構成
- @layer components 命名規約・11 ファイル migration mapping・
  inline-style-migration vitest 設計(陽性対照付き)・
  VRT 連携・docs/skills 改訂方針を自己完結で記述
- マスター設計書(PR 全体)は memory project_b_plan_progress.md を SoT
  として参照、本 spec は PR 1 のみカバー

Refs: #176
- 18 task 構成(Task 1: global.css / Task 2: migration test / Task 3-13:
  ui ファイル 11 件 / Task 14: docs/ui-conventions.md / Task 15: skill banner /
  Task 16: ローカルゲート / Task 17: push+CI/VRT / Task 18: PR 作成)
- 各 task は MIGRATED_FILES 追加 → fail 確認 → migration → pass 確認 → commit の TDD 順
- 全ファイルで full before/after コードを直書き(プレースホルダなし)
- Tailwind utility 換算リファレンス table を末尾に付録
- spec の .btn-copy CSS 順序バグ(is-compact → is-copied 順でないと
  compact+copied の color が muted に残る)を同 commit で修正

Refs: #176
ui/* simple 11 ファイル migration 用の semantic class を 1 ブロックに集約。
- typography: .body-emphasis / .caption(旧 src/utils/styles.ts 同等)
- color tokens not in @theme: .text-default/.text-muted/.text-on-primary/
  .bg-default/.bg-subtle/.border-default/.border-input
- alias for awkward auto-utility: .bg-error-tint / .bg-success-tint
- elevation: .shadow-elev-2
- component-scoped: .btn-clear / .btn-copy / .btn-action--{variant} /
  .toggle-grid / .btn-toggle

Refs: #176
style={{ / element.style.X= を progressive に検出する vitest を導入。
MIGRATED_FILES は空のまま、陽性対照 3 件で detector 自体の妥当性を担保。
Task 3-13 で 11 ファイルを順次 array に追加していく。

Refs: #176
- variant maps (bgMap/colorMap/borderMap/disabled overrides) を撤去
- style={{}} を .btn-action / .btn-action--{variant} className に置換
- disabled 時の bg/border は CSS :disabled 擬似で吸収
- caption / fontWeight 600 は 'caption font-semibold' Tailwind utility に
- inline-style-migration test に登録
- ActionButton.test / DownloadButton.test の inline style 検証を className 検証に更新

Refs: #176
- style={{}} を className(caption / bg-default / text-default / border-input
  または border-error / 任意 font-mono)に置換
- padding は Tailwind utility (px-2 py-1.5) で近似、微差は VRT で検証
- BareInput.test.tsx の el.style.* assertion を className 検査に翻訳
  (視覚検証は VRT/E2E 委譲)

Refs: #176
- style={{}} と onMouseEnter/Leave の CSSOM mutation を撤去
- .btn-clear class + CSS :hover で hover bg を表現(CSP strict 化に整合)
- transition-colors は .btn-clear の transition で吸収

Refs: #176
- copyStateColors ヘルパ撤去、状態切替は .btn-copy.is-copied で吸収
- compact mode は .btn-copy.is-compact で border 等を上書き
- Tailwind utility (text-xs / leading-none / tracking-wide / min-w-8) で近似

Refs: #176
- label / input / hint の style={{}} を className に置換
- bodyEmphasis → 'body-emphasis text-default'、caption → 'caption text-muted'
- width 6rem → w-24、margin → mb-1 / mt-1 Tailwind utility

Refs: #176
…ility に

style={{ display: inline-flex, ..., gap: 0.375rem }} を 'inline-flex items-center gap-1.5'
className に置換。

Refs: #176
- block variant: border border-error bg-error-tint で枠線+背景
- inline / block text: caption text-error
- colors / caption import を撤去

Refs: #176
- header / label / textarea の style={{}} を className に置換
- mono / resize は条件 className(font-mono / resize-y / resize-none)
- minHeight 2rem → min-h-8 / margin → mb-3 Tailwind utility

Refs: #176
- 外枠 / header / body の style={{}} を className に置換
- 外枠: rounded-xl border border-default
- header: body-emphasis text-default bg-subtle border-b border-default
- body: bg-default p-4
- Section.test.tsx の el.style.* assertion を className 検査に翻訳

Refs: #176
- wrapper / select / svg の style={{}} を className に置換
- select の paddingRight 2.5rem → pr-10 Tailwind utility
- svg の絶対配置 → absolute right-3 top-1/2 -translate-y-1/2 + text-muted

Refs: #176
- 外枠 / button の style={{}} を className に置換
- 動的 grid columns は .toggle-grid + setProperty('--toggle-cols', N) で表現
  (CSSOM API 経由のため CSP3 strict 下でも許容、属性経由 ≠)
- pressed 状態色は .btn-toggle[aria-pressed='true'] で吸収
- elevation.level2 inline → .btn-toggle[aria-pressed='true'] の box-shadow

Refs: #176
… ルールに更新

- 2.1: onMouseEnter/Leave による inline style 操作を禁止に変更
  (@layer components の :hover 擬似で表現する例を提示)
- 2.1: Tailwind 色値直書きは禁止維持、@theme 経由 auto-utility は許容と明記
- 2.2: caption / bodyEmphasis 参照を class 名に更新、leading-none 併記方法を提示

Refs: #176
- SKILL.md / references/components.md 冒頭に '⚠️ 移行中' status banner
- 既存の colors.* + style={{}} 例は『移行中の暫定パターン』として残置
- 全 rewrite は PR 6 で実施予定

Refs: #176
@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

🖼️ Visual Regression Test 結果

  • Status: ✅ 全 36 件 pass
  • Workflow run: 25284444072
  • Artifact (diff 画像 / playwright-report): 上記 workflow run の Artifacts セクションから download

diff が 意図的な visual 変更の場合: Update Visual Regression Baseline workflow を本 PR ブランチで workflow_dispatch trigger して baseline を更新。
diff が 意図しない regression の場合: 該当変更を fix。
本 check は required ではないため fail のままでも merge は可能(reviewer 判断)。

fumtas1k added 2 commits May 4, 2026 00:35
- VRT 実行 step で playwright stdout を vrt-output.log に tee(pipefail で exit 保持)
- 新規 step build-body で `✘` 行から viewport + spec 名を抽出して GITHUB_OUTPUT に
- comment 本文を build-body から動的生成(最大 30 件、retry 重複は除外)

これにより diff の概況が PR ページの comment 上で即座に把握できるようになり、
artifact download に依存しなくなる。

Refs: #176
@fumtas1k fumtas1k self-assigned this May 3, 2026
@fumtas1k fumtas1k closed this May 3, 2026
@fumtas1k fumtas1k reopened this May 3, 2026
旧設計: VRT step に continue-on-error: true を付与し、最終 step で exit 1 して
job conclusion を補正していた。結果として UI 上 VRT step が ✅ 表示になり、
diff の発生箇所がパッと見えなくなる悪い体感だった。

新設計:
- VRT step から continue-on-error を撤去(fail 時は step 自体が ❌ 表示)
- 後続 step(artifact upload / comment 投稿)は既に if: !cancelled() ガード済み
  なので step 失敗後も実行される
- 最終 exit-1 補正 step を削除(不要になった)

Refs: #176
@fumtas1k

fumtas1k commented May 3, 2026

Copy link
Copy Markdown
Owner Author

レビューサマリ

#176 B 案 PR 1 として src/components/ui/ simple 11 ファイルを @layer components semantic class へ移行する大型 PR (2722 行 / 22 commits / 28 files)。マージ承認 です。Frontend / Security / Tests を並列で多角的にレビューし、当初候補だった ClearButton focus-visible 退行は global.css の global :focus-visible 規則で担保 されていることが確認でき、🔴 Critical はゼロ。CI 全 green (e2e 2m14s / test 44s / visual-regression 1m21s で 36 baseline pass)。

#176 シリーズ全体の設計(meta strict layer + B 案 inline style 撤去)が PR #249 [064] と本 PR で連続的に整備され、PR 6 での style-src 'unsafe-inline' 最終削除へ向けた段階移行として筋が通っています。


🟡 Important

I-1: .btn-copy.is-compact.is-copied の border 抑止挙動を CSS コメントで明示

該当: src/styles/global.css.btn-copy 系規則 (PR 確認観点 #3)

ルール順は「base → is-compact → is-copied」で specificity (0,2,0) は等価、source order で is-copied が後勝ち。ただし is-compactborder: none ショートハンドが border-style: none を固定するため、後続の border-color: var(--color-success) は描画されません

.btn-copy.is-compact { border: none; ... }       /* line 251-254 */
.btn-copy.is-copied { border-color: var(--color-success); ... }  /* line 255-259 */

これは旧 CopyButton.tsx (develop) の compact 分岐が copied 状態に関わらず border: 'none' だった挙動を faithful に翻訳 したものですが、CSS のみ読むと「compact + copied で success border が出るはず」と誤読される懸念があります。

推奨アクション (本 PR 内 1-2 行 commit、または follow-up):

/* 注意: compact 時は border:none で描画自体を抑止するため、is-copied の border-color は
   compact では見えない (旧 develop 実装と同一)。bg/color は is-copied で上書きされ機能維持。 */

I-2: PR description の CSP 改善ニュアンス補強(任意)

該当: PR description / docs/decisions.md([064] / 後続のための [067] 候補)

PR description「setProperty('--toggle-cols', N) (CSSOM API は CSP3 strict 下でも許容)」は技術的に正しい記述ですが、本 PR の真の CSP 改善ポイント は次の方が大きいです:

  • style={{ gridTemplateColumns: ... }} (JSX inline) は React の server render で HTML style="" 属性 として吐かれる → CSP style-src 'unsafe-inline' の対象(PR 6 で 'unsafe-inline' を削除した時点で block される側)
  • setProperty('--toggle-cols', N) は CSSOM API → server render に style="" 属性が出ず、CSP 評価対象外

つまり「JSX style={{}} → CSSOM setProperty」が 'unsafe-inline' 削除可能化の本質。element.style.X = ...setProperty() への変更(ClearButton 等)は CSP profile としては等価(どちらも CSSOM)で、純粋な lint-friendliness / migration tracker 対応の側面のみ。

推奨: PR description で「JSX style={{}} 撤去が CSP 改善の核、element.style.X = setProperty() は detector 一貫性の都合」のように責務を分けると、PR 6 までのシリーズ意図がより明瞭になります。

I-3: PR 6 への依存性を decisions.md に記録推奨

該当: docs/decisions.md (本 PR では [067] 等の追加なし)

本 PR は infrastructure 整備のみで _headers / PRODUCTION_CSP変更なし。security profile の degradation はないものの、「PR 6 で style-src 'unsafe-inline' を削除する」依存が PR description にしかない状態。本 PR で B 案シリーズ全体の依存図を [067] 等で記録しておくと、シリーズが中間で止まった際の honesty が保てます。

推奨: 本 PR で対応せず、PR 6 で [067] を一括記録するのも可(本 PR 内の docs/decisions.md 変更ゼロ)。スコープ判断はお任せします。


🟢 Minor

M-1: className 構築方式の不統一

該当: 11 ファイル全て

template literal + .trim() (ClearButton/CopyButton/BareInput/OutputField)、template literal のみ (ActionButton/ToggleGroup/Section)、静的文字列 (CountInput/Select/ErrorMessage) が混在。

CopyButton.tsx:97 の非 compact 分岐で stateClass='' 時に 'btn-copy caption ...'連続空白 が入ります(.trim() は前後のみ、中間空白は残存)。動作影響なしですが DOM 検査ノイズ。

variant: Variant は union 型なので class="btn-action--undefined" 事故は型レベルで防止 ✅。

推奨: 本 PR スコープ外。フォローアップで clsx 統一 / [a, b, c].filter(Boolean).join(' ') ヘルパ抽出を別 PR で。

M-2: Tailwind utility approximation の差分

px-2 py-1.5 (0.5rem / 0.375rem) ≈ 旧 0.4rem 0.5rem垂直方向 +1.6px / 水平方向 -1.6pxtracking-wide (0.025em) ≈ 旧の差。VRT baseline は新値で再生成済 (bot commit 935c9db) で凍結。PR 本文 Δ≈1.6px 記載と一致。差分は明示的に許容判断。

M-3: 既存 unit test の coverage 緩和(VRT 委譲)

el.style.color === 'rgb(26, 86, 219)' 等の色値 assertion → 新 el.classList.contains('btn-action--primary') への翻訳。jsdom が @layer components を解釈しないため視覚検証は VRT 委譲。security 観点では問題なし、color token と class の対応は VRT 単独に集約された状態。PR description の「視覚検証は VRT/E2E 委譲」明記で honesty 担保済み。


✅ OK 確認済み

Frontend / a11y

  • ClearButton focus-visible 退行なし (当初 🔴 候補): global.css:98-101:where(button, a, [role='button'], input, textarea, select):focus-visible { outline: var(--focus-ring); outline-offset: 2px; } がグローバルに適用。.btn-clear 個別 :focus-visible 不在でも全 button 対象で keyboard a11y 確保。旧実装も onMouseEnter/Leave のみで keyboard hover 視覚 feedback はそもそも無く、a11y 等価
  • ActionButton danger:disabled の border-color 維持: 旧 disabledBorderOverrides も primary/secondary のみで danger は赤ボーダー残存 → faithful translation。PR 本文 follow-up ロットの文字数が多いと、表記が見切れる #3 の指摘通り意図通り
  • @layer components 143 行: class 名衝突なし (.text-link / .tool-info-* 等と分離)、@theme token (var(--color-primary) 等) と整合、specificity 設計健全
  • a11y 属性保持: aria-pressed (ToggleGroup:62) / aria-busy (ActionButton:42) / aria-live="polite" + role="status" (CopyButton CopyAnnounce) / role="alert" (ErrorMessage block/inline) / Section の span[role="heading"] aria-level={3} 変換 — すべて保持

Security / CSP

  • CSP profile 後退なし: public/_headerssrc/utils/csp.ts:PRODUCTION_CSP 共に 無変更。AND 評価戦略 (meta strict ∩ header permissive) も維持
  • VRT workflow shell injection 耐性: vrt-output.loggrep -E '^[[:space:]]+✘'awk -F'›' のチェーンは値を eval しない。GITHUB_OUTPUT への heredoc 書き込み (MARKDOWN_EOF delimiter) は multiline 対応 OK。peter-evans/create-or-update-comment への body 渡しは GitHub REST API への JSON serialize 経由で shell metachar 無害化
  • @layer components への user input sink 不在: typography / color / elevation / component すべて static rule。var(--toggle-cols, 2) の流入は options.length (numeric) のみで attacker-controllable ではない
  • continue-on-error 撤去: VRT step 自体が ❌ 表示になり、PR comment と合わせて diff 発生箇所が即時可視化

Tests

  • inline-style-migration.test.ts (新規 65 行): progressive tracker パターン + 陽性対照 3 件で detector の妥当性を保証。regex /\.style\.[a-zA-Z]+\s*=(?!=)/g=== 負先読みで除外、setProperty( は filter で許容。11 ファイルが正しく MIGRATED_FILES に登録、25 tests pass
  • 既存 unit test 翻訳: ActionButton / BareInput / DownloadButton / Section の className 検査への変換が conditional className ({isCopied && 'is-copied'}) も含めて適切

ルール準拠

  • --base develop ✅、PR タイトル・本文・コミット日本語 ✅、aria 削除なし ✅
  • tests/meta/docs-section-references.test.ts 7/7 pass で章番号ドリフト検出 OK
  • spec / plan の #XXX placeholder 残存なし
  • 22 commits は段階的に意味ある単位 (spec → plan → global.css → tracker → 11 ui ファイル × 各 commit → docs → skill banner → VRT 改善 + baseline 再生成 + continue-on-error 撤去)
  • bot baseline commit 935c9db は VRT 運用の正常フロー

総合判断

🟢 マージ承認

#176 B 案シリーズの第一弾として、src/ への変更は production code 11 ファイル + 既存 test 4 ファイル + 新規 test 1 ファイル + global.css の 4 種類に整然と分かれており、22 commits の粒度も spec/plan/migration/docs で見通しが良い。VRT が 36 baseline 全 pass で「見た目変化なし」が CI 担保された状態でマージできるのは、PR #254 で導入した VRT 基盤の効果が本 PR で初回有効化された好例。

マージ前手動確認 (任意 / 軽微):

  • I-1 の CSS コメント追加は 1 commit で対応可能、本 PR 内推奨
  • I-2 / I-3 は follow-up issue でも可

follow-up issue 候補 (PR 本文末尾と整合):

  • ToggleGroup の layout 切替時 --toggle-cols クリーンアップ追加(removeProperty
  • ClearButton / CopyButton に type="button" 追加(form 内配置時の submit 暴発回避)
  • .btn-action--danger:disabled の border-color 一貫化
  • className 構築方式の clsx 統一 (M-1)

PR 6 までの依存も明確で、シリーズ全体の見通しが取れた状態。お疲れさまでした。

レビュー I-1 対応。compact 分岐は旧実装どおり常に border:none で、
is-copied の border-color は compact 時に描画されない設計を CSS のみ
読んでも誤読しないよう注釈で明示。実装挙動は変更なし。

Refs: #176, #256
@fumtas1k

fumtas1k commented May 3, 2026

Copy link
Copy Markdown
Owner Author

レビューありがとうございます。対応内容:

🟡 Important 対応

  • I-1 (CSS コメント明示) ✅ 対応: commit 606d383.btn-copy.is-copied 上に提案文言の注釈を追加
  • I-2 (CSP 改善ニュアンス補強) ✅ 対応: PR description 冒頭に「CSP 改善の責務分離」セクションを追加。「JSX style={{}} 撤去 = 核」「element.style.X=setProperty() = detector 一貫性」の責務を明示
  • I-3 (decisions.md 依存記録) ⏸ defer: PR 6 で [067] として一括記録(reviewer 容認通り)

🟢 Minor 対応

M-1 / M-2 / M-3 はいずれも follow-up / 既知トレードオフとして scope 外で受領。

Follow-up issues 起票

PR description 末尾の follow-up 候補を以下の issue に起票しました:

CI 引き続き全 green、606d383 も problem なく検証済み。merge いただける状態です。

@fumtas1k

fumtas1k commented May 3, 2026

Copy link
Copy Markdown
Owner Author

再レビュー結果(修正コミット 606d383 + PR description 更新 + follow-up issues 起票 確認)

✅ 反映確認済み

I-1: CSS コメント明示(commit 606d383

src/styles/global.css:255-256

/* 注意: compact 時は border:none で描画自体を抑止するため、is-copied の border-color は
   compact では見えない (旧 develop 実装と同一)。bg/color は is-copied で上書きされ機能維持。 */

提案文言をそのまま採用。挿入位置も .btn-copy.is-compact.btn-copy.is-copied に置かれており、ルール順序と「mask 挙動の主体は is-compact 側」という設計意図が読む順序通りに伝わる配置。完璧です。

commit message も「実装挙動は変更なし」と明示されており、後追い reviewer が CSS 規則の検証だけで stop できるよう書かれています。

I-2: PR description に「CSP 改善の責務分離」セクション追加

PR description 冒頭(サマリ直後)

提案した「責務の分離」を 2 bullet で明文化:

  • : JSX style={{}} 撤去が 'unsafe-inline' 削除可能化の本質。React SSR で style="..." HTML 属性として吐かれるため、PR 6 で削除時に block される側
  • 副次: element.style.X = setProperty() への変更は CSSOM API として CSP profile 上は等価。migration tracker の検出パターン一貫性のための整備

技術的に正確で、後続 reviewer が「ToggleGroup の setProperty() で CSP が改善した」と誤読する経路が塞がれました。レビュー指摘の意図そのままを honesty 改善に転化できています。

I-3: PR 6 で [067] 一括記録に defer

当初レビューで「PR 6 で [067] を一括記録するのも可」と容認した通りの判断。✅ 妥当な scope discipline。


🆕 期待を超える対応: Follow-up issue 4 件起票

PR description 末尾の follow-up 候補すべてを当日中に issue 化:

Issue タイトル 起源
#257 OPEN ToggleGroup の --toggle-cols cleanup(removeProperty PR 本文 follow-up
#258 OPEN ClearButton / CopyButton に type="button" 追加 PR 本文 follow-up
#259 OPEN ActionButton variant=danger:disabled の border-color 一貫化 PR 本文 follow-up + I-2 関連
#260 OPEN className 構築方式を clsx 等で統一(M-1) レビュー M-1

メモリ「Follow-up Routing」(PR 後の離散タスクは issue で起票) に正しく整合した運用で、本 PR スコープから外れた指摘が 「忘れられる経路」を断つ 良い対応です。M-2 (Tailwind approximation) / M-3 (test coverage 緩和) は性質上 issue 化せず受領、という判断も適切。


検証

CI 全 green:

  • e2e 2m15s
  • test 42s
  • visual-regression 1m30s (36 baseline 全 pass を維持、606d383 の CSS コメント追加は実 CSS rule に影響しない textual 変更のため当然)

606d383 の textual 性質が維持されている(CSS コメントは描画に影響しない)ことを VRT pass で機械的に確認済み。


総合判断

🟢 マージ承認(修正完了、ブロック要因ゼロ)

レビューサイクルの完成度として:

  • I-1 / I-2 が 本 PR 内 で適切に解消
  • I-3 / Minor 群が 明示的に defer or 既知トレードオフとして受領
  • Follow-up が issue 化で永続化
  • すべて honesty / scope discipline / 仕組み化 の三方向に揃った理想形

#176 B 案 PR 1 として、'unsafe-inline' 削除可能化の核(JSX style={{}} 撤去)の foundation がこれで完成しました。次は PR 1.5(ResultTable / InputField API redesign)→ PR 2-5(tools 系 migration)→ PR 6(CSP 撤去)の流れですね。お疲れさまでした。

@fumtas1k
fumtas1k merged commit eb5e537 into develop May 3, 2026
3 checks passed
@fumtas1k
fumtas1k deleted the feature/issue-176-b1-foundation-and-ui-simple branch May 3, 2026 16:36
fumtas1k added a commit that referenced this pull request May 4, 2026
… 撤去 + cellStyle API 再設計 (#261)

* docs(spec/plan): #176 B 案 PR 1.5 — spec + plan を追加

- spec: ResultTable + InputField inline style 撤去 + cellStyle API 再設計
- plan: 10 task / TDD-style commit chain
- 直前 PR 1 (#256) と同じ pattern で spec/plan を repo commit

* style(global): #176 B 案 PR 1.5 用 @layer components 追記 (.result-table-* / .bg-surface-row / .btn-link-plain)

* style(global): bg-surface-row → bg-surface に rename (token-semantic 命名統一)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(ui): ResultTable 内部 inline style を class 化 + cellPadding flag 追加 (#176 B 案 PR 1.5)

* refactor(ui): ResultTable の <th> を常に normal padding に固定 (#176 B 案 PR 1.5)

cellPadding flag は <td> のみに適用するよう修正。<th> はオリジナルの
hardcoded padding: '0.5rem 0.75rem' 挙動を維持。Task 3/4 で copy 列が
cellPadding: 'compact' になった際に header text 'コピー' が縮む regression を予防。

* refactor(ui): UlidGenerator columns を ResultTable 新 API (cellPadding/className) に移行 (#176 B 案 PR 1.5)

* refactor(ui): UuidV7Generator columns を ResultTable 新 API (cellPadding/className) に移行 (#176 B 案 PR 1.5)

* refactor(ui)\!: ResultTable から cellStyle API を削除 (#176 B 案 PR 1.5)

Task 3/4 で全 consumer (UlidGenerator/UuidV7Generator) が新 API
(cellPadding/className) に移行完了したため、@deprecated だった
cellStyle フィールドを TableColumn<T> から削除。同時に <td> から
style={col.cellStyle} 属性も削除し、ResultTable.tsx の JSX inline
style を 0 件にする。CSSProperties の未使用 import も整理。

Breaking change: 外部から cellStyle: CSSProperties を渡していた
コードはコンパイルエラーになる。

* test(migration): MIGRATED_FILES に ResultTable を登録 (#176 B 案 PR 1.5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(ui): InputField 内部 inline style を class 化 (#176 B 案 PR 1.5)

* test(migration): MIGRATED_FILES に InputField を登録 (#176 B 案 PR 1.5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(spec): col-width 戦略の記述を実装に合わせて更新 (#176 B 案 PR 1.5)

実装は --col-width-N (per-index) ではなく --col-width (per-element scoped)
を採用。final review 指摘で stale 部分を修正。

* fix(ui+vrt): InputField サンプルボタンの下線除去 + VRT で new Date() を固定 (#176 B 案 PR 1.5)

VRT 結果 (#261 PR comment) を受けた 2 件の修正:

1. InputField サンプルボタン: text-link → text-link-color に変更。
   text-link は `text-decoration: underline` を含む(汎用テキストリンク用)が、
   元の `colors.link` インライン style は色のみで下線なしだった。
   text-link-color は "色のみ" 用 class(global.css line 144)。

2. visual-regression.spec.ts: Date.now の固定に加え、new Date() (引数なし)
   constructor も FIXED_NOW を返すよう Date global を Proxy 化。
   QrTicket.tsx:13 の getDefaultExpiry() が `new Date()` で today+7 を計算し、
   VRT 実行日に応じて表示日付がドリフトする問題を解決。
   引数付き new Date(arg) の挙動は維持(user 入力 expiry の解釈に影響しない)。

* test(vrt): visual regression baseline を CI Linux runner で再生成

* refactor(ui): ResultTable の useEffect+useRef を ref callback に統合 + width JSDoc 追加 (#176 B 案 PR 1.5)

PR #261 のレビュー指摘 (#265 / #266) への post-review fix:

#265 (useEffect [columns] anti-pattern):
- useEffect+useRef を撤去、ref callback 内で直接 setProperty
- consumer 側で columns 配列が毎レンダリング新参照になる問題を構造ごと解消
  (reviewer 提案の改善案 (3) 「実装最小」を採用)
- 副次的に minWidth が undefined に戻った時の removeProperty も追加
  (Final reviewer Minor 4 — minWidth never unset を併せて解消)

#266 (width / minWidth origin discipline):
- TableColumn<T>.width と Props.minWidth に hard-coded only の JSDoc 追加
- CSSOM の encapsulate と user input bridge 時 sanitize 必須を明文化
- コード変更なし (JSDoc のみ)

検証: npx astro check 0 errors / npm run test 694 pass

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
fumtas1k added a commit that referenced this pull request May 8, 2026
…and hash 取り込み + B 案完了記録 ([068]) (#315)

* docs(spec): #176 B 案 PR 10 — style-src strict 化最終 flip + Astro island hash 取り込み design

#176 B 案最終 flip の設計書を追加。PR 1〜9 で完了した両層 strict 化を
PR 10 で最終投入し、PR 9 Phase 2 で発覚した Astro island runtime
style hash の取り込みも同 PR で解決する。

設計合意事項 (brainstorming Q1〜Q? で確定):

- Astro hash 取り込み: option α (handcoded fingerprint sha256-vv9I... +
  検出網)。β (build-time 自動抽出) は overkill、γ (header permissive 維持)
  は B 案 goal「両層 strict 化」と矛盾
- 1 PR / 5 commit / ~250 行で完結 (memory feedback_pr_size.md 閾値内)
- 親 Opus 直接実装 (CSP flip は高 stakes)

scope:
- A: PR 8 backup 3 commit 再投入 (_headers strict 化 / stripMetaStyleSrc
  撤去 / test 群 strict 化)
- B: Astro island hash hardcode + meta-csp.test.ts 拡張で検出網
- C: docs/decisions.md [068] B 案完了記録
- D: docs/projects/issue-176-b-plan-progress.md 完了反映

検出網には feedback_positive_control_for_gates.md 準拠の陽性対照
メタテスト (hash 1 文字書換で fail 確認) を含む。

* docs(plan): #176 B 案 PR 10 implementation plan

spec 25787c3 に基づく bite-sized task 実装プラン。7 tasks 構成:

- Task 1-2: refactor(csp) — _headers / csp.ts / astro.config.mjs strict 化
- Task 3: test(csp) — test 群 strict 化 + Astro hash 検出網
  (sha256 整合性メタテスト含む)
- Task 4-5: docs — [068] B 案完了記録 + SoT 完了反映
- Task 6: 親直接 verify (build + E2E + 検出網陽性対照)
- Task 7: PR 作成

各 step に exact code / exact command / expected output を記載済。
[068] / PR body の full text を plan 内に展開し、executor が synthesize
不要な状態にした。self-review (spec coverage / placeholder / type
consistency) pass。

* refactor(csp): #176 B 案 PR 10 (1/5) — _headers / csp.ts style-src strict 化

- public/_headers: style-src 'self' 'unsafe-inline' → 'self' 'sha256-vv9I...'
- src/utils/csp.ts: PRODUCTION_CSP 同期更新

Astro island runtime が injection する inline style:
<style>astro-island,astro-slot,astro-static-slot{display:contents}</style>
の sha256 (vv9IoKo7BSLbWcUHr3tNmfNVmm5L/9Cfn2H6LMk7/ow=) を取り込む。
Astro が当該 inline style 文字列を変更しない限り stable な fingerprint。

Note: headers.test.ts / meta-csp.test.ts の strict assert 反転は本 PR
commit 3 で実施。本 commit 単独では既存 test が red (commit 3 で green
に戻す段階遷移)。

* refactor(csp): #176 B 案 PR 10 (2/5) — stripMetaStyleSrc 暫定 integration 撤去

- astro.config.mjs: stripMetaStyleSrc 関数定義 + integrations 配列 entry
  + 関連 import (readFileSync/writeFileSync/fileURLToPath/glob) 削除

#176 A-1 [064] で導入した暫定 integration。CSP3 仕様で hash と
'unsafe-inline' 共存時にブラウザが unsafe-inline を無視する制約により、
<meta> から style-src を除いて header 側の 'unsafe-inline' のみで制御
する設計だった。

PR 1〜9 で React style={{}} と Astro inline style を全廃 + PR 9 で
setProperty 経路を Constructable Stylesheets 化したことで、<meta> 側でも
style-src を hash + 'self' の strict 形式で生成して safe になった。

Note: meta-csp.test.ts / astro-config-csp.test.ts の test 反転 / 削除は
本 PR commit 3 で実施。本 commit 単独では test red (commit 3 で green
に戻す段階遷移)。

* test(csp): #176 B 案 PR 10 (3/5) — test 群 strict 化 + Astro hash 検出網

- headers.test.ts: style-src 'unsafe-inline' 不在 + Astro hash 存在を陽性 assert
- meta-csp.test.ts: <meta> 側 style-src 不在 → strict 形式 ('self') assert に
  反転 + dist HTML 内 Astro inline style 検出網 + sha256 整合性メタテスト追加
  (Astro は React island ありページにのみ inline style 注入するため
  distPages.some() パターンで検出)
- astro-config-csp.test.ts: stripMetaStyleSrc 関連 2 ブロック削除、JSDoc を
  [068] 参照に置換

陽性対照メタテストとして dist HTML inline style content の sha256 を計算し
_headers の hash 値と一致するか assert することで、Astro 文字列変更で hash
が同期更新されない silent regression を捕捉する。

Note: 本 commit で Task 1 / 2 の意図的 red 状態を resolved。全 unit test
green (822 件)。

* docs(decisions): #176 B 案 PR 10 (4/5) — [068] B 案完了記録

style-src 'unsafe-inline' 削除と B 案 (PR 0〜10 series) 完了の design
decision を記録。

- B 案 PR 0〜10 series の依存図 + 各 PR の達成サマリ
- 本 PR (PR 10) で達成した事項 (両層 strict 化 / stripMetaStyleSrc 撤去
  / Astro island hash 取り込み / 検出網拡張)
- Astro island hash 取り込みの設計選定 (α handcoded、β/γ 不採用根拠)
- 削除した暫定 infra (stripMetaStyleSrc / MIGRATED_FILES /
  applyStrictStyleSrcCsp)
- 設計判断 KEEP 記録 (.text-primary 命名 / Tailwind border + @layer 優先度)
- 検出網運用ノート (inline-style-migration glob / applyProductionCsp gate
  / Phase 0 spec / 本 PR の Astro hash 検出網)
- 関連 PR / issue (PR 0〜10 全 link、#176 / #305 close)
- Lessons learned (CSP3 仕様 / Astro 暗黙 inline style / 陽性対照運用 /
  PR 本数管理 / subagent 委譲方針)

PR 1 (#256) reviewer I-3 で defer 容認、PR 6 → PR 8 → PR 10 へ持ち越されて
いた B 案完了記録の約束を本 entry で消化。

* docs(projects): #176 B 案 PR 10 (5/5) — SoT 進捗 table 完了状態に同期

- 進捗 table の PR 10 行を「未着手」→「✅ merged」に更新 (merge hash は
  本 PR merge 後の chore PR で別途反映)
- 末尾に「B 案完了」セクションを新設 (達成サマリ / 削除した暫定 infra /
  follow-up 候補)、#176 close を明記

詳細な design decision は docs/decisions.md [068] を参照。

* refactor(e2e): #176 B 案 PR 10 (6/5) — applyStrictStyleSrcCsp helper 撤去

PR 9 で導入した applyStrictStyleSrcCsp は PRODUCTION_CSP が strict 化された
本 PR で applyProductionCsp と機能等価になり冗長化。spec § 5.1 では「別
cleanup PR 候補」と分類していたが、helpers.ts 内の drift sanity guard が
PRODUCTION_CSP 変更で即 throw するため本 PR で同時撤去が不可避。

- tests/e2e/helpers.ts: STRICT_STYLE_SRC_CSP 派生定数 / drift sanity guard
  / applyStrictStyleSrcCsp 関数を削除
- tests/e2e/csp-constructable-stylesheet.spec.ts: applyStrictStyleSrcCsp
  呼出を applyProductionCsp に置換 (機能等価)

Phase 0 spec (csp-constructable-stylesheet.spec.ts) は永続検出網として
継続運用、本 PR で書換後も陽性 / 陰性対照の動作は不変。

* docs(csp): #315 review S2 — _headers / csp.ts コメントに [068] 併記

PR #315 review S2 提案に対応。`_headers:5` / `src/utils/csp.ts:18` の
[064] のみ参照では style-src 両層 strict 化 (本 PR の意図) が
self-explanatory でなかったため、[068] への参照を 1 行ずつ追加。

- public/_headers: 「style-src は #176 B 案完了 ([068]) で両層 strict 化」
  + Astro island hash 取り込みの 1 行を defense-in-depth コメント直後に追加
- src/utils/csp.ts: PRODUCTION_CSP JSDoc に同等の段落を追加 (Astro inline
  style の literal + 検出網 location も明記)

検証: docs only コメント変更、behavioral change ゼロ。
fumtas1k added a commit that referenced this pull request May 8, 2026
…prop 追加) (#318)

- ActionButton に `size?: 'default' | 'compact'` を追加。`compact` のとき `font-bold px-3 py-2 leading-none` を適用し CopyButton (default) と同高に
- DownloadButton は内部で常に `size="compact"` を使用。呼び出し側 API は変更なし
- 過去 35bc1ca で CopyButton と統一されていたが #236 (ActionButton 特殊化 refactor) と #256 (CSS class 化) で回帰した状態を修正
- `OutputField` rightSlot 3 画面の横並び崩れ解消 + 単独配置 (Gs1Databar / JanCode / QrCode / qr-ticket) も以前の細い高さへ
- ActionButton を直接使う他箇所 (生成系 primary CTA 等) は `size` 未指定 = default で影響なし
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant