<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0" xml:lang="ja">
<channel>
<title>kazumich.log</title>
<link>http://kazumich.com/</link>
<atom:link href="https://kazumich.com/rss2.xml" rel="self" type="application/rss+xml"/>
<language>ja</language>
<copyright>Copyright (C) 2026 kazumich.log All rights reserved.</copyright>
<lastBuildDate>Fri, 05 Jun 2026 09:22:20 +0900</lastBuildDate>
<generator>a-blog cms</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<xhtml:meta content="noindex" name="robots" xmlns:xhtml="http://www.w3.org/1999/xhtml"/><item>
<dc:creator>山本 一道</dc:creator>
<title>a-blog cms のグループユニットで Swiper を利用できる実装方法について</title>
<link>https://kazumich.com/cms/ablogcms-swiper-groupunit.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6379" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202606/6716d2a97d5cd628.png?v=20260605091955" class="js-smartphoto" data-group="6379"><img src="https://kazumich.com/media/001/202606/mode3_w1200-6716d2a97d5cd628.png?v=20260605091955" class="unit-id-6379" width="1280" height="720" loading="lazy" decoding="async" data-mid="770"></a></figure></div><h2 id="toc_1"><strong>はじめに</strong></h2><p>以前、<a class="link" href="https://kazumich.com/cms/photoCollage.html">PhotoCollage.js を a-blog cms のブログテーマに実装する記事</a>を書きました。</p><p>「グループユニットにクラスを付けるだけで、中の画像をいい感じに変換する」という実装方針だったのですが、先日 <strong>まったく同じやり方で </strong><a class="link" href="https://swiperjs.com/"><strong>Swiper</strong></a><strong> のスライダーを実装してほしい</strong>、と Claude Code にお願いしてみたところ、<strong>ほぼ 5 分で動くところまで終わってしまいました</strong>。</p><p>正直、解説記事を書くまでもない気もするのですが、「次に同じことをやるとき」や「人に頼むとき」に <strong>“この記事と同じ方式で Swiper を”</strong> と言えば済むように、やったことを書き残しておきます。</p><h2 id="toc_2"><strong>何ができるようになるか</strong></h2><ul><li><p>編集画面で<strong>グループユニットのクラスに </strong><code>js-swiper</code><strong> を指定するだけ</strong></p></li><li><p>そのグループ内に入れた画像が、自動で <strong>Swiper のスライダー</strong>になる</p></li><li><p>機能：<strong>ページネーション（ドット）／前後ナビ矢印／ループ／オートプレイ</strong></p></li><li><p>おまけで <strong>再生・停止ボタン</strong>（自動再生の一時停止 UI）も追加</p></li></ul><p>PhotoCollage と同じく「<strong>マークアップを JS で組み替える</strong>」方式なので、テンプレート側の編集は最小限です。</p><h2 id="toc_3"><strong>実装の全体像</strong></h2><pre><code class="language-none">.js-swiper（グループユニット）
  └─ a-blog cms が出力する画像（column-media &gt; img）   ← JS で収集
        ↓ 必要な構造に組み替え
  .swiper &gt; .swiper-wrapper &gt; .swiper-slide × N
        （+ pagination / navigation / 再生停止ボタン）
        ↓
  new Swiper(...) で初期化</code></pre><p>やることは 3 つだけです。</p><ol><li><p>テーマの <code>head</code> に Swiper（CDN）と自前の init / CSS を読み込む</p></li><li><p><code>js-swiper</code> を Swiper に変換する init スクリプトを置く</p></li><li><p>管理画面でグループユニットのクラスに <code>js-swiper</code> を設定する</p></li></ol><h2 id="toc_4"><strong>1. テーマへの読み込み</strong></h2><pre><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"&gt;
&lt;link rel="stylesheet" href="/css/swiper-custom.css"&gt;

&lt;script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"&gt;&lt;/script&gt;
&lt;script src="/js/swiper-init.js"&gt;&lt;/script&gt;</code></pre><h2 id="toc_5"><strong>2. 初期化スクリプト（</strong><code>/js/swiper-init.js</code><strong>）</strong></h2><p><code>js-swiper</code> の中の画像を集めて、Swiper の構造に組み替えてから初期化します。 PhotoCollage で学んだ <strong>「画像が 0 枚なら中身を壊さない」安全策</strong>や、<code>alt</code><strong> の引き継ぎ</strong>もそのまま入れています。</p><pre><code>/**
 * Swiper 初期化
 * グループユニットのクラスに「js-swiper」を指定した箇所を、
 * グループ内の画像から Swiper スライダーに組み替えて初期化する。
 * 機能: ページネーション(ドット) / 前後ナビ矢印 / ループ / オートプレイ
 */
document.addEventListener("DOMContentLoaded", function () {
  if (typeof Swiper === "undefined") {
    return;
  }

  var groups = document.getElementsByClassName("js-swiper");

  Array.prototype.forEach.call(groups, function (group) {
    // グループ内の画像を収集
    var imgs = group.querySelectorAll("img");

    // 画像が1枚も無い場合は何もしない（元の内容を壊さない安全策）
    if (imgs.length === 0) {
      return;
    }
    // すでに初期化済みなら二重処理しない
    if (group.querySelector(".swiper")) {
      return;
    }

    // Swiper が要求する構造を生成
    var swiperEl = document.createElement("div");
    swiperEl.className = "swiper";
    var wrapperEl = document.createElement("div");
    wrapperEl.className = "swiper-wrapper";

    Array.prototype.forEach.call(imgs, function (img) {
      var slideEl = document.createElement("div");
      slideEl.className = "swiper-slide";
      var newImg = document.createElement("img");
      newImg.setAttribute("src", img.getAttribute("src") || "");
      var alt = img.getAttribute("alt");
      if (alt) {
        newImg.setAttribute("alt", alt); // alt を引き継ぐ
      }
      newImg.setAttribute("loading", "lazy");
      slideEl.appendChild(newImg);
      wrapperEl.appendChild(slideEl);
    });
    swiperEl.appendChild(wrapperEl);

    // ページネーション（ドット）
    var paginationEl = document.createElement("div");
    paginationEl.className = "swiper-pagination";
    swiperEl.appendChild(paginationEl);

    // 前後ナビ矢印
    var prevEl = document.createElement("div");
    prevEl.className = "swiper-button-prev";
    var nextEl = document.createElement("div");
    nextEl.className = "swiper-button-next";
    swiperEl.appendChild(prevEl);
    swiperEl.appendChild(nextEl);

    // グループの中身を Swiper 構造に差し替え
    group.textContent = "";
    group.appendChild(swiperEl);

    // 初期化
    var swiper = new Swiper(swiperEl, {
      loop: true,
      slidesPerView: 1,
      spaceBetween: 0,
      autoplay: {
        delay: 3000,
        pauseOnMouseEnter: true,
        disableOnInteraction: false,
      },
      pagination: { el: paginationEl, clickable: true },
      navigation: { nextEl: nextEl, prevEl: prevEl },
    });

    // --- 再生/停止トグルボタン（自動再生の一時停止 UI） ---
    var SVG_NS = "http://www.w3.org/2000/svg";
    var makeIcon = function (children) {
      var svg = document.createElementNS(SVG_NS, "svg");
      svg.setAttribute("viewBox", "0 0 24 24");
      svg.setAttribute("width", "18");
      svg.setAttribute("height", "18");
      svg.setAttribute("aria-hidden", "true");
      children.forEach(function (c) {
        var el = document.createElementNS(SVG_NS, c.tag);
        Object.keys(c.attrs).forEach(function (k) { el.setAttribute(k, c.attrs[k]); });
        svg.appendChild(el);
      });
      return svg;
    };
    var iconPause = makeIcon([
      { tag: "rect", attrs: { x: "6", y: "5", width: "4", height: "14" } },
      { tag: "rect", attrs: { x: "14", y: "5", width: "4", height: "14" } },
    ]);
    var iconPlay = makeIcon([
      { tag: "path", attrs: { d: "M8 5v14l11-7z" } },
    ]);

    var toggleBtn = document.createElement("button");
    toggleBtn.type = "button";
    toggleBtn.className = "swiper-autoplay-toggle";

    var renderToggle = function () {
      var running = !!(swiper.autoplay &amp;&amp; swiper.autoplay.running);
      while (toggleBtn.firstChild) { toggleBtn.removeChild(toggleBtn.firstChild); }
      toggleBtn.appendChild(running ? iconPause : iconPlay);
      toggleBtn.setAttribute("aria-label", running ? "スライドショーを一時停止" : "スライドショーを再生");
      toggleBtn.setAttribute("aria-pressed", running ? "false" : "true");
    };

    toggleBtn.addEventListener("click", function () {
      if (!swiper.autoplay) { return; }
      if (swiper.autoplay.running) { swiper.autoplay.stop(); }
      else { swiper.autoplay.start(); }
      renderToggle();
    });

    swiper.on("autoplayStart", renderToggle);
    swiper.on("autoplayStop", renderToggle);

    swiperEl.appendChild(toggleBtn);
    renderToggle();
  });
});</code></pre><h2 id="toc_6"><strong>3. CSS（</strong><code>/css/swiper-custom.css</code><strong>）</strong></h2><p>Swiper 本体の CSS に最低限の調整を足します。</p><pre><code>/* 次の要素との間隔 */
.js-swiper {
  margin-bottom: 32px;
}

.js-swiper .swiper {
  width: 100%;
  /* ドット・矢印の色を白に（既定の青の主張を抑える） */
  --swiper-theme-color: #fff;
  --swiper-pagination-bullet-inactive-color: #fff;
}

/* 高さが異なるスライドがあるとき、低いスライドを縦中央に配置 */
.js-swiper .swiper-wrapper {
  align-items: center;
}

