Skip to content

feat: ✨ fix tag cloud overflow on mobile#62

Merged
shin-sforzando merged 1 commit into
mainfrom
058_fix_tag_cloud_mobile_scroll
May 19, 2026
Merged

feat: ✨ fix tag cloud overflow on mobile#62
shin-sforzando merged 1 commit into
mainfrom
058_fix_tag_cloud_mobile_scroll

Conversation

@shin-sforzando

@shin-sforzando shin-sforzando commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • #tag-cloud コンテナの高さをレスポンシブ化(clamp(240px, 50vw, 320px))し、インライン style="height: 320px;" を削除
  • tag-cloud.ts をリファクタリングし renderTagCloud() を分離、ResizeObserver でデバイス回転時も再描画するよう対応
  • タグの位置クランプを追加し、絶対配置要素がコンテナ境界を超えないよう保証
  • アクティブタグ(中央配置)のフォントサイズを文字幅推定でキャップし、長いタグ名がコンテナを突き抜けないよう制御
  • task start--bind 0.0.0.0 --baseURL を追加し、LAN 内スマホからの実機確認を可能に

Test plan

  • task test:headless — 380件全テスト通過
  • 各テストファイルに tag cloud container stays within viewport テストを追加(getBoundingClientRect + scrollWidth で tag cloud コンテナ固有のオーバーフローを検証)
  • スマホ実機(iOS Safari)で /posts/ および /tags/* を目視確認

Closes #58

Summary by CodeRabbit

  • 新機能

    • Hugo開発サーバーをLAN内のスマートフォンからアクセス可能に設定しました。
  • バグ修正

    • タグクラウドがビューポート外にはみ出さないよう改善しました。
    • タグクラウドをレスポンシブ対応にし、画面サイズに応じた最適な高さで表示するようにしました。
  • テスト

    • タグクラウドがビューポート内に正しく収まることを検証するテストを追加しました。
  • その他

    • 設定ファイルを更新しました。

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

タグクラウドがモバイル表示時にビューポート外へはみ出す問題を、CSS レスポンシブ高さ、JavaScript 描画ロジック再構築、座標クランプ、リサイズ観測によって解決。複数言語環境での E2E テストで境界内配置を検証。同時に Hugo 開発サーバーを LAN 内デバイスからアクセス可能に構成。

Changes

タグクラウド モバイル対応&開発環境LAN対応

Layer / File(s) Summary
タグクラウド レスポンシブスタイル&テンプレート基盤
assets/css/custom.css, layouts/partials/tag-cloud.html
#tag-cloudclamp(240px, 50vw, 320px) でレスポンシブ高さを定義し、HTML テンプレートから固定高さのインラインスタイルを削除。CSS による統一的なレスポンシブ制御を実現。
タグクラウド 描画ロジック再構築&座標制御
assets/js/tag-cloud.ts
描画ロジックを renderTagCloud() に抽出、TagData 型を定義。座標を safeHalfW/safeHalfH でクランプしてコンテナ境界内に収め、アクティブタグのフォントサイズを文字数に応じて制限。ResizeObserver を導入してリサイズ時に自動再描画。
タグクラウド E2E テスト検証(複数環境)
tests/posts.en.spec.ts, tests/posts.ja.spec.ts, tests/tags.en.spec.ts, tests/tags.ja.spec.ts
英語・日本語、posts・tags ページの各テストスイートに、タグクラウドのオーバーフロー検証テストを追加。内部スクロール量と外部オーバーフロー量の合計が 0 であることを DOM 計測で確認。
開発環境 LAN アクセス対応(Taskfile&ドキュメント)
Taskfile.yml, README.md, .claude/settings.local.json, .vscode/settings.json
start タスクに LOCAL_IP 変数を導入し、Hugo サーバーを --bind 0.0.0.0 で 全インターフェースにバインド、--baseURL http://{{.LOCAL_IP}}:1313/ で動的ベース URL を設定。README を更新し、Claude 編集許可に Bash(task format *) を追加、getifaddr をスペル辞書に登録。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • sforzando/sfz.dev#53: タグクラウド機能の初期実装を行ったPR。本PRはその上に座標クランプやリサイズ対応を積み上げた改善版。
  • sforzando/sfz.dev#34: .claude/settings.local.jsonpermissions.allow エントリを変更する別の workflow PR。

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは「feat: ✨ fix tag cloud overflow on mobile」で、プルリクエストの主要な変更内容(モバイルでのタグクラウドのはみ出し修正)を正確に反映している。
Linked Issues check ✅ Passed Issue #58の要件(モバイル表示でタグクラウドがビューポート内に収まること)に対して、CSSのレスポンシブ設定、TypeScriptのポジショニング修正、ResizeObserverの導入、およびそれを検証するE2Eテストがすべて実装されている。
Out of Scope Changes check ✅ Passed .claude/settings.local.jsonの権限追加とVSCodeの辞書項目追加は、Taskfileのgetifaddrコマンド実行を支援するため、またコード品質を保つための周辺的かつ正当な変更である。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 058_fix_tag_cloud_mobile_scroll

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@shin-sforzando shin-sforzando self-assigned this May 19, 2026
@shin-sforzando shin-sforzando added the enhancement New feature or request label May 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@assets/js/tag-cloud.ts`:
- Around line 211-221: The JSON.parse calls in initTagCloud (parsing `raw` into
`parsed` and `rawPinned` into `pinnedTags`) can throw and currently abort
initialization; wrap each parse in a try/catch, validate the result
(Array.isArray for `parsed` and array/string items for `pinnedTags`), and on
error log the exception (e.g., console.error or a logger) and fall back safely
(return early if main `parsed` is invalid or set `pinnedTags = []` when pinned
parsing fails) so the tag cloud can continue rendering without crashing.

In `@README.md`:
- Line 73: The "start" task description and example connection URL are
inconsistent: update the README "start" (aliases: dev) entry so the example
access URL uses <LOCAL_IP> instead of 0.0.0.0 and ensure the explanatory text
clarifies LAN/mobile access; locate the line containing "* start:               
開始 - Hugo開発サーバー起動(LAN内スマホからもアクセス可)      (aliases: dev)" and replace the example
URL and any adjacent sentence that references 0.0.0.0 with a <LOCAL_IP>-based
instruction and brief note on how to find the local IP for mobile testing.

In `@tests/posts.en.spec.ts`:
- Around line 55-63: The current overflow calculation in the test (using
cloud.getBoundingClientRect(), internalScroll, and externalOverflow) only
measures right-side overflow (rect.right > viewport) and misses left-side
overflow (rect.left < 0); update the externalOverflow computation to account for
both sides by taking the max of 0 and rect.right -
document.documentElement.clientWidth as well as max of 0 and -rect.left (or
combine them), recompute overflow as internalScroll + both external overflows,
and keep the existing expect(overflow).toBe(0) assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bfe9d555-966a-44c9-80c5-8e520e91626e

📥 Commits

Reviewing files that changed from the base of the PR and between 33578d6 and 526c31f.

📒 Files selected for processing (11)
  • .claude/settings.local.json
  • .vscode/settings.json
  • README.md
  • Taskfile.yml
  • assets/css/custom.css
  • assets/js/tag-cloud.ts
  • layouts/partials/tag-cloud.html
  • tests/posts.en.spec.ts
  • tests/posts.ja.spec.ts
  • tests/tags.en.spec.ts
  • tests/tags.ja.spec.ts
💤 Files with no reviewable changes (1)
  • layouts/partials/tag-cloud.html

Comment thread assets/js/tag-cloud.ts
Comment on lines +211 to +221
const parsed: unknown = JSON.parse(raw)
if (!Array.isArray(parsed) || parsed.length === 0) return
const data = parsed as TagData[]

const activeTag = (container.dataset.activeTag ?? "").toLowerCase()

// pinnedCenterTags is configured in config/_default/params.toml [tagCloud]
const rawPinned = container.dataset.pinnedTags
const pinnedTags: string[] = rawPinned
? (JSON.parse(rawPinned) as string[]).map((s) => s.toLowerCase())
: []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

JSON.parseの例外未処理で初期化が中断されます

Line 211 と Line 220 の JSON.parse が失敗すると initTagCloud() 全体が落ち、タグクラウド描画が完全停止します。データ属性破損時のフォールバックを入れてください。

修正案(例外を握りつぶさず安全にフォールバック)
 function initTagCloud(): void {
   injectTagCloudStyle()

   const container = document.getElementById("tag-cloud")
   if (!container) return

   const raw = container.dataset.tags
   if (!raw) return

-  const parsed: unknown = JSON.parse(raw)
+  let parsed: unknown
+  try {
+    parsed = JSON.parse(raw)
+  } catch {
+    return
+  }
   if (!Array.isArray(parsed) || parsed.length === 0) return
   const data = parsed as TagData[]

   const activeTag = (container.dataset.activeTag ?? "").toLowerCase()

   // pinnedCenterTags is configured in config/_default/params.toml [tagCloud]
   const rawPinned = container.dataset.pinnedTags
-  const pinnedTags: string[] = rawPinned
-    ? (JSON.parse(rawPinned) as string[]).map((s) => s.toLowerCase())
-    : []
+  let pinnedTags: string[] = []
+  if (rawPinned) {
+    try {
+      const parsedPinned = JSON.parse(rawPinned)
+      if (Array.isArray(parsedPinned)) {
+        pinnedTags = parsedPinned
+          .filter((v): v is string => typeof v === "string")
+          .map((s) => s.toLowerCase())
+      }
+    } catch {
+      pinnedTags = []
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/js/tag-cloud.ts` around lines 211 - 221, The JSON.parse calls in
initTagCloud (parsing `raw` into `parsed` and `rawPinned` into `pinnedTags`) can
throw and currently abort initialization; wrap each parse in a try/catch,
validate the result (Array.isArray for `parsed` and array/string items for
`pinnedTags`), and on error log the exception (e.g., console.error or a logger)
and fall back safely (return early if main `parsed` is invalid or set
`pinnedTags = []` when pinned parsing fails) so the tag cloud can continue
rendering without crashing.

Comment thread README.md
* restart: 再起 - Hugo開発サーバーを再起動
* setup: 初回 - 初期セットアップ
* start: 開始 - Hugo開発サーバー起動 (aliases: dev)
* start: 開始 - Hugo開発サーバー起動(LAN内スマホからもアクセス可) (aliases: dev)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

start の説明と接続先URL案内を一致させてください。

タスク一覧はLANアクセス対応になっていますが、Start セクションの案内URLが 0.0.0.0 のままだと、モバイル確認時の導線として誤解を招きます。<LOCAL_IP> ベースの案内に更新してください。

✏️ 提案差分(READMEの案内文更新)
-Then, web server is available at [http://0.0.0.0:1313/](http://0.0.0.0:1313/).
+Then, web server is available at `http://<LOCAL_IP>:1313/` (example: `http://192.168.1.10:1313/`).
+`task start` sets the base URL automatically; use the URL shown in Hugo startup logs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 73, The "start" task description and example connection
URL are inconsistent: update the README "start" (aliases: dev) entry so the
example access URL uses <LOCAL_IP> instead of 0.0.0.0 and ensure the explanatory
text clarifies LAN/mobile access; locate the line containing "* start:          
開始 - Hugo開発サーバー起動(LAN内スマホからもアクセス可)      (aliases: dev)" and replace the example
URL and any adjacent sentence that references 0.0.0.0 with a <LOCAL_IP>-based
instruction and brief note on how to find the local IP for mobile testing.

Comment thread tests/posts.en.spec.ts
Comment on lines +55 to +63
const rect = cloud.getBoundingClientRect()
const internalScroll = cloud.scrollWidth - cloud.clientWidth
const externalOverflow = Math.max(
0,
rect.right - document.documentElement.clientWidth
)
return internalScroll + externalOverflow
})
expect(overflow).toBe(0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

左方向のはみ出し検知が欠けています

Line 57-60 は右側だけを評価しているため、rect.left < 0 のケースを見逃します。ビューポート内保証の回帰テストとしては片手落ちです。

修正案(左右両方を評価)
       const rect = cloud.getBoundingClientRect()
       const internalScroll = cloud.scrollWidth - cloud.clientWidth
-      const externalOverflow = Math.max(
-        0,
-        rect.right - document.documentElement.clientWidth
-      )
-      return internalScroll + externalOverflow
+      const rightOverflow = Math.max(
+        0,
+        rect.right - document.documentElement.clientWidth
+      )
+      const leftOverflow = Math.max(0, -rect.left)
+      return internalScroll + rightOverflow + leftOverflow
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rect = cloud.getBoundingClientRect()
const internalScroll = cloud.scrollWidth - cloud.clientWidth
const externalOverflow = Math.max(
0,
rect.right - document.documentElement.clientWidth
)
return internalScroll + externalOverflow
})
expect(overflow).toBe(0)
const rect = cloud.getBoundingClientRect()
const internalScroll = cloud.scrollWidth - cloud.clientWidth
const rightOverflow = Math.max(
0,
rect.right - document.documentElement.clientWidth
)
const leftOverflow = Math.max(0, -rect.left)
return internalScroll + rightOverflow + leftOverflow
})
expect(overflow).toBe(0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/posts.en.spec.ts` around lines 55 - 63, The current overflow
calculation in the test (using cloud.getBoundingClientRect(), internalScroll,
and externalOverflow) only measures right-side overflow (rect.right > viewport)
and misses left-side overflow (rect.left < 0); update the externalOverflow
computation to account for both sides by taking the max of 0 and rect.right -
document.documentElement.clientWidth as well as max of 0 and -rect.left (or
combine them), recompute overflow as internalScroll + both external overflows,
and keep the existing expect(overflow).toBe(0) assertion.

@claude

claude Bot commented May 19, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

@shin-sforzando shin-sforzando merged commit 3cb9568 into main May 19, 2026
6 checks passed
@shin-sforzando shin-sforzando deleted the 058_fix_tag_cloud_mobile_scroll branch May 19, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 🐛 fix tag cloud overflow on mobile

1 participant