From 5ee0b8e0859889284474310de779ba9d820c89e2 Mon Sep 17 00:00:00 2001
From: Kato Hiroki
Date: Fri, 2 Jan 2026 14:18:19 +0000
Subject: [PATCH 01/62] test: Add e2e tests before migration libs (#2054)
---
.../component-mapping.md | 362 +++++++++++
.../plan.md | 576 ++++++++++++++++++
.../smoke-tests.md | 465 ++++++++++++++
tests/custom-colors.spec.ts | 75 +++
tests/dark-mode.spec.ts | 76 +++
tests/navbar.spec.ts | 56 ++
6 files changed, 1610 insertions(+)
create mode 100644 docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
create mode 100644 docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/plan.md
create mode 100644 docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/smoke-tests.md
create mode 100644 tests/custom-colors.spec.ts
create mode 100644 tests/dark-mode.spec.ts
create mode 100644 tests/navbar.spec.ts
diff --git a/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
new file mode 100644
index 000000000..d32ccdb8b
--- /dev/null
+++ b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
@@ -0,0 +1,362 @@
+# コンポーネント対応マトリクス
+
+svelte-5-ui-lib → Flowbite Svelte 移行時の各コンポーネント難易度と対応方法。
+
+---
+
+## 概要
+
+コンポーネントを以下の4カテゴリに分類。各カテゴリの対応手数と注意点を記載。
+
+---
+
+## カテゴリ1:ライブラリ名置き換えのみ(⭐ 難易度低)
+
+| コンポーネント | svelte-5-ui-lib | Flowbite Svelte | 対応内容 | 注意点 |
+| ---------------- | --------------- | --------------- | --------------------------- | ----------------------------- |
+| `Heading` | ✅ | ✅ | import のみ変更 | `tag` prop 互換 |
+| `P` | ✅ | ✅ | import のみ変更 | - |
+| `Label` | ✅ | ✅ | import のみ変更 | - |
+| `Input` | ✅ | ✅ | import のみ変更 | - |
+| `Hr` | ✅ | ✅ | import のみ変更 | - |
+| `Img` | ✅ | ✅ | import のみ変更 | - |
+| `List` | ✅ | ✅ | import のみ変更 | slot 互換 |
+| `Li` | ✅ | ✅ | import のみ変更 | - |
+| `Helper` | ✅ | ✅ | import のみ変更 | - |
+| `Badge` | ✅ | ✅ | import のみ変更 | `color` prop 互換 |
+| `Avatar` | ✅ | ✅ | import のみ変更 | `src`, `size` prop 互換 |
+| `Breadcrumb` | ✅ | ✅ | import のみ変更 | component 階層互換 |
+| `BreadcrumbItem` | ✅ | ✅ | import のみ変更 | slot 互換 |
+| `Table` | ✅ | ✅ | import のみ変更 | - |
+| `TableHeadCell` | ✅ | ✅ | import のみ変更 | - |
+| `TableBodyCell` | ✅ | ✅ | import のみ変更 | - |
+| `TableBodyRow` | ✅ | ✅ | import のみ変更 | - |
+| `Button` | ✅ | ✅ | import のみ変更 + props確認 | size, color, variant 系を確認 |
+| `Card` | ✅ | ✅ | import のみ変更 | slot 互換 |
+| `Alert` | ✅ | ✅ | import のみ変更 | `color` prop 互換 |
+
+**対応方法:**
+
+```typescript
+// Before
+import { Heading, Button, Label } from 'svelte-5-ui-lib';
+
+// After
+import { Heading, Button, Label } from 'flowbite-svelte';
+```
+
+**テスト:** Vitest snapshot または Playwright (コンポーネント render 確認)
+
+**参考:** [Flowbite Svelte Components](https://flowbite-svelte.com/docs/components/)
+
+---
+
+## カテゴリ2:置き換え + 属性調整(⭐⭐ 難易度中)
+
+| コンポーネント | 変更内容 | 詳細 | 参考 |
+| ------------------ | --------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------- |
+| `Tabs` + `TabItem` | import + slot 名確認 | API は同一だが、slot 名が異なる可能性 | [Flowbite Tabs](https://flowbite-svelte.com/docs/components/tabs) |
+| `Tooltip` | import + `triggeredBy` prop | v3: `content` prop / v5: `triggeredBy` で target selector 指定 | [Flowbite Tooltip](https://flowbite-svelte.com/docs/components/tooltip) |
+| `Checkbox` | import + `bind:checked` | Svelte v5 runes: `bind:checked` で直接管理 | [Flowbite Checkbox](https://flowbite-svelte.com/docs/components/checkbox) |
+| `Radio` | import + `bind:group` | Svelte v5 runes: `bind:group` で group 管理 | [Flowbite Radio](https://flowbite-svelte.com/docs/components/radio) |
+| `Toggle` | import + `bind:checked` | Svelte v5 runes: `bind:checked` で state 管理 | [Flowbite Toggle](https://flowbite-svelte.com/docs/components/toggle) |
+
+**対応例:Tabs**
+
+```svelte
+
+
+
+
+
+ Content 1
+
+
+
+
+
+
+
+
+ Content 1
+
+
+```
+
+**対応例:Checkbox**
+
+```svelte
+
+
+
+
+
+
+
+```
+
+**テスト:** Vitest props + event テスト
+
+---
+
+## カテゴリ3:外部ライブラリからの復帰(⭐⭐ 難易度中)
+
+| コンポーネント | 現在の対応 | Flowbite Svelte での対応 | 変更内容 |
+| -------------- | ---------------------------------- | ----------------------------------- | ----------------------------------------------- |
+| `Carousel` | `embla-carousel-svelte` (外部導入) | ✅ Native Flowbite Svelte component | embla 削除、Flowbite Svelte Carousel へ置き換え |
+
+**対応例:Carousel**
+
+```typescript
+// Before(embla-carousel-svelte)
+import { Carousel } from 'embla-carousel-svelte';
+
+// After(Flowbite Svelte)
+import { Carousel, Controls, CarouselIndicators } from 'flowbite-svelte';
+```
+
+**詳細:** [Flowbite Carousel](https://flowbite-svelte.com/docs/components/carousel)
+
+**テスト:** Playwright slide navigation test
+
+---
+
+## カテゴリ4:抜本的な書き直し必要(⭐⭐⭐ 難易度高)
+
+### 4-1. Dropdown(最優先対応)
+
+**差分の大きさ:** 🔴 高
+
+**svelte-5-ui-lib:**
+
+```svelte
+
+
+
+
+
+ Item 1
+
+
+```
+
+**Flowbite Svelte:**
+
+```svelte
+
+
+
+
+
+ Item 1
+ (isOpen = false)}>Item 2
+
+```
+
+**主な変更点:**
+
+- `DropdownUl` / `DropdownLi` → `DropdownItem` に統合
+- `uiHelpers()` → `$state(isOpen)` runes で管理
+- `bind:isOpen` でバインド
+- slot ではなく component の直接配置
+
+**参考:** [Flowbite Dropdown](https://flowbite-svelte.com/docs/components/dropdown)
+
+---
+
+### 4-2. Modal
+
+**差分の大きさ:** 🟡 中(native `
-
-
-
-
-
-
- {#each problemImages as problemImage}
-
-

-
- {/each}
-
+
+
+
+
From a0d07e022b1b6cb776aa173728b0d9b8d6758b9f Mon Sep 17 00:00:00 2001
From: Kato Hiroki
Date: Sun, 4 Jan 2026 07:01:34 +0000
Subject: [PATCH 13/62] chore(docs): Update plan (#2054)
---
.../component-mapping.md | 108 +++++++++++++++++-
.../plan.md | 100 ++++++++++++----
2 files changed, 183 insertions(+), 25 deletions(-)
diff --git a/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
index d32ccdb8b..d6d2d94bd 100644
--- a/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
+++ b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/component-mapping.md
@@ -357,6 +357,110 @@ import { Carousel, Controls, CarouselIndicators } from 'flowbite-svelte';
---
+## カテゴリ3:外部ライブラリ復帰(⭐⭐ 難易度中)
+
+| コンポーネント | 変更内容 | 詳細 |
+| -------------- | ------------------------------------------------ | --------------------------------------------------- |
+| **Carousel** | embla-carousel-svelte → Flowbite Svelte Carousel | Plugin-based → Prop-based API、自動スケーリング無し |
+
+### Carousel プロパティ対応表
+
+| 項目 | embla-carousel-svelte | Flowbite Carousel | 説明 |
+| ---------------------- | ------------------------------------------------ | -------------------------------------------- | ------------------------------------------------------------- |
+| **基本API** | `use:emblaCarouselSvelte={{ options, plugins }}` | `` | embla: action directive / Flowbite: component-based |
+| **自動スライド** | `Autoplay()` plugin | `duration` prop | embla: plugin系 / Flowbite: prop単位で制御 |
+| **ループ動作** | `options = { loop: true }` | デフォルト有効 | Flowbite は常にループ(設定不可) |
+| **画像配列形式** | `[{ src: '...', alt: '...' }]` | `[{ src: '...', alt: '...', title: '...' }]` | **互換性あり**(同一形式) |
+| **画像スケーリング** | `imgClass="object-contain h-full w-fit"` | `slideFit="contain"` | embla: CSS class管理 / Flowbite: prop制御 |
+| **レスポンシブ高さ** | 外側div に手動で class 設定 | `class="min-h-[300px] xs:min-h-[400px]..."` | どちらも外側divで制御必須 |
+| **Overflow 処理** | 外側 div に `overflow-hidden` | 内部処理あり + 明示的推奨 | Flowbite内部処理だが、CSS overrides対応のため明示的指定が安全 |
+| **Alt 属性** | 手動設定(`imgClass` 別管理) | `images` 配列内に `alt` 含める | **自動適用**(Slide.svelte で自動反映) |
+| **インジケータ表示** | 手動実装が必要 | `` | Flowbite が提供(コンポーネント化) |
+| **ナビゲーション矢印** | 手動実装が必要 | `` (任意) | Flowbite が提供(optional) |
+
+### 移行実装例
+
+**Before (embla-carousel-svelte v8.6.0)**
+
+```svelte
+
+
+
+
+ {#each problemImages as image}
+
+

+
+ {/each}
+
+
+```
+
+**After (Flowbite Carousel v1.31.0)** ✅
+
+```svelte
+
+
+
+
+
+
+
+```
+
+### 移行時の注意点
+
+1. **Plugin-based → Prop-based への設計変更**
+ - `Autoplay()` plugin → `duration` prop(ミリ秒単位)
+ - 簡潔だが、細かい制御が必要な場合は Flowbite API では対応不可
+
+2. **自動スケーリング不可**
+ - embla: `imgClass` で自動管理
+ - Flowbite: `slideFit` prop で明示的に指定が必要
+
+3. **レスポンシブクラスは手動指定**
+ - 外側 div の `class` prop に`min-h-[300px] xs:min-h-[400px]` など記載必須
+ - embla同様、Flowbite も内部では自動生成されない
+
+4. **Alt 属性は自動適用** ✅
+ - `images` 配列の各オブジェクトに `alt` を含める
+ - Slide.svelte 内で `{...image}` で展開されるため自動で反映
+
+5. **Overflow 処理は明示的に指定** ✅
+ - Flowbite 内部で処理される可能性だが、CSS overrides に対応するため外側 div に `overflow-hidden` を追加推奨
+
+### 教訓
+
+- **API 設計の違いを理解することの重要性**: Plugin-based と Prop-based では柔軟性が異なる
+- **ドキュメント不足時はソースコード確認が必須**: alt属性の自動適用はドキュメント未記載だったが GitHub で確認可能
+- **Canonical CSS classes の使用**: Tailwind v4 では `min-h-[300px]` 形式が推奨される(VSCode拡張で警告あり)
+
+---
+
**作成日:** 2026-01-02
-**最終更新:** 2026-01-02
-**ステータス:** ドラフト完成
+
+**最終更新:** 2026-01-04
+
+**ステータス:** カテゴリ3 実装完了、ドキュメント更新完了
diff --git a/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/plan.md b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/plan.md
index 1864bd6a9..5737fb51a 100644
--- a/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/plan.md
+++ b/docs/dev-notes/2026-01-02/migrate-from-svelte-5-ui-lib-to-flowbite-svelte/plan.md
@@ -299,6 +299,7 @@ find src -name "*.svelte" -type f \
pnpm test:unit # Vitest
pnpm playwright test tests/navbar.spec.ts # Playwright
pnpm playwright test tests/custom-colors.spec.ts # Playwright
+pnpm playwright test tests/dark-mode.spec.ts # Playwright
```
**工数:** 1-2 days
@@ -319,34 +320,99 @@ pnpm playwright test tests/custom-colors.spec.ts # Playwright
---
-**段階 1-3: Carousel 置き換え**
+**段階 1-3: Carousel 置き換え** ✅ **2026-01-04 完了**
`embla-carousel-svelte` → `Flowbite Svelte Carousel` へ置き換え
-**API 差分:**
+**実装内容:**
-- v4: `bind:index` で slide 管理
-- `Controls`, `CarouselIndicators` コンポーネントの組み合わせ
+- ✅ embla-carousel-svelte, embla-carousel-autoplay をアンインストール
+- ✅ Flowbite Carousel に置き換え
+- ✅ CSS クラス互換性確認:
+ - ✅ レスポンシブ高さ:`min-h-[300px] xs:min-h-[400px] md:min-h-[540px]` 追加
+ - ✅ レスポンシブマージン:`mb-8 xs:mb-12` 追加
+ - ✅ `overflow-hidden` 追加(内部処理 + 安全性確保)
+ - ✅ `slideFit="contain"` で image scaling 制御
+ - ✅ `alt` 属性:image オブジェクトプロパティで自動適用
+- ✅ ビルド確認・成功
+- ✅ package.json から embla パッケージ削除確認
+
+**詳細:** component-mapping.md の「カテゴリ3」セクション参照
**参考:** https://flowbite-svelte.com/docs/components/carousel
-**工数:** 1-2 days
+**工数:** 1-2 days ✅
---
-**段階 1-4: 複雑なコンポーネント(Dropdown, Modal, Toast)**
+**段階 1-4: 複雑なコンポーネント(Dropdown, Modal, Toast)** 🔄 **Pending**
+
+- 🔄 `Dropdown`: v5 runes `$state(isOpen)` で管理 → Header.svelte で stub 化
+- 🔄 `Modal`: native `` + `form` prop + `onaction` callback
+- 🔄 `Toast`: `ToastContainer` で位置管理、auto-dismiss は手動
+- 🔄 `Spinner`, `ButtonGroup`, `Footer`: シンプル置き換え
+
+**現状:**
-- `Dropdown`: v5 runes `$state(isOpen)` で管理
-- `Modal`: native `` + `form` prop + `onaction` callback
-- `Toast`: `ToastContainer` で位置管理、auto-dismiss は手動
-- `Spinner`, `ButtonGroup`, `Footer`: シンプル置き換え
+- Header.svelte の Dropdown/Modal 機能をコメント化して TODO 化済み
+- 後続フェーズで実装予定
**参考:** Flowbite Svelte GitHub Repository
- https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/dropdown/Dropdown.svelte
- https://github.com/themesberg/flowbite-svelte/blob/main/src/lib/modal/Modal.svelte
-**工数:** 3-4 days
+**工数:** 3-4 days(後日)
+
+---
+
+## 学習ポイント・教訓
+
+### UI ライブラリ移行で重要だったこと
+
+#### 1. **コンポーネント API の差異を先に把握する**
+
+- embla-carousel: Plugin-based API (`Autoplay()` plugin)
+- Flowbite: Prop-based API (`duration` prop)
+- **教訓**: 単純な「置き換え」ではなく、設計思想の違いを理解してから実装
+
+#### 2. **CSS 自動化の落とし穴**
+
+- embla: `imgClass="object-contain"` で自動化
+- Flowbite: `slideFit="contain"` prop で制御
+- レスポンシブクラス(`min-h-[300px] xs:min-h-[400px]`)は手動指定必須
+- **教訓**: コンポーネント ライブラリの「自動化範囲」の把握が重要
+
+#### 3. **Alt 属性は自動適用される**
+
+- `images` 配列に `alt` を含めば、Slide.svelte が自動で `
` に反映
+- ドキュメントに明記されていなかったため、ソースコード確認が必要だった
+- **教訓**: ドキュメントが不十分な場合は GitHub ソースコードを読むしかない
+
+#### 4. **Overflow 処理は明示的に指定**
+
+- Flowbite 内部で処理されているが、CSS overrides に対応するため外側 div に明示的に `overflow-hidden` を追加
+- **教訓**: "内部処理で大丈夫" は信じず、サイドエフェクトを考慮した設定が必要
+
+#### 5. **属性名の変更を見落とさない**
+
+- Tooltip: `type="auto"` → `showOn="hover"`
+- Input: `on:change` → `onchange`
+- **教訓**: Breaking Changes ドキュメントをリスト化して一括チェックが効果的
+
+#### 6. **Tailwind CSS canonical classes の使用**
+
+- VSCode 拡張機能 `suggestCanonicalClasses` で `min-h-[300px]` 等が推奨される
+- Tailwind v4 では arbitrary values は `min-h-[]` 形式が standard
+- 標準 spacing scale の値(`min-h-64` など)より、明示的な px 単位が推奨される場合がある
+- **教訓**: 拡張機能の警告を無視せず、公式ドキュメントで確認する習慣が重要
+
+### 今後の移行作業での活用ポイント
+
+- **Category 4(Dropdown, Modal, Toast)** では、上記 1-2 の API 差異が大きいため、先に設計思想を理解してから実装
+- **テストファースト戦略**(Phase -1)の有効性が確認できた → TailwindCSS v4 colors の問題を事前に検出できた
+- **段階的実装**が効果的 → 各 Category 毎にビルド+テスト実行で早期問題発見
+- **ソースコード読解**: ドキュメント不足の際は実装を進める前に GitHub リポジトリを確認するステップが必須
---
@@ -678,12 +744,6 @@ Running 10 tests using 3 workers
---
-**作成日:** 2026-01-02
-**最終更新:** 2026-01-03
-**ステータス:** フェーズ0 完了(TailwindCSS v3→v4 移行完了、ビルド成功)
-
----
-
## フェーズ0 実装結果と教訓(2026-01-03)
### 実装内容
@@ -740,12 +800,6 @@ Running 10 tests using 3 workers
---
-**作成日:** 2026-01-02
-**最終更新:** 2026-01-03
-**ステータス:** フェーズ0 完了
-
----
-
**作成日:** 2026-01-02
**最終更新:** 2026-01-04
**ステータス:** フェーズ0 完了・実装検証済み
From 858733346b7816c1c854294b44ac44f2d0f8d4ec Mon Sep 17 00:00:00 2001
From: Kato Hiroki
Date: Sun, 4 Jan 2026 09:04:22 +0000
Subject: [PATCH 14/62] breaking: Replace from Svelte 5 UI lib and
flowbite-svelte (#2054)
---
src/lib/components/Footer.svelte | 6 +++---
src/lib/components/SpinnerWrapper.svelte | 2 +-
src/lib/components/TaskTables/TaskTable.svelte | 3 +--
src/lib/components/WorkBooks/WorkBookList.svelte | 15 +++++++++++----
4 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte
index 16c76e883..e3ae6b989 100644
--- a/src/lib/components/Footer.svelte
+++ b/src/lib/components/Footer.svelte
@@ -1,12 +1,12 @@
-
-