/* スライド画像をスライド幅にフィット */
.js-swiper .swiper-slide img {
  display: block;
  width: 100%;
  height: auto;
}

/* オートプレイ 再生/停止トグルボタン（白アイコン・右下配置） */
.js-swiper .swiper-autoplay-toggle {
  position: absolute;
  right: 12px;
  bottom: 8px;
  z-index: 11;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.4); /* 白の半透明で UI を統一 */
  color: #fff;
  cursor: pointer;
  line-height: 0;
}
.js-swiper .swiper-autoplay-toggle svg { fill: currentColor; }
.js-swiper .swiper-autoplay-toggle:hover { background: rgba(255, 255, 255, 0.6); }
.js-swiper .swiper-autoplay-toggle:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }</code></pre><h3>ポイント：</h3><ul><li><p><strong>色</strong>：Swiper はテーマ色を CSS 変数（既定の青 <code>#007aff</code>）で持っているので<code>--swiper-theme-color</code> を <code>#fff</code> にするだけでドット・矢印が白になります。</p></li><li><p><strong>高さ違い</strong>：Swiper は全スライドの高さを自動では揃えません<code>.swiper-wrapper</code>（flex）に <code>align-items: center</code> を付けると、低いスライドが行の中で**縦中央**に収まります<code>.swiper-slide</code> 側ではなく wrapper 側がポイント）。</p></li><li><p><strong>下マージン</strong><code>.js-swiper</code> に <code>margin-bottom</code> を付けるだけ。</p></li></ul><h2 id="toc_7"><strong>4. 管理画面の設定</strong></h2><p>「編集画面」メニューの「編集設定」から「グループユニット class属性」を設定します。</p><ul><li><p>グループユニットの <strong>クラス</strong>：<code>js-swiper</code></p></li><li><p><strong>ラベル</strong>：<code>swiper</code>（任意）</p></li></ul><p>投稿時にこのユニットグループを選び、中に画像を並べるだけ。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6379" data-no-lightbox="true"><figure class="" style="max-width: 100%;"><img src="https://kazumich.com/media/001/202606/mode3_w1200-a96ff373a45add95.png?v=20260605091157" class="unit-id-6379" width="860" height="238" loading="lazy" decoding="async" data-mid="769"></figure></div><h2 id="toc_9"><strong>まとめ</strong></h2><ul><li><p>PhotoCollage と<strong>まったく同じ「グループユニット＋クラス指定＋JS変換」方式</strong>で Swiper が実装できました。</p></li><li><p>しかも AI に「PhotoCollage と同じ方式で Swiper を」と頼んだら<strong>5 分</strong>。</p></li><li><p>同じ要領で、ライトボックスや別のスライダーライブラリにも応用できそうです。</p></li></ul><p>というわけで、今後はこの記事を指して「<strong>これと同じ方式で◯◯を</strong>」と言えば済む、という備忘録でした。</p>
</div>

















































































]]></description>
<category>CMS</category>
<guid isPermaLink="true">https://kazumich.com/cms/ablogcms-swiper-groupunit.html</guid>
<pubDate>Fri, 05 Jun 2026 09:21:27 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>PhotoCollage.js を a-blog cms のブログテーマに実装してみた</title>
<link>https://kazumich.com/cms/photoCollage.html</link>
<description><![CDATA[

































































<!-- テキスト -->




 










<p>2024年に公開した記事を、今でも使えるようにアップデートしました。</p>
<p>数年前のインターンの課題として「<strong>Facebook の複数枚写真をアップロードした際に、写真をレイアウトしてくれる JavaScriptライブラリを作ってみよう！</strong> 」というお題を出していた時期があります。</p>














































































<!-- 画像 -->
<div class="column-image-center acms-col-sm-12">
<a href="https://kazumich.com/archives/001/202401/large-8f2e979aa0fca03a.jpg" data-rel="SmartPhoto" data-group="group6258" data-caption="これはサンプルの1枚画像です">
<img class="columnImage"
 src="https://kazumich.com/archives/001/202401/8f2e979aa0fca03a.jpg"
 alt=""
 width="700"
 height="700">
</a>
<p class="caption">これはサンプルの1枚画像です</p>
</div>



































































































]]></description>
<category>CMS</category>
<guid isPermaLink="true">https://kazumich.com/cms/photoCollage.html</guid>
<pubDate>Thu, 04 Jun 2026 10:34:28 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>Claude Code SKILL.md で a-blog cms のテンプレートを実装する</title>
<link>https://kazumich.com/cms/skill-html2ablogcms.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6378" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202605/98ccf8cb2cde587b.png?v=20260531000509" class="js-smartphoto" data-group="6378"><img src="https://kazumich.com/media/001/202605/mode3_w1200-98ccf8cb2cde587b.png?v=20260531000509" class="unit-id-6378" width="1280" height="720" loading="lazy" decoding="async" data-mid="760"></a></figure></div><p>年末に「<a class="link" href="https://kazumich.com/cms/vibe-coding2ablogcms.html">バイブコーディング時代に a-blog cms が再評価される理由。 Gemini 3 / Nano Banana Pro で変わるWeb制作</a>」というブログ記事を書いているが、あれから半年で随分変わってきている。今回は、その大きな変化について書いてみようと思っています。</p><p>まず、最初にこの週末に作ったものを紹介しておきます。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-link="https://github.com/kazumich/skill-html2ablogcms" data-width="100%" data-eid="6378" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://github.com/kazumich/skill-html2ablogcms"><img src="https://kazumich.com/media/001/202605/mode3_w1200-eaa2cd44bea0c670.png?v=20260531001232" class="unit-id-6378" width="1200" height="600" loading="lazy" decoding="async" data-mid="761"></a></figure></div><p>Github.com に <a class="link" href="https://github.com/kazumich/skill-html2ablogcms">skill-htm2ablogcms</a> という Claude Code の SKILL.md を公開しました。これを利用すると、a-blog cms デベロッパーサイトにあるチュートリアル「<a class="link" href="https://developer.a-blogcms.jp/document/tutorial/acms-template/static2cms2025.html">静的HTMLサイトの「お知らせ」を部分的に CMS化してみよう</a>」を Claude Code が代わりに実装してくれるようになります。</p><p>まだ、試しに作ってみた段階なので、まだまだ精度が低いかもしれませんが、しばらく実験を行いアップデートしていくことにしようと思っております。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-link="https://ablogcms-nagoya.doorkeeper.jp/events/197435" data-width="100%" data-eid="6378" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://ablogcms-nagoya.doorkeeper.jp/events/197435"><img src="https://kazumich.com/media/001/202605/mode3_w1200-91de88ef0b34eba8.png?v=20260531002527" class="unit-id-6378" width="1200" height="630" loading="lazy" decoding="async" data-mid="762"></a></figure></div><p>2026年6月9日 16:00 - ベースキャンプ名古屋で開催する勉強会では、集まった皆さんの力（スキル）によって、より完成度の高い SKILL.md のアップデートをする会を開催します。 興味のある方は、ぜひ参加をご検討ください。</p><ul><li><p><a class="link" href="https://ablogcms-nagoya.doorkeeper.jp/events/197435">a-blog cms 勉強会 in 名古屋 2026/06</a></p></li></ul><h2>インストール</h2><p><code>~/.claude/skills/html2ablogcms/</code> にダウンロードしてきた html2ablogcms フォルダを設置します。</p><h2>使い方</h2><p class="">Claude Code で、対象のローカル a-blog cms 環境を開いて依頼するだけです。</p><pre><code>themes/sample の「お知らせ」部分を a-blog cms で動的化して</code></pre><p>スキルが着手前に前提（格納カテゴリー・作るビュー・テーマ名等）を確認し、テンプレート一式と SETUP.md を生成します。また、作成するモジュールIDをインポートするための YAMLファイルも作ってくれます。</p><h2>構成</h2><pre><code>html2ablogcms/
├── SKILL.md               # 判断手順 + a-blog cms の作法 + SETUP生成ルール
├── examples/
│   └── worked-example.md  # 変換の通し例（※写経用ではなく「型」を示す一例）
└── references/
    ├── developer-docs.md  # a-blog cms 公式ドキュメントのリンク集
    └── module-id-yaml.md  # モジュールID設定YAMLの生成＆インポート</code></pre>
