<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>suganuma &#8211; なすびブログ</title>
	<atom:link href="https://blog.nasbi.jp/author/suganuma/feed/index.xml" rel="self" type="application/rss+xml" />
	<link>https://blog.nasbi.jp/</link>
	<description></description>
	<lastBuildDate>Thu, 29 Oct 2026 07:37:03 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>
	<item>
		<title>package-lock.jsonがコンフリクトした場合の正しい解消方法</title>
		<link>https://blog.nasbi.jp/true-resolve-package-lock-json/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Fri, 11 Jul 2025 08:26:01 +0000</pubDate>
				<category><![CDATA[開発環境]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=1127</guid>

					<description><![CDATA[ここにブランチが3つあります featureAとBそれぞれpackage.jsonにアプデがあり、featureAを先にmainにマージ済みの状態 main←featureBでpackage.jsonないしpackage [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">ここにブランチが3つあります</p>



<ul class="wp-block-list">
<li>main</li>



<li>featureA</li>



<li>featureB</li>
</ul>



<p class="wp-block-paragraph">featureAとBそれぞれpackage.jsonにアプデがあり、featureAを先にmainにマージ済みの状態</p>



<p class="wp-block-paragraph">main←featureBでpackage.jsonないしpackage-lock.jsonにコンフリクトが発生する。</p>



<p class="wp-block-paragraph">普通に考えたらfeatureB←mainして <code>npm install</code> すれば解決するが、パッケージのマイナーバージョンを変えたくない場合はこの方法だとNG。package-lock.jsonが更新されて依存パッケージのマイナーバージョンが変更されてしまう。</p>



<p class="wp-block-paragraph">そんなときは、mainをmainBとしてフォークして、mainBにfeatureBで追加したパッケージを改めてインストール。 <code>npm install hogehoge@1.2.3</code></p>



<p class="wp-block-paragraph">featureB←mainBするとやっぱりpackage-lock.jsonがコンフリクトするのでmainBのものを使う <code>git checkout --theirs package-lock.json</code></p>



<p class="wp-block-paragraph">で、 <code>npm ci</code> などすれば正常化できるのだ</p>



<p class="wp-block-paragraph">mainも取り込み済みなので、main←featureBがコンフリクトすることもない。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>我々はいつからimportをファイルの先頭に書かなければいけないと勘違いしていたのか</title>
		<link>https://blog.nasbi.jp/import-is-not-require-at-start-of-file/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Fri, 16 Aug 2024 03:06:19 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=1115</guid>

					<description><![CDATA[自分だけかもしれないのだが、 import はファイルの先頭に書かなければシンタックスエラーになるのだと勝手に勘違いをしていた。 下記のソースコードはエラーではないのだ。 ただし import をファイルの途中に書くメリ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">自分だけかもしれないのだが、 <code>import</code> はファイルの先頭に書かなければシンタックスエラーになるのだと勝手に勘違いをしていた。</p>



<p class="wp-block-paragraph">下記のソースコードはエラーではないのだ。</p>



<pre class="wp-block-code language-javascript"><code>import hoge from 'hoge';

export function hogefuga() {
  hoge();
  fuga();
}

import fuga from 'fuga';

export function fugahoge() {
  fuga();
  hoge();
}</code></pre>



<p class="wp-block-paragraph">ただし <code>import</code> をファイルの途中に書くメリットはまったく思いつかないので、先頭に書いたほうが良いだろうね</p>



<p class="wp-block-paragraph">なぜこんな勘違いをしていたのかと考えてみたけど</p>



<ul class="wp-block-list">
<li>トップレベルに書かなければいけないという仕様をファイルの先頭と勘違いしていた</li>



<li>importはエディタが勝手にファイルの先頭に書いてくれるものだから</li>



<li>ES Modules 覚え始めのころのプロジェクトで、eslintで禁止されていた</li>
</ul>



<p class="wp-block-paragraph">こういう理由があったのかもしれない。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>各種ブラウザのフレームレートを調べた</title>
		<link>https://blog.nasbi.jp/research-browser-framerate/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Wed, 26 Jun 2024 12:36:16 +0000</pubDate>
				<category><![CDATA[開発環境]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=1092</guid>

					<description><![CDATA[ちょっと気になることがあって各主要ブラウザのフレームレートを調べてみたのでその結果をメモ。 調べ方 PCのChromeであれば、DevToolsで下記のようにリアルタイムのフレームレートを表示する事が可能です。 今回はC [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">ちょっと気になることがあって各主要ブラウザのフレームレートを調べてみたのでその結果をメモ。</p>



<h2 class="wp-block-heading">調べ方</h2>



<p class="wp-block-paragraph">PCのChromeであれば、DevToolsで下記のようにリアルタイムのフレームレートを表示する事が可能です。</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="564" src="https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-200324-1024x564.png" alt="" class="wp-image-1093" srcset="https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-200324-1024x564.png 1024w, https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-200324-300x165.png 300w, https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-200324-768x423.png 768w, https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-200324.png 1210w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">今回はChrome以外のブラウザや、スマホでも調べたかったので下記のようなコードを用意してみました。</p>



<p class="wp-block-paragraph"><code>requestAnimationFrame()</code> を１秒間回して、その回数を調べる単純なコードです。</p>



<pre class="wp-block-code language-js"><code>async function measureFPS() {
    return new Promise((resolve) =&gt; {
        let count = 0;
        const start = performance.now();
        function frame() {
            if (performance.now() - start &gt;= 1000) {
                resolve(count);
                return;
            }

            count = count + 1;
            requestAnimationFrame(frame);
        }
        frame();
    });
}</code></pre>



<p class="wp-block-paragraph">それをHTML上に実装したものが <a href="https://playground.nasbi.jp/framerate-test/">こちらのページ</a> で確認できます</p>



<p class="wp-block-paragraph">Macに144Hzのディスプレイを繋いでChromeで試した結果がこちら</p>



<figure class="wp-block-image size-full"><img decoding="async" width="304" height="154" src="https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-201449.png" alt="" class="wp-image-1098" srcset="https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-201449.png 304w, https://blog.nasbi.jp/wp-content/uploads/2024/06/screenshot-2024-06-26-201449-300x152.png 300w" sizes="(max-width: 304px) 100vw, 304px" /></figure>



<p class="wp-block-paragraph">どうやら1〜2fpsほど大きい数字が出てしまうようです。<br>コードの問題かもしれませんが、まあいいでしょう。なんとなく測れればよいのです。</p>



<p class="wp-block-paragraph">このHTMLを使って各ブラウザで計測していきます。</p>



<h2 class="wp-block-heading">計測環境</h2>



<p class="wp-block-paragraph">結果発表の前に、計測した環境について書いておきます</p>



<ul class="wp-block-list">
<li>Macbook Pro M1 Max
<ul class="wp-block-list">
<li>内蔵ディスプレイ（<a href="https://support.apple.com/ja-jp/102297">ProMotion</a>の120Hz）</li>



<li>外部ディスプレイ：AG274UXP/11（144Hzで接続）</li>



<li>Sidecar の iPad Pro （ProMotion対応だが、Sidecar接続のため60Hz）</li>
</ul>
</li>



<li>iPad Pro 11インチ 第4世代 M2チップ
<ul class="wp-block-list">
<li>内蔵ディスプレイ（ProMotion 120Hz）</li>



<li>外部ディスプレイ：AG274UXP/11（ステージマネージャ有効）</li>
</ul>
</li>



<li>Xiaomi 13T Pro
<ul class="wp-block-list">
<li>内蔵ディスプレイ（最高144Hzまでの動的フレームレート設定）</li>



<li>内蔵ディスプレイ（固定144Hz）</li>



<li>内蔵ディスプレイ（固定60Hz）</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">AG274UXP/11が長いので以下AGONと表記します</p>



<h2 class="wp-block-heading">計測した結果</h2>



<h3 class="wp-block-heading">Macbook Pro M1</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ブラウザ</th><th>ディスプレイ</th><th>FPS</th></tr></thead><tbody><tr><td>Chrome</td><td>内蔵</td><td>122</td></tr><tr><td>Chrome</td><td>AGON</td><td>145</td></tr><tr><td>Chrome</td><td>Sidecar</td><td>62</td></tr><tr><td>Safari</td><td>内蔵</td><td>62</td></tr><tr><td>Safari</td><td>AGON</td><td>74</td></tr><tr><td>Safari</td><td>Sidecar</td><td>62</td></tr><tr><td>Firefox</td><td>内蔵</td><td>145</td></tr><tr><td>Firefox</td><td>AGON</td><td>146</td></tr><tr><td>Firefox</td><td>Sidecar</td><td>144</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">この結果から、Chromeはディスプレイのリフレッシュレートに合わせてレンダリングのフレームレート制限を可変させることがわかった。</p>



<p class="wp-block-paragraph">Safariも基本的にはChromeと同じくディスプレイのリフレッシュレートを最大値としてフレームレート制限をかけるようだが、</p>



<ul class="wp-block-list">
<li>最高でも72FPS（？）までに制限される</li>



<li>ProMotionの下では60FPSに制限される</li>
</ul>



<p class="wp-block-paragraph">という条件がつくようだ。</p>



<p class="wp-block-paragraph">Firefoxだけ理解できなかったのでAGONを物理的に切断して、MacとSidecarだけ繋がってる状態で再計測</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ブラウザ</th><th>ディスプレイ</th><th>FPS</th></tr></thead><tbody><tr><td>Firefox</td><td>内蔵</td><td>61</td></tr><tr><td>Firefox</td><td>Sidecar（プライマリ）</td><td>62</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">なるほど、どうやらプライマリディスプレイのリフレッシュレートでレンダリングされるらしい。（↑の計測時はSidecarがプライマリになっていた）</p>



<p class="wp-block-paragraph">今度は内蔵をプライマリにしてみる</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ブラウザ</th><th>ディスプレイ</th><th>FPS</th></tr></thead><tbody><tr><td>Firefox</td><td>内蔵（プライマリ）</td><td>120</td></tr><tr><td>Firefox</td><td>Sidecar</td><td>120</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">なるほどね</p>



<h3 class="wp-block-heading">iPad Pro 11インチ</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ブラウザ</th><th>ディスプレイ</th><th>FPS</th></tr></thead><tbody><tr><td>Safari</td><td>内蔵</td><td>61</td></tr><tr><td>Safari</td><td>AGON</td><td>62</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">ステージマネージャの外部ディスプレイはブラウザの外側でリフレッシュレートがどのくらいなのか調べられなかったのだけど、マウスポインターをグルグル動かしたときの軌跡がProMotionな内蔵ディスプレイと比べると明らかに少ないので、おそらく60Hzで接続されてるのだと思う。</p>



<p class="wp-block-paragraph">ちなみにAGONを接続せずにiPad単体で測っても62FPSでした。</p>



<h3 class="wp-block-heading">Xiaomi 13T Pro</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ブラウザ</th><th>ディスプレイ</th><th>FPS</th></tr></thead><tbody><tr><td>Chrome</td><td>内蔵・動的Hz</td><td>122</td></tr><tr><td>Chrome</td><td>内蔵・固定144Hz</td><td>121</td></tr><tr><td>Chrome</td><td>内蔵・固定60Hz</td><td>62</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Mac+Chromeと同じ結果を予想していたけど、144Hz固定にしても120相当しか出なかったのは予想外。この端末の特有な現象の可能性もある。</p>



<p class="wp-block-paragraph">もしかしたらAndroidのChromeは上限120FPSで制限されているのかもしれない。</p>



<h2 class="wp-block-heading">結論</h2>



<p class="wp-block-paragraph">今回iPhone実機では調査しなかったけど、まあ間違いなくiPadと同じで60FPSに制限されていると思います。</p>



<p class="wp-block-paragraph">フロントエンド界隈では「ヌルサクアニメーションにするために60FPSを死守すべし！」というのはよく聞く話ですが、実際に調べてみた結果Safariをターゲットとするならばやはり60FPSがひとつの基準になるのは間違いないようです。<br>ただ、Safari以外はハードウェアさえ対応していれば少なくとも120FPSでレンダリングできるので、これからは120FPSを基準にしていくべきなのかもしれません。</p>



<p class="wp-block-paragraph">iPhoneだってハードウェア的には120Hzなわけで、Apple的にハイリフレッシュレートに対して否定的な立場ではないハズ。<br>OS/Safariのアップデートで突然60FPS制限を引き上げるのも時間の問題なのでは？と思いました。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>スタイルを隔離してHTMLを埋め込むにはShadowDOMを使うとスマートかもしれない</title>
		<link>https://blog.nasbi.jp/isolate-style-to-embedded-html/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Thu, 16 May 2024 16:00:01 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=1071</guid>

					<description><![CDATA[管理画面があるようなWebサイト・Webアプリを作るとき、稀によくHTMLテキストを値として入力したいという要件があります。 Webサイトの運用者がある程度自由に内容を書き換えられる必要があるコンテンツ、例えばWordP [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">管理画面があるようなWebサイト・Webアプリを作るとき、稀によくHTMLテキストを値として入力したいという要件があります。</p>



<p class="wp-block-paragraph">Webサイトの運用者がある程度自由に内容を書き換えられる必要があるコンテンツ、例えばWordPressの記事本文などがそうですが、管理画面ではWYSIWYGエディタのようなものだったり、場合によってはmarkdownを書く仕様だったりすると思います。</p>



<p class="wp-block-paragraph">これを表示する側の実装ではその部分にも当然スタイルを当てなければなりませんが、多くの場合は出力されるHTMLに自由にクラス名などを付与することができないため、 <code>&lt;h1></code> や <code>&lt;p></code> など、クラス名のついていない素のタグに対してスタイルを適用することになります。</p>



<p class="wp-block-paragraph">例えば、下記のような。</p>



<pre class="wp-block-code language-html"><code>&lt;div class="post_content"&gt;
  &lt;h1&gt;見出し１&lt;/h1&gt;
  &lt;p&gt;ほげほげ&lt;/p&gt;
  &lt;h2&gt;見出し２&lt;/h2&gt;
  &lt;ul&gt;
    &lt;li&gt;リスト１&lt;/li&gt;
    &lt;li&gt;リスト２&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;</code></pre>



<pre class="wp-block-code language-css"><code>.post_content h1 {
  font-size: 24px;
  font-weight: bold;
}
.post_content h2 {
  font-size: 20px;
  font-weight: bold;
}

.post_content p {
  margin-top: 12px;
  margin-bottom: 12px;
}

.post_content ul {
  list-style: disc;
}

...
</code></pre>



<p class="wp-block-paragraph">私はこういうスタイルを書くとき、このサイトがベースのスタイルとしてtailwindやリセットCSSを使っているとしたら、わりと気を使うんですよね。</p>



<p class="wp-block-paragraph">h1やpのようなよく使うタグはもちろんデザインに合わせてスタイルを書いておきますが、 <code>&lt;blockquote></code> みたいな普段使わないような、デザインの想定にもないようなタグを使われた場合、スタイルを用意できておらず見た目ではdivと同じになってしまいます。</p>



<p class="wp-block-paragraph">normalize.cssをベースにしていれば少なくともデフォルトのスタイルが存在するので、スタイルがないよりマシだと思いますが、ベースがリセットCSSだと前後の余白すらなくなってしまって酷い見た目になります。</p>



<p class="wp-block-paragraph">上記の例で言うところの <code>.post_content</code> 以下だけCSS的に隔離ができて、normalize.cssもしくは独自のCSSを適用できるのだとしたら、外からの影響を受けずに堅牢なスタイルが作れるのではないかと思いました。</p>



<p class="wp-block-paragraph">親要素からの隔離だったら <code>ShadowDOM</code> が使えるのでは？ というのが今回の話の趣旨になります。</p>



<h2 class="wp-block-heading">ShadowDOMとは？</h2>



<p class="wp-block-paragraph">詳しい説明はみんなだいすき <a href="https://developer.mozilla.org/ja/docs/Web/API/Web_components/Using_shadow_DOM">MdN</a> を見てもらうとして、一言でいうならばDOMツリーの中に独立したDOMツリーを構築する機能のこと。</p>



<p class="wp-block-paragraph">その影響としてCSSのスコーピングが可能になります。</p>



<p class="wp-block-paragraph">ShadowDOMの外はtailwind、中はnormalizeができるわけですね。</p>



<pre class="wp-block-code language-html"><code>&lt;div class="js-dom-prison"&gt;
  &lt;h1&gt;見出し１&lt;/h1&gt;
  &lt;p&gt;ほげほげ&lt;/p&gt;
  &lt;h2&gt;見出し２&lt;/h2&gt;
  &lt;ul&gt;
    &lt;li&gt;リスト１&lt;/li&gt;
    &lt;li&gt;リスト２&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;</code></pre>



<p class="wp-block-paragraph"><code>.js-dom-prison</code> をShadowDOMのrootにして、内容物をすべてShadowDOMの中に配置し直します。</p>



<pre class="wp-block-code language-javascript"><code>const container = document.querySelector('.js-dom-prison');
const children = container.childNodes;
const shadowRoot = container.attachShadow({mode: 'open'});
shadowRoot.append(...children);</code></pre>



<p class="wp-block-paragraph">ShadowDOMの中にnormalize.css（もちろん、独自のCSSでも可）を適用したい場合は、こう↓</p>



<pre class="wp-block-code language-javascript"><code>const container = document.querySelector('.js-dom-prison');
const children = container.childNodes;
const shadowRoot = container.attachShadow({mode: 'open'});
const stylesheet = document.createElement('link');
stylesheet.setAttribute('href', 'https://necolas.github.io/normalize.css/8.0.1/normalize.css');
stylesheet.setAttribute('rel', 'stylesheet');
shadowRoot.append(stylesheet, ...children);</code></pre>



<p class="wp-block-paragraph">これをDevToolsでみるとこうなります↓</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="345" src="https://blog.nasbi.jp/wp-content/uploads/2024/05/screenshot-2024-05-17-005507-1024x345.png" alt="" class="wp-image-1082" srcset="https://blog.nasbi.jp/wp-content/uploads/2024/05/screenshot-2024-05-17-005507-1024x345.png 1024w, https://blog.nasbi.jp/wp-content/uploads/2024/05/screenshot-2024-05-17-005507-300x101.png 300w, https://blog.nasbi.jp/wp-content/uploads/2024/05/screenshot-2024-05-17-005507-768x258.png 768w, https://blog.nasbi.jp/wp-content/uploads/2024/05/screenshot-2024-05-17-005507.png 1076w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">あえてinnerHTMLなどを使わずに最初から <code>.js-dom-prison</code> にHTMLを展開しておくことで、たぶんSEO的にも問題にならない。（たぶん）</p>



<p class="wp-block-paragraph">querySelector等で探せなくなるなど、デメリットがないわけではないので使い所が選ぶ必要がありそうですが、iframeなどと違ってオーバーヘッドも小さいですし、もののJS数行でCSS的に隔離ができるので、ハマる状況ならとてもスマートなんじゃいかと思ってます</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Alpine.jsの$storeはいつ使うのか？</title>
		<link>https://blog.nasbi.jp/when-to-use-store-in-alpine-js/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Fri, 19 Apr 2024 02:37:34 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=1058</guid>

					<description><![CDATA[Alpine.jsでは x-data="{open: false}" というように x-data 属性を付与することで、その要素の内側で x-show="open" のように参照することができます。 とまあこれはAlpi [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Alpine.jsでは <code>x-data="{open: false}"</code> というように <code>x-data</code> 属性を付与することで、その要素の内側で <code>x-show="open"</code> のように参照することができます。</p>



<pre class="wp-block-code language-html"><code>&lt;div x-data="{open: false}"&gt;
  &lt;button @click="open = !open"&gt;開閉するよ&lt;/button&gt;
  &lt;div x-show="open"&gt;
    開いてるね
  &lt;/div&gt;
&lt;/div&gt;</code></pre>



<p class="wp-block-paragraph">とまあこれはAlpine.jsを知っている人なら全員知ってることなんですが、 <code>x-data</code> でなくても <code>$store</code> で同じようなことができます。</p>



<p class="wp-block-paragraph">例えばこんな感じですね</p>



<pre class="wp-block-code language-html"><code>&lt;button x-data @click="$store.menu.open = !$store.menu.open"&gt;開閉するよ&lt;/button&gt;

&lt;div x-data x-show="$store.menu.open"&gt;
  開いてるね
&lt;/div&gt;

&lt;script&gt;
  document.addEventListener('alpine:init', () =&gt; {
    Alpine.store('menu', {
      open: false,
    })
  })
&lt;/script&gt;</code></pre>



<p class="wp-block-paragraph">コードでみると結構違いますが、やってることは一緒で、openという変数の定義場所が違うだけなんですよね。</p>



<p class="wp-block-paragraph"><code>$store</code> はコンポーネントを跨いで参照することができます。便利。</p>



<p class="wp-block-paragraph">ただし、v3から <code>x-data</code> は<a href="https://alpinejs.dev/upgrade-guide#x-data-scope">入れ子になっていても変数名がカブらない限り祖先の変数も参照できる</a>という仕様なので、</p>



<pre class="wp-block-code language-html"><code>&lt;body x-data="{open: false}"&gt;
  ...
  &lt;button @click="open = !open"&gt;開閉するよ&lt;/button&gt;
  ...
  &lt;div x-show="open"&gt;
    開いてるね
  &lt;/div&gt;
  ...
&lt;/body&gt;</code></pre>



<p class="wp-block-paragraph">このように <code>&lt;body></code> で <code>x-data</code> しちゃえばどこからでも参照できるグローバル変数を定義できちゃうんですよね。これでは$storeの立場がありません。</p>



<p class="wp-block-paragraph">じゃあ <code>$store</code> っていつ使うの？って話なんですが、おそらく主に下記2点に注目すべきポイントがあります</p>



<h3 class="wp-block-heading">外部のJSと連携したい！</h3>



<p class="wp-block-paragraph">UI系のライブラリを利用する場合など、Alpine.js外のJSとステートの共有をしたい場合は往々にしてあると思います。</p>



<p class="wp-block-paragraph">そんな場合には <code>x-data</code> や <code>Alpine.data</code> で頑張るより素直にstoreを使ったほうが良いでしょう。</p>



<pre class="wp-block-code language-html"><code>&lt;script&gt;
    Alpine.store('menu').open = true;
&lt;/script&gt;</code></pre>



<h3 class="wp-block-heading">パフォーマンスへの影響</h3>



<p class="wp-block-paragraph"><code>x-data</code> を付与すると <code>Alpine.start()</code> を実行したときに、その要素以下がコンポーネントとして初期化されます。</p>



<p class="wp-block-paragraph">もしその要素が巨大なツリーだった場合、初期化にかかるコストが大きいものになるため、できる限りコンポーネントは小さくするのがベター。</p>



<p class="wp-block-paragraph">ちょっとした状態管理のためだけにbodyで <code>x-data</code> をしてしまうのはコストに見合わないので、$storeを使うのがよいでしょう。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>いい感じの色を作りたいならOklchを使うといい感じになる</title>
		<link>https://blog.nasbi.jp/good-looking-color-oklch/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Mon, 25 Dec 2023 04:53:07 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=829</guid>

					<description><![CDATA[時折、明度と彩度を一定に保ったまま色相を変えていくつかの色を作りたいときがあります。 なすびのウェブサイトで例えると、制作事例のタグは頻繁な増減があり、数も多く、色自体に拘りたいわけではないので、すべての色をひとつずつ決 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">時折、明度と彩度を一定に保ったまま色相を変えていくつかの色を作りたいときがあります。</p>



<p class="wp-block-paragraph">なすびのウェブサイトで例えると、<a href="https://nasbi.jp/works">制作事例</a>のタグは頻繁な増減があり、数も多く、色自体に拘りたいわけではないので、すべての色をひとつずつ決めるのは結構な手間になります。<br>そのため、ここでは <code>codePointAt()</code> を使ってタグ名を数値に変換し、色相に当てはめてランダムではない動的な色を作ることにしました。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="299" src="https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517-1024x299.png" alt="" class="wp-image-899" srcset="https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517-1024x299.png 1024w, https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517-300x88.png 300w, https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517-768x224.png 768w, https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517-1536x448.png 1536w, https://blog.nasbi.jp/wp-content/uploads/2023/12/screenshot-2023-12-23-152517.png 1590w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">このような場合、これまでは<a href="https://developer.mozilla.org/ja/docs/Web/CSS/color_value/hsl">hsl()</a>を使うことで簡単に明度彩度を固定したまま色相だけ違う色をたくさん作ることができましたが、hslによる指定では明度彩度を一定にしても視覚的な均一性はありませんでした（特に黄色系の色味が明るすぎるなど）。</p>



<p class="wp-block-paragraph">最近のブラウザではhslに似た指定で視覚的均一性のあるカラー指定<a href="https://developer.mozilla.org/ja/docs/Web/CSS/color_value/oklch">oklch()</a>が使えるようになっています。</p>



<p class="wp-block-paragraph">hslとoklchをわかりやすく並べて比較するとこのくらい違いがありました。</p>



<div class="wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper"><iframe id="cp_embed_JjwRVNq" src="//codepen.io/anon/embed/JjwRVNq?height=450&#038;theme-id=1&#038;slug-hash=JjwRVNq&#038;default-tab=result" height="450" scrolling="no" frameborder="0" allowfullscreen allowpaymentrequest name="CodePen Embed JjwRVNq" title="CodePen Embed JjwRVNq" class="cp_embed_iframe" style="width:100%;overflow:hidden">CodePen Embed Fallback</iframe></div>



<p class="wp-block-paragraph">上下のどちらの色が均一に見えるでしょうか。</p>



<p class="wp-block-paragraph">色相だけでなく、例えばデザインシステムのカラー設計で色相と明度（+彩度）を軸にしたカラーグリッドを作る場合でも、Oklchを使うことで視覚的に統一された色を簡単に作ることができます。</p>



<div class="wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper"><iframe id="cp_embed_eYXYjVq" src="//codepen.io/anon/embed/eYXYjVq?height=450&#038;theme-id=1&#038;slug-hash=eYXYjVq&#038;default-tab=result" height="450" scrolling="no" frameborder="0" allowfullscreen allowpaymentrequest name="CodePen Embed eYXYjVq" title="CodePen Embed eYXYjVq" class="cp_embed_iframe" style="width:100%;overflow:hidden">CodePen Embed Fallback</iframe></div>



<p class="wp-block-paragraph">使用する上で注意点を挙げるとするならば、oklchはカラースペースがsRGBではなく、より広い範囲の色を指定することができることです。</p>



<p class="wp-block-paragraph">極彩色に近い色だとディスプレイによっては色の差がわからないことがあるかもしれません。</p>



<p class="wp-block-paragraph">とはいえ一般的なスマートフォンやMacbookのようなdisplay-p3対応を謳っているディスプレイであれば、ほぼ正確に出力できます。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">参考にさせていただいたサイト<br><a href="https://griponminds.jp/blog/relearn-css-color/">https://griponminds.jp/blog/relearn-css-color/</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>FrankenPHPでサクッとWordPressの開発環境を作る</title>
		<link>https://blog.nasbi.jp/wordpress-on-frankenphp/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Sat, 23 Dec 2023 04:47:51 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=880</guid>

					<description><![CDATA[FrankenPHPは先日v1.0がリリースされたばかりの、PHPとWebサーバがセットになったDockerイメージです。 ちょうどローカルでWordPressを動かしたいタイミングがあったので試してみました 基本的には [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">FrankenPHPは先日v1.0がリリースされたばかりの、PHPとWebサーバがセットになったDockerイメージです。</p>



<p class="wp-block-paragraph">ちょうどローカルでWordPressを動かしたいタイミングがあったので試してみました</p>



<p class="wp-block-paragraph">基本的には<a href="https://github.com/dunglas/frankenphp-wordpress">dunglas/frankenphp-wordpress</a>にひな形があるのでこれを参考にします。</p>



<p class="wp-block-paragraph">まずはDockerfile。<br>ほぼひな形通りですが、PHP8.3以上の環境だと<a href="https://github.com/dunglas/frankenphp-wordpress/issues/11">Imagickのビルドでコケる</a>ので、今回は明示的に <code>sha-2eabec8-php8.2</code> を指定しています。</p>



<pre class="wp-block-code language-Dockerfile"><code>FROM dunglas/frankenphp:sha-2eabec8-php8.2

# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions)
RUN install-php-extensions \
    bcmath \
    exif \
    gd \
    intl \
    mysqli \
    zip \
    imagick \
    opcache

COPY --from=wordpress /usr/local/etc/php/conf.d/* /usr/local/etc/php/conf.d/
COPY --from=wordpress /usr/local/bin/docker-entrypoint.sh /usr/local/bin/
COPY --from=wordpress --chown=root:root /usr/src/wordpress /app/public

VOLUME /app/public

RUN sed -i \
    -e 's/\&#91; "$1" = '\''php-fpm'\'' \]/\&#91;\&#91; "$1" == frankenphp* \]\]/g' \
    -e 's/php-fpm/frankenphp/g' \
    -e 's#/usr/src/wordpress#/app/public#g' \
    /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT &#91;"/usr/local/bin/docker-entrypoint.sh"]
CMD &#91;"frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]
</code></pre>



<p class="wp-block-paragraph">docker-compose.ymlはそのままでも大丈夫そうだけど、好みの問題で下記のようにしました</p>



<pre class="wp-block-code language-compose.yaml"><code>version: '3.1'

services:

  wordpress:
    build: .
    ports:
      - 80:80
      - 443:443
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: docker
      WORDPRESS_DB_PASSWORD: docker
      WORDPRESS_DB_NAME: dbname
    volumes:
      - ./:/app/public

  db:
    image: mysql
    environment:
      MYSQL_DATABASE: dbname
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    ports:
      - 3306:3306</code></pre>



<p class="wp-block-paragraph">上記2ファイルをドキュメントルートの階層に置いて、起動すればlocalhostでアクセスできました。</p>



<pre class="wp-block-code"><code>docker compose up</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>aspidaのRestClientで多重送信を抑制する</title>
		<link>https://blog.nasbi.jp/aspidaのrestclientで多重送信を抑制する/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Wed, 22 Mar 2023 13:02:50 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=740</guid>

					<description><![CDATA[OpenAPIでAPIが定義されているプロジェクトにおいて @aspida/fetch を使ってRestClientを生成・利用しています。 とあるGETリクエストを複数のコンポーネントからほぼ同時に呼び出す必要があり、 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">OpenAPIでAPIが定義されているプロジェクトにおいて <code>@aspida/fetch</code> を使ってRestClientを生成・利用しています。</p>



<p class="wp-block-paragraph">とあるGETリクエストを複数のコンポーネントからほぼ同時に呼び出す必要があり、リクエストの回数を1回にまとめたい状況がありました。<br>これをaspidaでやろうと思ったときにちょっと思ったようにできなかったのでメモ。</p>



<p class="wp-block-paragraph">最初にやろうとした実装は、aspidaに渡すfetchをラップする方法。</p>



<pre class="wp-block-code language-javascript"><code>const promises = {};
function customFetch(url, option) {
if (option.method === 'GET') {
const key = `GET__${url}`;
if (!promises&#91;key]) {
promises&#91;key] = fetch(url, option);
}
return promises&#91;key];
} else {
return fetch(url, option);
}
}</code></pre>



<p class="wp-block-paragraph">promiseを再利用したいわけですが、こうすると、ResponseのStreamが使用済みなのでエラーになります。そりゃそうだ。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="58" src="https://blog.nasbi.jp/wp-content/uploads/2023/03/screenshot-2023-03-22-214357-1024x58.png" alt="" class="wp-image-750" srcset="https://blog.nasbi.jp/wp-content/uploads/2023/03/screenshot-2023-03-22-214357-1024x58.png 1024w, https://blog.nasbi.jp/wp-content/uploads/2023/03/screenshot-2023-03-22-214357-300x17.png 300w, https://blog.nasbi.jp/wp-content/uploads/2023/03/screenshot-2023-03-22-214357-768x44.png 768w, https://blog.nasbi.jp/wp-content/uploads/2023/03/screenshot-2023-03-22-214357.png 1374w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><code>Response.clone()</code> で複製してから使えばよいわけですが、aspidaの生成するtsコードで <code>Response.json()</code> を実行してしまうので、外からcloneを挟むようなことは無理そう…</p>



<p class="wp-block-paragraph">色々考えた結果、こうなりました</p>



<pre class="wp-block-code language-javascript"><code>const promises = {};
function dedupe(api, option) {
const key = `GET__${api.$path(option)}`;
if (!promises&#91;key]) {
promises&#91;key] = api.$get(option);
}
return promises&#91;key];
}</code></pre>



<p class="wp-block-paragraph">使う側では</p>



<pre class="wp-block-code language-javascript"><code>const result = await client.path.to.hoge.$get({ query: { fuga: 'piyo' } });</code></pre>



<p class="wp-block-paragraph">これを、</p>



<pre class="wp-block-code language-javascript"><code>const result = await dedupe(client.path.to.hoge, { query: { fuga: 'piyo' } });</code></pre>



<p class="wp-block-paragraph">こうする。</p>



<p class="wp-block-paragraph">関数で囲わなければいけなくなったのがちょっとスマートじゃないですが、まあ重複排除したいところだけ使えてポータブルな関数になったので良いということにしよう。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Google Publisher Tag のよくあるエラー</title>
		<link>https://blog.nasbi.jp/google-publisher-tag-error/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Fri, 03 Mar 2023 03:56:12 +0000</pubDate>
				<category><![CDATA[その他]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=717</guid>

					<description><![CDATA[googletag.openConsole() で表示される警告文の意味と主な対処方法の自分メモ 用語 GPT: Google Publisher TagGAM: Google Ad Manager ドキュメント ⚠️  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><code>googletag.openConsole()</code> で表示される警告文の意味と主な対処方法の自分メモ</p>



<h4 class="wp-block-heading">用語</h4>



<p class="wp-block-paragraph">GPT: Google Publisher Tag<br>GAM: Google Ad Manager</p>



<h4 class="wp-block-heading">ドキュメント</h4>



<ul class="wp-block-list">
<li><a href="https://developers.google.com/publisher-tag/reference?hl=ja">GPTリファレンス</a></li>
</ul>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 広告ユニットが取得されませんでした</h2>



<h3 class="wp-block-heading">何が起きているか</h3>



<p class="wp-block-paragraph">defineSlotは行われたが、displayやrefreshを行っておらず、広告リクエストが走っていない状態</p>



<h3 class="wp-block-heading">対処方法</h3>



<p class="wp-block-paragraph">displayやrefreshを実行する。</p>



<pre class="wp-block-code language-js"><code>googletag.display('div-id');</code></pre>



<pre class="wp-block-code language-js"><code>googletag.pubads().refresh(&#91;slot]);</code></pre>



<p class="wp-block-paragraph"><code>disableInitialLoad()</code> が利用されている場合は、displayでは広告リクエストが走らなくなるのでrefreshを使う。</p>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 広告ユニットが埋められませんでした</h2>



<h3 class="wp-block-heading">何が起きているか</h3>



<p class="wp-block-paragraph">配信条件に一致する広告申込情報が見つからずに広告を埋められなかった</p>



<h3 class="wp-block-heading">対処方法</h3>



<p class="wp-block-paragraph">GAMで該当の広告ユニットと広告申込情報の配信設定やターゲティングを見直す</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>CSSのfont-synthesisとtext-renderingが原因でAndroidで太字にならない</title>
		<link>https://blog.nasbi.jp/android-not-bold/</link>
		
		<dc:creator><![CDATA[suganuma]]></dc:creator>
		<pubDate>Tue, 28 Feb 2023 06:56:23 +0000</pubDate>
				<category><![CDATA[CSS]]></category>
		<guid isPermaLink="false">https://blog.nasbi.jp/?p=713</guid>

					<description><![CDATA[Viteで新規にプロジェクトを作るとapp.cssに font-synthesis と text-rendering が指定されています。 これらのプロパティがあると、 Android Chrome で見たときにfont [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Viteで新規にプロジェクトを作るとapp.cssに <code>font-synthesis</code> と <code>text-rendering</code> が指定されています。</p>



<pre class="wp-block-code language-css"><code>:root {
  ...
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  ...
}</code></pre>



<p class="wp-block-paragraph">これらのプロパティがあると、 Android Chrome で見たときにfont-weightで700以上を指定しても太字にならない場合があります。</p>



<p class="wp-block-paragraph"><code>font-synthesis</code> は、フォントに太字やイタリックの字体が含まれていない場合にブラウザが合成してよいかどうかを制御するもの。</p>



<p class="wp-block-paragraph"><code>text-rendering</code> は、合字やカーニングの処理を行うか否かを制御し、読みやすさと速度のどちらを優先するか決めることができます。</p>



<p class="wp-block-paragraph">これら2つの両方もしくはどちらかと、おそらく <code>font-family</code> によるフォント指定の組み合わせによって太字書体が使える環境にも関わらず太字にならないことがあるようです。</p>



<p class="wp-block-paragraph">基本的に欧文フォントのためのプロパティなので、日本語環境では、少なくとも現段階では使わないほうが無難なようです。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