</div>

















































































]]></description>
<category>CMS</category>
<guid isPermaLink="true">https://kazumich.com/cms/skill-html2ablogcms.html</guid>
<pubDate>Sun, 31 May 2026 00:28:44 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>そろそろ MAMP を卒業しませんか？DDEV + a-blog cms のローカル環境構築</title>
<link>https://kazumich.com/cms/ddev-ablogcms-install.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6372" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202603/90dc43dee3598a29.png?v=20260316165354" class="js-smartphoto" data-group="6372"><img src="https://kazumich.com/media/001/202603/mode3_w1200-90dc43dee3598a29.png?v=20260316165354" class="unit-id-6372" width="1280" height="720" loading="lazy" decoding="async" data-mid="753"></a></figure></div><p>Web 制作をはじめた頃、ローカル環境といえば <strong>MAMP</strong> でした。インストールして起動ボタンを押すだけで PHP と MySQL が動く。あのシンプルさは本当にありがたかったです。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">ただ、案件が増えてくると、こんな場面が増えてきませんか？</p><ul><li><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">今のバージョンだと PHP 8.1 じゃないと動かないんだけど、古い案件は 7.4 で使いたい</p></li><li><p>自分の環境では動いてるのに、なんで他の人の環境だと動かないんだろう</p></li><li><p>新しい Mac に乗り換えるたびに、MAMP の設定をゼロからやり直す</p></li></ul><p>MAMP が悪いわけじゃないんです。ただ、こういった悩みを解決するツールが今はあります。それが <strong>DDEV</strong> です。</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>DDEV ってなに？</h2><p class="">ローカル開発環境を本格的に整えようとすると、次のステップは <strong>Docker</strong> になります。プロジェクトごとに PHP や MySQL のバージョンを完全に分離できて、チームで環境を共有できる。理想的なのですが、Docker を直接使いこなすにはそれなりの学習コストがかかります。</p><p class="">DDEV はその Docker を裏側で動かしながら、<strong>Docker の知識がなくても使えるように</strong> ラップしてくれるツールです。難しい設定はすべて DDEV が引き受けてくれるので、やることはシンプルなコマンドを数回打つだけ。Docker の恩恵をそのまま受けながら、MAMP と同じくらいの手軽さで使えるのが特徴です。</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>最初に一度だけやること（Mac の環境整備）</h2><p class="">まずはあなたの Mac に開発環境を整えます。この作業は最初に一度やれば OK です。次の案件が始まっても、新しい a-blog cms サイトを作るたびにやり直す必要はありません。</p><h3>Homebrew をインストールする</h3><p class=""><a class="link" href="https://brew.sh/ja/">https://brew.sh/ja/</a></p><p class="">Mac のパッケージマネージャーです。すでにインストール済みの方は読み飛ばしてください。インストールされているか分からない人は、ターミナルで以下を実行してみてください。</p><pre><code>brew --version</code></pre><p class="">バージョンの表示が出てくるようなら次に進んでください。command not found: brew と出てくるのであれば、以下のコマンドを実行します。</p><pre><code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</code></pre><p>公式サイトに書かれているコマンドと一緒ですが、心配な方はサイトの以下の部分をクリックしてコードを取得ください。<br></p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-link="https://brew.sh/ja/" data-width="100%" data-eid="6372" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://brew.sh/ja/" target="_blank" rel="noopener noreferrer"><img src="https://kazumich.com/media/001/202603/mode3_w1200-043f81df4b0d0032.png?v=20260316170809" class="unit-id-6372" width="983" height="563" loading="lazy" decoding="async" data-mid="756"></a></figure></div><h3>Docker をインストールする</h3><p><a class="link" href="https://www.docker.com/ja-jp/">https://www.docker.com/ja-jp/</a></p><p class="">DDEV は内部で Docker を使って動いています。まずは Docker Desktop をインストールしてください。こちらは、アプリケーションフォルダに Docker があるかで判断ができます。</p><p class="">インストール後、Docker が起動してください。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-link="https://www.docker.com/ja-jp/" data-width="100%" data-eid="6372" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://www.docker.com/ja-jp/" target="_blank" rel="noopener noreferrer"><img src="https://kazumich.com/media/001/202603/mode3_w1200-e006584a1af4cf0f.png?v=20260316170912" class="unit-id-6372" width="1003" height="351" loading="lazy" decoding="async" data-mid="757"></a></figure></div><h3>DDEV をインストールする</h3><p>上記でインストールした Homebrew を利用して DDEV をインストールできます。このコマンドを実行する場所は、どこでも同じ結果になりますので、あまり気にする必要はありません。</p><pre><code>brew install ddev/ddev/ddev</code></pre><p>続けて HTTPS 対応のための設定も一度だけ実行しておきます。 </p><pre><code>mkcert -install</code></pre><p>これで準備完了です。Docker と DDEV のインストールは最初の一度だけ。</p><p>次の案件からは不要です。</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>案件ごとにやること</h2><h3>開発環境のディレクトリを作る</h3><p>Finder からフォルダを作ってもいいですが、ここまでターミナルで作業をしているので、ディレクトリを作成して、その中に移動します。</p><pre><code>mkdir my-ablogcms-site
cd my-ablogcms-site</code></pre><h3>a-blog cms 環境を準備する</h3><p>この install.sh では DDEV の config 設定と、この次に実行するコマンドの設定を行います。</p><pre><code>curl -fsSL https://developer.a-blogcms.jp/ddev/install.sh | bash</code></pre><p>この1文での作業内容は以下のようになります。</p><ul><li><p>DDEV が未設定の場合は ddev config を実行して開発環境を初期化します（PHP 8.4 / MySQL 8.4 / docroot=web）</p></li><li><p>.ddev/commands/host/acms という DDEV カスタムコマンド を配置します</p></li><li><p>scripts/install.sh を配置します（a-blog cms のインストーラーを取得するスクリプト）</p></li><li><p>ddev start を実行して開発環境を起動します</p></li></ul><pre><code>ddev acms setup</code></pre><p>こちらの処理は以下のようになります。</p><ul><li><p>簡単セットアップ用のパッケージをダウンロードします</p></li><li><p>ダウンロードした install.zip を一時ディレクトリに展開します</p></li><li><p>setup.php を web ディレクトリに配置</p></li><li><p>セットアップ画面をブラウザで表示</p></li></ul><h3>a-blog cms インストール</h3><p>ブラウザから必要な設定を行い、テーマやログインする管理ユーザーの作成を行います。（データベースの設定は ddev の設定が済んだ状態で画面が表示されます）</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">インストール後のよく使うコマンド</h3><p class="">初回は ddev acms setup で ddev start を兼ねているのですが、次回動かす時や、止める時などのいつくかのコマンドを紹介しておきます。</p><div class="tableWrapper acms-table-scrollable"><table class="js-table-unit-scroll-hint" data-scrollable="true"><tbody><tr><th colspan="1" rowspan="1"><p>コマンド</p></th><th colspan="1" rowspan="1"><p>説明</p></th></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev start</strong></p></td><td colspan="1" rowspan="1"><p>プロジェクトを起動する</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev stop</strong></p></td><td colspan="1" rowspan="1"><p>プロジェクトを停止する（データは保持される）</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev restart</strong></p></td><td colspan="1" rowspan="1"><p>プロジェクトを再起動する</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev poweroff</strong></p></td><td colspan="1" rowspan="1"><p>すべてのプロジェクトとコンテナを停止する</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev describe</strong></p></td><td colspan="1" rowspan="1"><p>プロジェクトの接続情報を表示する</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev mailpit</strong></p></td><td colspan="1" rowspan="1"><p>Mailpit をブラウザで開く</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev delete</strong></p></td><td colspan="1" rowspan="1"><p>プロジェクトとデータベースを削除する</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev sequelace</strong></p></td><td colspan="1" rowspan="1"><p>Sequel Ace でデータベースを開く</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>ddev import-db --src=</strong></p></td><td colspan="1" rowspan="1"><p>続けて SQLのファイルを指定してインポート</p></td></tr></tbody></table></div><p>このコマンドの実行は、今回の説明では my-ablogcms-site のディレクトリに移動して実行すると覚えておけば大丈夫です。</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>phpMyAdmin の設定</h2><p class="">MAMP では MySQL の設定を phpMyAdmin という Web UI が提供されていました。同じものを利用できるようにするためには add-on という機能から追加します。</p><pre><code>ddev add-on get ddev/ddev-phpmyadmin</code></pre><p>これは SSL の設定のように ddev 全体ではなく、案件ごと・プロジェクトごとになりますので、初回は ddev acms setup の前に事前に実行しておくのが、これまで MAMP を利用していた人にはオススメです。</p><p>アクセスする際には、</p><pre><code>ddev phpmyadmin</code></pre><p>とターミナルから実行してください。ブラウザが切り替わります。</p><p>また、Sequel Ace という Mac 用のアプリをインストールしているようであれば、</p><pre><code>ddev sequelace</code></pre><p>の方が専用のアプリケーションが動くことになり便利かもしれません。</p>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>Mailpit を活用しよう</h2><p>ローカル開発中に「フォームからメールが送られるか確認したい」という場面は多いと思います。でも、テストのたびに本物のメールアドレスに送信してしまうのは困りますよね。DDEV には <strong>Mailpit</strong> というメールテストツールが最初から組み込まれています。追加のインストールは一切不要です。</p><h3>Mailpit ってどんなもの？</h3><p class="">PHP から送信されたメールを<strong>外部には送らず、すべてキャッチ</strong>して Web 画面で確認できるツールです。実際のメールアドレスに誤送信してしまう心配がなく、件名・本文・宛先・HTML の見た目までウェブメールのように安全にチェックできます。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">MAMP を使っていたころはメール送信のテストに別途ツールを用意する必要がありましたが、DDEV ではこれも最初から揃っています。</p><h3>Mailpit にアクセスする</h3><p>以下のコマンドでブラウザから Mailpit が開きます。</p><pre><code>ddev mailpit</code></pre><p>または URL で直接アクセスすることもできます。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-bash">https://my-ablogcms-site.ddev.site:8026</code></pre><h4 class="text-text-100 mt-2 -mb-1 text-base font-bold">送信されたメールを確認する</h4><p>a-blog cms のお問い合わせフォームや会員登録など、メール送信を伴う操作をローカルで実行すると、Mailpit の画面に受信メールとして表示されます。件名・本文・HTML の見た目・宛先などをそのまま確認できます。確認が終わったら画面上から削除でき、何度テストしても外部への影響はありません。</p><blockquote><p>Mailpit はローカル専用のツールです。外部のメールアドレスには届きません。本番環境では通常通り外部にメールが送信されます。</p></blockquote>
</div>




























































<div class="column-block-editor">
<div data-type="horizontalRule"><hr></div><h2>さいごに</h2><p>MAMP はシンプルで使いやすく、ローカル環境の入門として本当によくできたツールです。ただ、案件が増えれば増えるほど「環境の管理」が少しずつ辛くなってくるのも事実です。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">DDEV に乗り換えることで、PHP や MySQL のバージョン管理、チームでの環境共有、メールのテスト環境まで、開発まわりの悩みがまとめて解決します。「Docker って難しそう」という印象があるかもしれませんが、DDEV を使う分には Docker の知識はほぼ不要です。まずは今回の手順通りに一度試してみてください。慣れてしまえば、もう MAMP には戻れなくなるはずです。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">DDEV を試してみるついでに、<strong>a-blog cms</strong> も試してみてください！</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-link="https://www.a-blogcms.jp/" data-width="100%" data-eid="6372" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://www.a-blogcms.jp/" target="_blank" rel="noopener noreferrer"><img src="https://kazumich.com/media/001/202603/mode3_w1200-8b00da13314b4376.png?v=20260316180103" class="unit-id-6372" width="1360" height="820" loading="lazy" decoding="async" data-mid="758"></a></figure></div>
</div>

















































































]]></description>
<category>CMS</category>
<guid isPermaLink="true">https://kazumich.com/cms/ddev-ablogcms-install.html</guid>
<pubDate>Mon, 16 Mar 2026 20:31:52 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>AI を活用して大量のウェブサイトのキャプチャー作業を自動化する</title>
<link>https://kazumich.com/web-production/playwright-site-capture.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6370" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202603/c8574cc7b6f75f74.png?v=20260315120432" class="js-smartphoto" data-group="6370"><img src="https://kazumich.com/media/001/202603/mode3_w1200-c8574cc7b6f75f74.png?v=20260315120432" class="unit-id-6370" width="1280" height="720" loading="lazy" decoding="async" data-mid="751"></a></figure></div><h2>はじめに — WCAN mini 2026 Vol.1 懇親会での話</h2><p>先日、WCAN mini 2026 Vol.1 に参加してきました。今回のテーマは AI。セッションでもさまざまな AI 活用の話題が上がっていましたが、懇親会でも引き続き「最近こんなふうに AI を使っている」という話で盛り上がりました。</p><p>その中で出てきたのが、<strong>サイトリニューアル前のキャプチャ作業</strong> の話です。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6370" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202603/2c6f56c7d300382d.jpg?v=20260315132711" class="js-smartphoto" data-group="6370"><img src="https://kazumich.com/media/001/202603/mode3_w1200-2c6f56c7d300382d.jpg?v=20260315132711" class="unit-id-6370" width="2364" height="1330" loading="lazy" decoding="async" data-mid="752"></a></figure></div><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">サイトをリニューアルするとき、まず現状のサイトがどんな状態になっているかを把握する作業が発生します。ページ数が多いサイトだと、トップページ・下層ページ・カテゴリページ……と、確認すべき URL が数十〜数百になることも珍しくありません。それを一枚一枚、手作業でブラウザからスクリーンショットを撮っていくのは、地味に時間がかかる作業です。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">そこで紹介したのが、<strong>Playwright</strong> を使ったキャプチャの一括取得です。</p>
</div>




























































<div class="column-block-editor">
<h2>Playwright とは</h2><p>Playwright は、Microsoft が開発したオープンソースのブラウザ自動操作フレームワークです。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">「ブラウザ自動操作」というと難しそうに聞こえますが、要するに <strong>人間がブラウザでやっている操作を、プログラムで自動的に実行できるようにするもの</strong> です。「ページを開く」「スクリーンショットを撮る」「ボタンをクリックする」「フォームに入力して送信する」といった操作を、コードで記述して自動化できます。</p><h3>ヘッドレスブラウザとして動作する</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Playwright が操作するブラウザは、通常の Chrome や Edge のように画面（ウィンドウ）を表示しません。画面を持たずにバックグラウンドで動作するブラウザのことを <strong>ヘッドレスブラウザ</strong> と呼びます。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">画面を表示しない分、処理が高速で、サーバー上でも動作させることができます。</p><h3>3つのブラウザエンジンに対応している</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Playwright の大きな特徴のひとつが、主要な3つのブラウザエンジンを切り替えて使える点です。</p><div class="tableWrapper acms-table-scrollable"><table class="js-table-unit-scroll-hint" data-scrollable="true"><tbody><tr><th colspan="1" rowspan="1"><p>指定名</p></th><th colspan="1" rowspan="1"><p>対応するブラウザ</p></th></tr><tr><td colspan="1" rowspan="1"><p>chromium</p></td><td colspan="1" rowspan="1"><p>Chrome / Edge 相当</p></td></tr><tr><td colspan="1" rowspan="1"><p>firefox</p></td><td colspan="1" rowspan="1"><p>Firefox 相当</p></td></tr><tr><td colspan="1" rowspan="1"><p>webkit</p></td><td colspan="1" rowspan="1"><p>Safari 相当</p></td></tr></tbody></table></div><p>コードの中でエンジンを切り替えるだけで、それぞれのブラウザでレンダリングした結果を取得できます。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">キャプチャ取得の用途であれば <strong>Chromium（Chrome / Edge 相当）</strong> を使うのが一般的です。一方、E2E テスト（システム全体の動作確認テスト）の現場では「Safari でも崩れていないか確認したい」という場面で WebKit エンジンが活躍します。Mac を持っていない Windows 環境でも Safari 相当の表示確認ができるのは、現場では重宝されています。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">なお、WebKit については Apple が公式配布している Safari そのものではなく、WebKit エンジンをベースにした Playwright 独自ビルドです。完全な Safari の再現ではありませんが、「Safari 系エンジンで大きく崩れていないか」の確認用途としては十分に機能します。</p><h3>対応言語</h3><p class="">JavaScript（Node.js）、Python、Java、.NET（C#）など複数の言語から利用できます。今回紹介するキャプチャ取得のスクリプトは <strong>Node.js</strong> で作成します。</p><h3>主な用途</h3><p class="">Playwright はさまざまな用途で活用されています。</p><ul><li><p class=""><strong>E2E テスト</strong> — ユーザー操作を再現してアプリケーション全体の動作を自動検証する</p></li><li><p class=""><strong>スクリーンショットの一括取得</strong> — 複数ページのキャプチャをまとめて生成する（今回の用途）</p></li><li><p class=""><strong>スクレイピング</strong> — JavaScript でレンダリングされる動的なページからデータを取得する</p></li><li><p class=""><strong>PDF 生成</strong> — ページを PDF として自動出力する</p></li></ul>
</div>




























































<div class="column-block-editor">
<h2>Playwright のインストール</h2><h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">事前準備：Node.js のインストール </h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">Playwright を Node.js で使うには、あらかじめ Node.js がインストールされている必要があります。まだインストールしていない場合は、<a class="underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current link" href="https://nodejs.org/">Node.js 公式サイト</a>から LTS 版をダウンロードしてインストールしてください。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">インストールできたか確認するには、ターミナル（Mac の場合はターミナル.app、Windows の場合はコマンドプロンプトや PowerShell）で次のコマンドを実行します。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"></p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-bash">node -v</code></pre><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">バージョン番号が表示されれば OK です。</p><h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">プロジェクトフォルダの作成と初期化</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">作業用のフォルダを作り、その中で以下のコマンドを順番に実行します。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-bash">npm init -y
npm install playwright
npx playwright install</code></pre><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><code>npx playwright install</code> は、Chromium・Firefox・WebKit の各ブラウザを Playwright 用にダウンロードするコマンドです。少し時間がかかりますが、これで準備完了です。</p>
</div>




























































<div class="column-block-editor">
<h2 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">キャプチャ取得スクリプトを用意する</h2><h3>AI にコードを作ってもらう</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">スクリプトは自分で書かなくても大丈夫です。Claude や ChatGPT などの AI チャットに、次のようなプロンプトを投げてみてください。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code>URL のリストが書かれたテキストファイル（urls.txt）を読み込んで、各ページのスクリーンショットをフルページで PNG として保存する。Node.js のスクリプトを Playwright で作ってください。ファイル名は連番にしてください。</code></pre><p>これだけで、動作するコードを生成してくれます。「PC とスマホの両方のサイズで撮りたい」「ファイル名をページタイトルにしたい」など、追加の要望も自然な言葉で伝えるだけで調整してもらえます。</p><h3 class="text-text-100 mt-2 -mb-1 text-base font-bold">AI が生成するコードの例</h3><p>上記のプロンプトで生成されるコードは、おおむね次のような内容になります。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-javascript">const { chromium } = require('playwright');
const fs = require('fs');

(async () =&gt; {
  const urls = fs.readFileSync('urls.txt', 'utf-8')
    .split('\n')
    .map(url =&gt; url.trim())
    .filter(url =&gt; url.length &gt; 0);

  const browser = await chromium.launch();
  const page = await browser.newPage();

  for (let i = 0; i &lt; urls.length; i++) {
    await page.goto(urls[i]);
    await page.screenshot({
      path: `screenshot-${String(i + 1).padStart(3, '0')}.png`,
      fullPage: true
    });
    console.log(`撮影完了: ${urls[i]}`);
  }

  await browser.close();
})();</code></pre><h2>実行してみる</h2><h3>URL リストのファイルを用意する</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">同じフォルダに <code>urls.txt</code> というファイルを作成し、キャプチャしたい URL を1行に1つずつ書いていきます。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code>https://example.com/
https://example.com/about/
https://example.com/service/
https://example.com/contact/</code></pre><h3>スクリプトを実行する</h3><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">スクリプトファイル（例：<code>capture.js</code>）を同じフォルダに保存し、次のコマンドで実行します。</p><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5"><code class="language-bash">node capture.js</code></pre><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">実行が完了すると、フォルダの中に各ページのスクリーンショットが PNG ファイルとして保存されます。数十ページであっても、数分で完了します。</p><h2>まとめ</h2><p>Playwright は、ブラウザ操作を自動化するための強力なツールです。ヘッドレスブラウザとして動作し、Chromium・Firefox・WebKit の3つのエンジンを切り替えて使える柔軟性が特徴です。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">今回紹介したキャプチャの一括取得は、Playwright の機能のほんの一部に過ぎません。E2E テストやスクレイピング、PDF 生成など、Web 制作の現場でさまざまな自動化に応用できます。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">「こういうことができるツールがある」と知っておくだけで、日々の作業の中で「これも自動化できるかもしれない」という発想が生まれます。まずは今回のキャプチャ取得から、Playwright を試してみてください。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"></p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">実際の私が使ってる batch.js は以下のリンクで公開しています。自分で作ってみるのがオススメですが、参考にご覧ください。</p><p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><a class="link" href="https://github.com/kazumich/web-capture">https://github.com/kazumich/web-capture</a></p>
</div>

















































































]]></description>
<category>ウェブ制作</category>
<guid isPermaLink="true">https://kazumich.com/web-production/playwright-site-capture.html</guid>
<pubDate>Sun, 15 Mar 2026 12:00:36 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>HTML ファーストで始める View Transitions API（htmx 編）</title>
<link>https://kazumich.com/web-production/view-transitions-htmx202601.html</link>
<description><![CDATA[

































































<!-- テキスト -->




 










<p>前回は、<a href="view-transitions202601.html">HTML ファーストで始める View Transitions API 入門</a> を書きました。その中では、リロードを伴うページ遷移である「cross-document」における View Transitions API の利用方法を解説しました。</p>
<p>今回は「same-document」という、ページ遷移を伴わず同一 HTML ドキュメント内で DOM が更新されるケースを取り上げ、View Transitions API を <strong>htmx</strong> でどのように実装できるかを見ていきます。</p>
<h2>same-document / cross-document の整理</h2>
<p>View Transitions API では、適用されるシーンによって same-document と cross-document の 2 つのパターンに分けて考えることができます。この分類は SPA / MPA の違いではなく、ブラウザにとってページ遷移が発生したかどうか という観点で区別されます。</p>
<h3>cross-document</h3>
<p>cross-document は、異なる HTML ドキュメント間でページ遷移が発生するケースです。</p>
<ul>
<li><code>&lt;a href="..."&gt;</code> による通常のページ遷移</li>
<li>ブラウザが新しい HTML を読み込む</li>
<li>DOM はすべて再構築される</li>
</ul>
<p>このような場合に、遷移前と遷移後のページ全体を対象として View Transition が適用されます。</p>
<pre><code class="css">@view-transition {
  navigation: auto;
}
</code></pre>
<p>上記の指定を行うだけで、ページ遷移時に自動的なクロスフェードが有効になります。</p>
<h3>same-document</h3>
<p>same-document は、ページ遷移を伴わず、同一 HTML ドキュメント内で DOM が更新されるケースです。</p>
<ul>
<li>JavaScript による DOM 操作</li>
<li>htmx による部分更新</li>
<li>SPA における画面切り替え</li>
</ul>
<p>などが該当します。この場合、HTML ドキュメント自体は切り替わらず、更新前後の DOM の差分に対して View Transition が適用されます。</p>
<h2>違いの整理</h2>
<table>
<thead>
<tr><th>項目</th><th>cross-document</th><th>same-document</th></tr>
</thead>
<tbody>
<tr><td>ページ遷移</td><td>あり</td><td>なし</td></tr>
<tr><td>HTML ドキュメント</td><td>切り替わる</td><td>同一</td></tr>
<tr><td>DOM の更新範囲</td><td>全体</td><td>一部</td></tr>
<tr><td>主なトリガー</td><td><code>&lt;a href&gt;</code></td><td>JavaScript / htmx</td></tr>
<tr><td>主な用途</td><td>MPA</td><td>部分更新 / SPA 的 UI</td></tr>
</tbody>
</table>
<p>また、htmx は基本的に JavaScript を書かず、HTML の属性を記述するだけで実装できる点も大きな特徴です。そのため、View Transitions API と組み合わせた場合でも、JavaScript による遷移制御やアニメーション処理を意識することなく、same-document の画面更新に自然な動きを加えることができます。</p>
<p>本記事では、この「HTML フラグメントの差し替え」と「same-document View Transitions」を組み合わせた実装を通して、HTML ファーストな設計のまま UX を向上させる方法 を具体的に見ていきます。</p>
<h2>demo</h2>
<p>サンプルサイトでは、サブカラムに <code>index.html</code> と初期表示時の HTML ファイル名を表示しています。サブカラムを書き換えることなく、メインカラムのコンテンツのみが切り替わっていることを確認できます。また、ページ遷移後にブラウザをリロードすると、そのページの HTML ファイル名に変わる点も確認できるようにしています。</p>
<p><img src="https://storage.googleapis.com/zenn-user-upload/db1c03608a61-20260127.png" alt="" /></p>
<p>https://kazumich.com/demo/view-transition/3_htmx/index.html</p>
<p>https://github.com/kazumich/view-transition</p>
<p>github に公開されている 2_view-transition をベースに 3_htmx へ変更していく流れを、以降で解説していきます。</p>
<h2>htmx を利用可能にする</h2>
<p>htmx は、特別なビルド環境や設定を必要とせず、JavaScript ファイルを 1 つ読み込むだけで利用できます。既存の HTML に後から追加できる点も、大きな特徴です。</p>
<h3>CDN から読み込む</h3>
<p>まずは、<code>&lt;head&gt;</code> 内に、以下を書きます。</p>
<pre><code class="html">&lt;script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.js"&gt;&lt;/script&gt;
</code></pre>
<p>これだけで、hx-get や hx-post などの htmx 用属性が有効になります。 JavaScript の初期化コードは不要です。 CDN からの読み込みは、検証や実験用途として手軽に試す場合 には便利ですが、実運用のサイトでは htmx をダウンロードし、自サーバーに配置して利用する ことをおすすめします。</p>
<h3>View Transitions API の利用</h3>
<p>次の設定を追加することで、JavaScript を個別に記述することなく、htmx による same-document 更新で View Transitions API を利用できるようになります。</p>
<pre><code class="html">&lt;script&gt;
    htmx.config.globalViewTransitions = true;
&lt;/script&gt;
</code></pre>
<h2>一覧ページから詳細ページへの遷移を htmx 化する</h2>
<p>View Transitions API の基本設定については、<a href="https://zenn.dev/kazumich/articles/d3c1fd8ef2b93c">HTML ファーストで始める View Transitions API 入門</a> で解説していますので、ここでは htmx 化に関する変更点のみを見ていきます。</p>
<h3>変更前</h3>
<p>通常の一覧ページから詳細ページへのリンクは、次のようなシンプルな <code>&lt;a&gt;</code> タグになります。</p>
<pre><code class="html">&lt;a href="entry-1.html"&gt;
</code></pre>
<p>この場合、リンククリック時にページ全体が再読み込みされ、cross-document のページ遷移が発生します。</p>
<h3>変更後</h3>
<p>これを htmx で same-document の部分更新に変更します。</p>
<pre><code class="html">&lt;a href="entry-1.html"
  hx-get="entry-1.html"
  hx-target="#main-content"
  hx-swap="innerHTML"
  hx-select="#main-content"
  hx-push-url="true"
&gt;
</code></pre>
<p>entry-1.html 以外のリンクについて、同様に追記していきます。</p>
<h3>各属性の役割</h3>
<ul>
<li><p><code>hx-get</code>
リンククリック時に、指定した URL へ HTTP GET リクエストを送信します。</p>
</li>
<li><p><code>hx-target</code>
サーバーから返された HTML を差し替える対象要素を指定します。
この例では <code>#main-content</code> の中身だけが更新されます。</p>
</li>
<li><p><code>hx-swap="innerHTML"</code>
対象要素の内側を、取得した HTML に置き換えます。</p>
</li>
<li><p><code>hx-select</code>
取得した HTML 全体ではなく、その中から指定した要素だけを抜き出して使用します。
一覧ページと詳細ページで共通のレイアウトを使っている場合に有効です。</p>
</li>
<li><p><code>hx-push-url="true"</code>
ブラウザの URL をリンク先の URL に更新し、履歴にも追加します。
これにより、戻る・進む操作や URL の共有が通常のページ遷移と同じ感覚で行えます。</p>
</li>
</ul>
<h3>この変更による挙動</h3>
<ul>
<li>ページ全体はリロードされない</li>
<li><code>#main-content</code> のみが差し替わる</li>
<li>View Transitions API により、same-document の画面遷移としてアニメーションが適用される</li>
</ul>
<p>つまり、<strong>HTML の構造を保ったまま、MPA の設計で SPA に近い体験を実現</strong>できます。</p>
<h3>補足：<code>href</code> を残す理由</h3>
<p><code>href</code> 属性は削除せず、そのまま残しています。これにより、</p>
<ul>
<li>JavaScript が無効な環境でも通常のリンクとして機能する</li>
<li>検索エンジンやクローラにも正しく認識される</li>
</ul>
<p>といった、<strong>HTML ファーストな設計</strong> を維持できます。</p>
<h2>詳細ページへの遷移も htmx 化する</h2>
<p>パンくずリストのリンクから、一覧ページへ戻る動作も htmx 化します。</p>
<pre><code class="html">&lt;a href="index.html"
  hx-get="index.html"
  hx-target="#main-content"
  hx-swap="innerHTML"
  hx-select="#main-content"
  hx-push-url="true"&gt;ホーム&lt;/a&gt;
</code></pre>
<p>ここで指定している属性の役割は、一覧ページから詳細ページへの遷移と同じです。そのため、個別の解説は省略します。</p>
<h2>HTML ファーストな same-document 更新</h2>
<p>htmx による画面更新は、ページ遷移を伴わない <strong>same-document</strong> の更新であり、HTML ファーストな設計と非常に相性が良いのが特徴です。そのため、サーバーから返す HTML はページ全体である必要はなく、<strong>表示に必要な部分だけを返す構成でも問題ありません</strong>。</p>
<p>このように、最初から部分的な HTML（HTML フラグメント）を返す設計であれば、<code>hx-select</code> を指定せずに、そのまま <code>hx-target</code> へ差し替えることができます。</p>
<pre><code class="html">&lt;a href="entry-1.html"
  hx-get="entry-1.html"
  hx-target="#main-content"
  hx-swap="innerHTML"
  hx-push-url="true"
&gt;
</code></pre>
<p>entry-1.html のファイルの中身を <code>&lt;div id="main-content"&gt; 〜 &lt;/div&gt;</code> だけにしても動作しますので、よろしければ一度お試しください。</p>
<h3>それでも hx-select を使っている理由</h3>
<p>本記事のデモでは、各ページを <strong>通常の HTML ファイルとしても成立させる</strong> ことを重視しています。そのため、一覧ページ・詳細ページともにページ全体の HTML を返しつつ、<code>hx-select</code> を使って表示に必要な部分だけを抜き出しています。</p>
<p>この構成により、</p>
<ul>
<li>JavaScript が無効な環境でも通常のページ遷移が可能</li>
<li>リロード時にも正しいページが表示される</li>
<li>htmx を外しても HTML として成立する</li>
</ul>
<p>といった、<strong>HTML ファーストな設計</strong> を維持できます。</p>
<h2>まとめ</h2>
<p>View Transitions API は、ページ遷移を伴う <strong>cross-document</strong> だけでなく、ページ遷移を伴わない <strong>same-document</strong> の更新にも適用できます。htmx を利用することで、JavaScript をほとんど書かずに、HTML の属性だけで same-document な画面更新を実装できる点は、大きな利点です。</p>
<p>same-document の更新では、ページ全体の HTML を返す必要はなく、<strong>表示に必要な部分だけの HTML（HTML フラグメント）を返す設計でも成立</strong>します。一方で、通常の HTML ファイルとしても成立させたい場合には、<code>hx-select</code> を使って表示部分を切り出す構成も有効です。</p>
<p>重要なのは、フレームワークありきで SPA 化するのではなく、<strong>HTML の構造とブラウザの挙動を前提に、必要な部分だけを same-document 化する</strong> という考え方です。</p>
<p>まずは cross-document の View Transitions API から始め、必要な箇所だけ htmx による same-document 更新を取り入れる。そのように段階的に導入することで、<strong>HTML ファーストな設計を保ったまま UX を向上</strong>させることができます。</p>





















































































































]]></description>
<category>ウェブ制作</category>
<guid isPermaLink="true">https://kazumich.com/web-production/view-transitions-htmx202601.html</guid>
<pubDate>Sat, 31 Jan 2026 12:16:20 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>HTML ファーストで始める View Transitions API 入門</title>
<link>https://kazumich.com/web-production/view-transitions202601.html</link>
<description><![CDATA[

































































<!-- テキスト -->




 










<h2>View Transitions API とは</h2>
<p>View Transitions API は、ページ遷移や画面切り替えの際に、<strong>前後の画面をつなぐようなアニメーションを付与できる Web 標準の API</strong> です。従来であれば JavaScript やフレームワークに依存しがちだった画面遷移の演出を、<strong>ブラウザが主導して処理</strong> してくれる点が特徴です。</p>
<p>重要なのは、この API が <strong>見た目の体験を改善するためのもの</strong> であり、通信の高速化やデータ取得方法を変えるものではない、という点です。</p>
<p><img src="https://storage.googleapis.com/zenn-user-upload/d12c9feab9a7-20260119.png" alt="demo" /></p>
<p>https://kazumich.com/demo/view-transition/2_view-transition/index.html</p>
<p>https://github.com/kazumich/view-transition</p>
<h2>いま、制作現場で使える理由</h2>
<p>以前は View Transitions API を使うには、対応ブラウザやフラグの存在を強く意識する必要がありました。しかし現在では状況が変わっています。</p>
<ul>
<li><strong>Chrome / Edge</strong> では安定して利用可能</li>
<li><strong>Safari</strong> も正式対応が進み、実用上の問題はほぼ解消</li>
<li><strong>Firefox</strong> も対応が進み、非対応環境でも自然にフォールバックする</li>
</ul>
<p>このため、Progressive Enhancement を前提とすれば、<strong>通常の Web サイト制作の現場でも無理なく導入できる段階</strong> に入っています。</p>
<h2>最低限の記述で何が起きるのか</h2>
<p>View Transitions API を使うために、特別な meta タグや JavaScript を用意する必要はありません。まず必要なのは、次の CSS だけです。</p>
<pre><code class="CSS">@view-transition {
  navigation: auto;
}
</code></pre>
<p>これを追加するだけで、</p>
<ul>
<li>通常の &lt;a&gt; リンクによるページ遷移</li>
<li>リロードを伴うページ遷移（cross-document）</li>
</ul>
<p>に対して、<strong>ブラウザ標準の View Transition</strong> が自動的に適用されます。対応ブラウザではフェードを中心とした滑らかな遷移が行われ、非対応ブラウザでは従来どおり即時に画面が切り替わるだけです。</p>
<h2>HTML ファーストで考える View Transitions</h2>
<p>View Transitions API は、特定のフレームワークを前提とした技術ではありません。既存の HTML 構造とナビゲーションを、そのまま活かせます。</p>
<ul>
<li><code>&lt;a&gt;</code> タグのリンクはそのまま</li>
<li>JavaScript によるルーティングは不要</li>
<li>SEO やアクセシビリティへの影響も最小限</li>
</ul>
<p>「このフレームワークを使えばできる」と思われがちですが、実際には <strong>ごく簡単な記述を追加するだけ</strong> で成立します。</p>
<h2>一覧ページ → 詳細ページで体験する View Transitions</h2>
<p>一覧ページと詳細ページの両方で、対応する要素に view-transition-name を指定することで、一覧から詳細へ遷移する際に、<strong>要素同士がつながって見える View Transition</strong> を実現できます。</p>
<pre><code class="html">&lt;img src="..." style="view-transition-name: photo-1;"&gt;
</code></pre>
<p>view-transition-name の指定は、CSS ファイルに記述することも可能ですが、<strong>対象となる要素を明示しやすいという点から <code>style</code> 属性での指定がおすすめ</strong> です。特に一覧ページと詳細ページで <strong>1 対 1 の対応関係</strong> を持たせる場合、HTML 上で対応関係が一目で分かるため、実装・保守の両面で扱いやすくなります。</p>
<p>一覧ページと詳細ページで同じ名前を指定するだけで、ブラウザはそれらを <strong>同一要素</strong> として扱い、位置やサイズの違いを自動的に補間しながら、画像が移動・変形するような遷移を生成します。</p>
<p>HTML 側で行うのは、<strong>「どの要素とどの要素を対応させたいか」を明示することだけ</strong> です。アニメーションの実装や補間処理は、すべてブラウザに委ねられます。</p>
<h3>同一ページ内での view-transition-name の扱い</h3>
<p>view-transition-name は、同一ページ内では一意である必要があります。同じページ内に同じ名前を持つ要素が複数存在すると、ブラウザが対応関係を判断できず、View Transition は正しく動作しません。</p>
<p>そのため、</p>
<ul>
<li>一覧ページでは、記事ごとに異なる名前を付ける（例：photo-1, photo-2, photo-3）</li>
<li>詳細ページでは、対応する 1 要素のみに同じ名前を付ける</li>
</ul>
<p>という 1 対 1 の関係 を保つことが重要です。</p>
<h3>画像以外の要素にも利用できる</h3>
<p>view-transition-name を指定できるのは、画像だけではありません。見出しや div 要素など、任意の HTML 要素 に指定できます。</p>
<pre><code class="html">&lt;h2 style="view-transition-name: title-1;"&gt;記事タイトル&lt;/h2&gt;
&lt;div style="view-transition-name: card-1;"&gt;...&lt;/div&gt;
</code></pre>
<p>一覧ページと詳細ページで同じ名前を指定すれば、見出しやコンテンツブロックであっても、shared element transition が成立します。</p>
<h3>実務での考え方</h3>
<p>shared element transition は、意味的に同じ役割を持つ要素を対応付ける ことで、最も効果を発揮します。</p>
<ul>
<li>一覧のアイキャッチ画像 → 詳細のメイン画像</li>
<li>一覧のタイトル → 詳細ページの見出し</li>
<li>カード全体 → 詳細コンテンツのラッパー</li>
</ul>
<p>HTML の構造を大きく変えることなく、見た目の体験だけを自然に向上させられる 点が、View Transitions API の強みです。</p>
<h2>アクセシビリティへの最低限の配慮</h2>
<p>画面全体が動く View Transitions では、ユーザーの設定を尊重することも重要です。</p>
<pre><code class="CSS">@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}
</code></pre>
<p><code>prefers-reduced-motion</code> は、「画面の動きを減らしたい」というユーザーの OS 設定を検知するための Media Query です。</p>
<p>この指定を入れておくことで、</p>
<ul>
<li>通常のユーザー：アニメーションあり</li>
<li>動きを控えたいユーザー：即時遷移</li>
</ul>
<p>という、無理のない出し分けが可能になります。</p>
<h2>まとめ：まずは HTML + CSS で十分</h2>
<p>View Transitions API は、</p>
<ul>
<li>フレームワーク前提ではない</li>
<li>最低限の記述から始められる</li>
<li>HTML ファーストな設計と相性が良い</li>
</ul>
<p>という特徴を持っています。</p>
<p>最後に、もう一度だけ書いておきますが、必要なのは次の記述だけです。</p>
<pre><code class="CSS">@view-transition {
  navigation: auto;
}
</code></pre>
<pre><code class="html">&lt;img src="..." style="view-transition-name: photo-1;"&gt;
</code></pre>
<p>これだけで、通常のページ遷移に View Transition が適用され、一覧ページと詳細ページをつなぐ体験を実現できます。まずは <strong>HTML + CSS</strong> だけ で体験し、必要になった段階でアニメーション調整や拡張を検討すれば十分です。</p>
<p>この次は、<a href="view-transitions-htmx202601.html">HTML ファーストで始める View Transitions API（htmx 編）</a> で、<strong>same-document の View Transitions</strong> や、<strong>htmx</strong> と組み合わせた実装について紹介します。</p>





















































































































]]></description>
<category>ウェブ制作</category>
<guid isPermaLink="true">https://kazumich.com/web-production/view-transitions202601.html</guid>
<pubDate>Sat, 31 Jan 2026 11:00:00 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>無事 TRAK RACER TR80S セットアップ完了</title>
<link>https://kazumich.com/log/entry-6362.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<p>購入前に「<a class="link" href="https://kazumich.com/log/entry-6353.html">自宅に TRAK RACER TR80S を導入することに</a>」と書いていましたが、セットアップが完了しましたので報告しておきます。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6362" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/cb6ce465c985549c.jpg?v=20251231081442" class="js-smartphoto" data-group="6362"><img src="https://kazumich.com/media/001/202512/mode3_w1200-cb6ce465c985549c.jpg?v=20251231081442" class="unit-id-6362" width="1010" height="778" loading="lazy" decoding="async" data-mid="738"></a></figure></div><p>娘が小さかった頃に買った YAMAHA のエレクトーン が使われないまま和室の畳でないスペースに置かれていましたが、これを処分して TRAK RACER TR80S を設置することになりました。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6362" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/f9994d281e6b5f8c.jpg?v=20251231081713" class="js-smartphoto" data-group="6362"><img src="https://kazumich.com/media/001/202512/mode3_w1200-f9994d281e6b5f8c.jpg?v=20251231081713" class="unit-id-6362" width="794" height="990" loading="lazy" decoding="async" data-mid="739"></a></figure></div><p>ほとんど1人での作業でしたが、ディスプレイをつける時だけは1人だと無理でした。かかった時間は 6時間ほどでカタチに。でも、実際に遊べるポジションに調整するのにネジをほとんど緩めてズラす作業でプラス2時間くらいかかってしまいました。</p><h2>TRAK RACER にして良かった点</h2><p class="">やっぱり、チューブフレームコックピットより、アルミフレームコックピット の方が調整の幅が広くいい！って思えました。次に TR40S / TR80S / TR120S / TR160S のような種類があり、下から2番目を選択したわけですが、どれが良かったかは比べていないので分かりませんが、ペダルマウントでフォーミュラポジションにすることはしないので前側を上げる部分は使いませんでした。</p><p class="">アルミフレームを組む際の専用ブラケットが汎用のものと違っていい感じだったり、専用のエンドキャップなんかも MiSUMi のアルミフレーム を自分で組むよりいいのではないかと言うのが感想ですね。</p><h2>TRAK RACER を使い始めて変わった点</h2><p>実はこれまで、右でアクセル、左でブレーキ で走っていたのですが、ペダル側の安定感が増したことから右足でブレーキにしても大丈夫になった感じがし、より実車を運転するのに近い感じになりました。左足が暇になったので、<a class="link" href="https://dele.io/products/tr80-footrest2-1">ユニバーサル フットレスト TR80-FOOTREST2</a> を追加を検討中。</p><h2>アンプの導入</h2><p class="">上記にあるペダルに加え、音の調整をするために何か追加をしないといけないようです。現在は、HDMI から ディスプレイ に、ディスプレイのヘッドフォン端子からヘッドフォンに音が入っているのでボリューム調整がハードウェア的に無いので簡単に変えられない状況なのです。</p><p class="">そこで、SteelSeries GameDac 61370 を導入予定。 Gen 2 というモデルが出ていて、一つ型落ちモデルっぽいが安く買えたのでコレにした。</p><h2>最後に</h2><p class="">こんな感じで届いた。全部で 50kg 以上あった、一番重たい箱は 37kg もあって玄関から部屋まで移動できず、玄関で開けて部品を何往復もして運ぶ必要があったことも書き残しておく。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6362" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/9c1793d175cae454.jpg?v=20251231084422" class="js-smartphoto" data-group="6362"><img src="https://kazumich.com/media/001/202512/mode3_w1200-9c1793d175cae454.jpg?v=20251231084422" class="unit-id-6362" width="1056" height="744" loading="lazy" decoding="async" data-mid="740"></a></figure></div>
</div>

















































































]]></description>
<category>雑記</category>
<guid isPermaLink="true">https://kazumich.com/log/entry-6362.html</guid>
<pubDate>Wed, 31 Dec 2025 11:03:41 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>新しい福利厚生「SANU」を導入しました</title>
<link>https://kazumich.com/appleple/appleple-sanu.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6363" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/8373d03271fc7f16.jpg?v=20251231111557" class="js-smartphoto" data-group="6363"><img src="https://kazumich.com/media/001/202512/mode3_w1200-8373d03271fc7f16.jpg?v=20251231111557" class="unit-id-6363" width="2800" height="1866" loading="lazy" decoding="async" data-mid="741"></a><figcaption class="caption">社員旅行で SANU に行ってきました</figcaption></figure></div><p>少し前になりますが、福利厚生の一環として貸別荘のサブスクサービス <strong>SANU</strong> を契約しました。いわゆる「ワーケーションを推進したい」という話ではなく、もう少しシンプルに、<strong>自然の中でリフレッシュしに行くきっかけを会社として用意したかった</strong>、というのが一番の理由です。</p><h2>名古屋の街中で暮らしているからこそ</h2><p>アップルップルのスタッフの多くは名古屋の街中で暮らしています。生活にはとても便利ですが、意識しないと自然の中でゆっくり過ごす時間はなかなか取れません。私自身は南に 30数km の田舎なところに住んでいますが…</p><p>週末に少し遠出をすれば自然はあるものの、「計画を立てる」「宿を探す」といったハードルがあると、結局行かずじまいになりがちです。そこで、<strong>行こうと思えばすぐ行ける場所を、会社として用意しておく</strong>という選択肢として <strong>SANU</strong> はちょうどいいなと感じました。</p><h2>SANU とは</h2><p>全国の自然豊かな場所にあるキャビンを、会員制で利用できる貸別荘のサブスクリプションサービスです。森や湖のそばなど、日常から少し離れた環境に拠点があり、「何もしない時間」や「自然の中で過ごす時間」を気軽に持てるのが特徴です。</p><p>エリアは 35拠点で、軽井沢、一宮、北軽井沢、山中湖、南アルプス、那須、伊豆、蓼科、河口湖、館山、白樺湖、八ヶ岳、ニセコ、いすみ、安曇野、下田、足柄、淡路島、奄美大島、白馬 が利用でき、名古屋からだと長野の辺りで3時間ドライブで山の中に行くイメージになります。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6363" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/bd039ba6d28ecec7.jpg?v=20251231113651" class="js-smartphoto" data-group="6363"><img src="https://kazumich.com/media/001/202512/mode3_w1200-bd039ba6d28ecec7.jpg?v=20251231113651" class="unit-id-6363" width="3671" height="2064" loading="lazy" decoding="async" data-mid="742"></a><figcaption class="caption">SANU CABIN MOSS Medium</figcaption></figure></div><h2>家族や友達と行ってもらってもいい</h2><p>この福利厚生は、社員本人だけのためのものではありません。<strong>家族や友達と一緒に使ってもらう</strong>のも、まったく問題ないと考えています。</p><p>仕事を頑張るためには、プライベートが充実していることも大事ですし、「会社の制度のおかげで、ちょっといい時間を過ごせた」と思ってもらえたら、それだけで十分です。</p><p>春までに 1人 2泊できるようにしています。1棟1泊なので、1人分の権利で複数人が泊まれます。</p><h2>開発合宿や「数人で遊びに行く」使い方も</h2><p>今後は、</p><ul><li><p>数名での <strong>開発合宿的な利用</strong></p></li><li><p>「ちょっと気分転換に行こう」という <strong>小旅行</strong></p></li></ul><p>こういった使い方もしていけたらいいなと思っています。あまり制度でガチガチに縛らず、<strong>使いたい人が、使いたい形で使える</strong>くらいがちょうどいい。</p><p>社内で利用する際には、4人で行っても1泊分で済むので、開発合宿的に行けば4人が1泊づつ消費し、4泊5日で行く事が可能になります。</p><h2>SANUの問題点</h2><p class="">基本、家族で利用する前提の貸別荘であることから、4人泊まれる建物だとしても、ダブルベットが2つだったりします。男性4人での開発合宿を考えると、一緒のベットで寝るのは難しいのではないかと思います。</p><p class="">シングルベットが2つ合体してダブルベットになるようなものだったりしたら良かったのですが。その場合には、シングルの布団が余分に必要になったりするので収納も少ないので難しいかもしれません。</p><p class="">その点からも、SANU CABIN MOSS Medium や BEE などはグループ向けではないようです。 MOSS の 6人まで OK の少し広い SANU CABIN MOSS Large であれば大丈夫なのかもしれないので、一度行ってみようと考えています。</p><h2>追記</h2><p class="">2026年5月に BEE というタイプの 八ヶ岳 2nd にも行ってきました。外観の写真が玄関側しかなかったですが、窓が大きく＆天井が高く MOSSタイプよりいいなぁって感じでした。次に泊まる違うタイプは ARC というものを予約済です。</p><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6363" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202605/4fdae39f667c11e5.jpg?v=20260531065823" class="js-smartphoto" data-group="6363"><img src="https://kazumich.com/media/001/202605/mode3_w1200-4fdae39f667c11e5.jpg?v=20260531065823" class="unit-id-6363" width="4284" height="4284" loading="lazy" decoding="async" data-mid="764"></a></figure></div><div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6363" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202605/d4b10d8eaafe8508.jpg?v=20260531070123" class="js-smartphoto" data-group="6363"><img src="https://kazumich.com/media/001/202605/mode3_w1200-d4b10d8eaafe8508.jpg?v=20260531070123" class="unit-id-6363" width="4032" height="3024" loading="lazy" decoding="async" data-mid="765"></a></figure></div><p>こんな感じで1年の契約で、いろいろな所に泊まりに行っています。社員もプライベートで使ってもらったりして 年間 30泊のところ 26泊予約済みで残り 4泊残すことなく利用できるように頑張ります。</p>
</div>

















































































]]></description>
<category>アップルップル</category>
<guid isPermaLink="true">https://kazumich.com/appleple/appleple-sanu.html</guid>
<pubDate>Tue, 30 Dec 2025 06:56:06 +0900</pubDate>
</item>
<item>
<dc:creator>山本 一道</dc:creator>
<title>View Transition API と htmx を組み合わせに成功</title>
<link>https://kazumich.com/web-production/htmx-view_transition_api202512.html</link>
<description><![CDATA[































































































<div class="column-block-editor">
<div class="media-image-block align-center" data-type="imageBlock" data-align="center" data-width="100%" data-eid="6358" data-no-lightbox="false"><figure class="" style="max-width: 100%;"><a href="https://kazumich.com/media/001/202512/a8439eac9b6751f8.png?v=20251228233624" class="js-smartphoto" data-group="6358"><img src="https://kazumich.com/media/001/202512/mode3_w1200-a8439eac9b6751f8.png?v=20251228233624" class="unit-id-6358" width="2752" height="1536" loading="lazy" decoding="async" data-mid="737"></a></figure></div><p>今年の3月くらいに「<a class="link" href="https://kazumich.com/ablogcms-viewTransitionsApi.html">a-blog cms と View Transitions API で作るページ遷移のアニメーションの実装について</a>」を書いていますが、少し進展したことから続編を書いてみようと思います。前回は、a-blog cms への実装について書いていましたが、今回は CMS 関係なく HTML + CSS + htmx ( JS ) というところでの万人に向けた内容になります。</p>
</div>




























































<div class="column-block-editor">
<h2>View Transitions API との出会い</h2><p class="">ICS MEDIA の「<a class="link" href="https://ics.media/entry/230510/">View Transitions API入門 - 連続性のある画面遷移アニメーションを実現するウェブの新技術</a>」をご覧ください。これを読んで、そういうものがあるんだという事を知り、CMS のテンプレートに利用することを考えて、色々チャレンジしておりました。</p><p class="">私自身、JavaScript などが得意ではない中でも、HTML + CSS だけで実装する <strong>View Transitions API</strong> の画面遷移アニメーション、ICS MEDIA さんの中で MPA編 としては春時点では対応できておりました。しかし、SPA編の解説を読んでも、自分なりに実装することができず挫折しておりました。</p>
</div>




























































<div class="column-block-editor">
<h2>htmx を利用した SPA な View Transitions API について</h2><p><strong>htmx</strong> を利用することで、基本的には JavaScript を書かずに、属性を書くだけで Ajax でコンテンツを更新ができるライブラリです。これと <strong>View Transitions API</strong> を組み合わせることで春に挫折したところをクリアすることができましたので書き残しておきます。</p><h3>htmx の準備</h3><pre><code>&lt;script src="https://unpkg.com/htmx.org@2.0.8"&gt;&lt;/script&gt;
&lt;script&gt;
  htmx.config.globalViewTransitions = true;
&lt;/script&gt;</code></pre><h3>CSS の準備</h3><pre><code>&lt;style&gt;
    .vt-img {
        contain: paint;
        width: 100%;
        aspect-ratio: 16/9;
        object-fit: cover;
        display: block;
    }
    ::view-transition-group(root) {
        animation-duration: 0.5s;
    }
&lt;/style&gt;</code></pre><p>大事なのは contain: paint; の設定です。</p><h3>一覧ページの HTML サンプル</h3><pre><code>&lt;div class="card"&gt;
    &lt;button
        hx-get="detail-1.html" 
        hx-target="#main-area" 
        hx-swap="innerHTML transition:true"
        hx-push-url="detail-1.html"
        style="background: none; border: none; cursor: pointer; padding: 0;"&gt;
        &lt;img src="/images/detail-1.jpg" 
                class="vt-img" 
                style="view-transition-name: detail-1;"
                alt="商品1"&gt;
        &lt;h3&gt;サンプルタイトル1&lt;/h3&gt;
    &lt;/button&gt;
&lt;/div&gt;</code></pre><p><strong>htmx</strong> の属性については説明を省き、View Transitions API の説明だけ書きますが、img に <strong>class="vt-img"</strong> を設定し、<strong>style="view-transition-name: detail-1;"</strong> で個別に名前を設定します。これが一覧と詳細で同じ名前のものが同じモノとして扱われます。</p><h3>詳細ページの HTML サンプル</h3><pre><code>&lt;div class="detail-container"&gt;
    
    &lt;button 
        hx-get="list.html" 
        hx-target="#main-area" 
        hx-swap="innerHTML transition:true"
        hx-push-url="list.html"&gt;
        ← 一覧に戻る
    &lt;/button&gt;

    &lt;figure&gt;
        &lt;img src="/images/detail-1.jpg" 
             class="vt-img"
             style="view-transition-name: detail-1;"
             loading="eager"
             fetchpriority="high"
             alt="詳細画像"&gt;
    &lt;/figure&gt;
    &lt;h1&gt;サンプルタイトル1&lt;/h1&gt;
    &lt;p&gt;説明テキスト...&lt;/p&gt;
&lt;/div&gt;</code></pre><p>詳細ページでも、一覧と同じ <strong>class="vt-img" style="view-transition-name: detail-1;"</strong> の設定を行い、loading="lazy" させず、できるだけ早めに読み込んでくださいという意味で <strong>loading="eager" fetchpriority="high"</strong> としておきます。</p><p>以前は、view-transition-name: の設定を &lt;head&gt; に &lt;style&gt; で書かないといけないと思い、結構面倒なことをしていたのですが、&lt;img&gt; の中に style属性で書けばいい事がわかり実装が手軽になったと感じています。</p><p><a class="link" href="https://kazumich.com/demo/h-vta/list.html">実際のデモ環境（上の2枚）</a></p><h2>大きな勘違いをしていました</h2><p>ここまで実際のデモの HTML を実装しながら出来た気でいましたが、画像だけを拡大するアニメーションがつくように実装をしていましたという事でなく、見出しなどの要素についても同様にアニメーションさせる事ができる事が、ブログを書いている途中で気がつきました。</p><p>そういう事であれば、一覧の時と詳細ページでの表示の際には、アイキャッチ画像とタイトルの位置関係は変えないようなレイアウトの方がオススメという事になります。</p><p><a class="link" href="https://kazumich.com/demo/h-vta/list.html">実際のデモ環境（下に右側）</a></p><h3>一覧ページのタイトル</h3><pre><code>&lt;h3 style="view-transition-name: title-1;"&gt;サンプルタイトル1&lt;/h3&gt;</code></pre><h3>詳細ページのタイトル</h3><pre><code>&lt;h1 style="view-transition-name: title-1;"&gt;サンプルタイトル1&lt;/h1&gt;</code></pre><h2>カード全体の設定も可能でした</h2><p class="">カードレイアウトであれば、そのカード自体に <strong>view-transition-name:</strong> を設定して、そのカード自体が詳細ページで大きくなるイメージであれば、カード全体の div に view-transition-name: を設定しておくことで同様に動かす事が可能でした。</p><p class="">この場合には、画像や見出しには、view-transition-name: をつける必要がなくなります。カードのレイアウトとしての画像と見出しの位置関係を、詳細ページにも同じであれば、全体で定義という実装がオススメです。</p><p class=""><a class="link" href="https://kazumich.com/demo/h-vta/list.html">実際のデモ環境（下に左側）</a></p><h2>最後に</h2><p class="">他の人が、読んでもよくわからないかもしれませんが、やっぱり書く事によって理解度が深まったのでヨシって事にします。 htmx + View Transitions API の組み合わせは、お手軽にいい感じの動きを付けられてイイ感じです。 今後、社内の案件でも普通に使っていきたいと思っています。</p><p class="">今回、SPA な View Transitions API を htmx を利用して実装してみました。 今回は静的な HTML で実装のデモを作ってみましたが、実際には、バックエンドに a-blog cms を利用することで詳細ページでリロードしても正しくコンテンツを出してくれたりするので、ぜひ htmx + View Transitions API のお供には a-blog cms の利用もご検討ください。</p><p class=""></p><p class="">このブログも数日中には、Tailwind css + Alpine.js + htmx + View Transitions API + a-blog cms な感じのテーマに変更予定です。</p>
</div>

















































































]]></description>
<category>ウェブ制作</category>
<guid isPermaLink="true">https://kazumich.com/web-production/htmx-view_transition_api202512.html</guid>
<pubDate>Sun, 28 Dec 2025 23:30:49 +0900</pubDate>
</item>
</channel>
</rss>