<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>yuheiy</title><description>ウェブデザインやフロントエンドに関する雑記と、外部サイトに寄稿した記事などの紹介。</description><link>https://yuheiy.com</link><item><title>段落間の余白の大きさを正しく均等に見えるようにする</title><link>https://www.figma.com/slides/GYDE0OIMmDw9cY8gLhl9xF</link><guid isPermaLink="true">https://www.figma.com/slides/GYDE0OIMmDw9cY8gLhl9xF</guid><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate></item><item><title>Space Toggleハックを使ってcolor-schemeに応じた値の切り替えを実現する</title><link>https://yuheiy.com/blog/2026/space-toggle-hack-for-switching-based-on-color-scheme</link><guid isPermaLink="true">https://yuheiy.com/blog/2026/space-toggle-hack-for-switching-based-on-color-scheme</guid><description>ライトモードとダークモードに対応したCSSを作成するとき、最初に考えられるのはprefers-color-schemeを使って宣言を上書きする方法だろう。</description><pubDate>Sat, 21 Feb 2026 11:30:00 GMT</pubDate><content:encoded>&lt;p&gt;ライトモードとダークモードに対応したCSSを作成するとき、最初に考えられるのは&lt;code&gt;prefers-color-scheme&lt;/code&gt;を使って宣言を上書きする方法だろう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;light&lt;/span&gt;&lt;span&gt; dark&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;prefers-color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかしプロパティの値だけを切り替えたいのであれば、宣言を重複して記述するのは冗長だ。このような場合は&lt;code&gt;light-dark()&lt;/code&gt;を使うと簡潔になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: light-dark(&lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: light-dark(&lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、値を変数化して使い回すこともできるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --fg-color-inverted&lt;/span&gt;&lt;span&gt;: light-dark(&lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-inverted&lt;/span&gt;&lt;span&gt;: light-dark(&lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--bg-color-inverted&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--fg-color-inverted&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、&lt;code&gt;light-dark()&lt;/code&gt;で切り替えられるのは色だけだ。たとえば&lt;code&gt;font-weight&lt;/code&gt;の値を切り替えたいとしても対応していない。そこで最近、&lt;a href=&quot;https://www.bram.us/2025/09/30/css-custom-light-dark/&quot;&gt;新たなCSS仕様として&lt;code&gt;color-scheme()&lt;/code&gt;が追加された&lt;/a&gt;。次のように&lt;code&gt;if()&lt;/code&gt;と組み合わせて使う。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#element&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-weight&lt;/span&gt;&lt;span&gt;: if(color-scheme(&lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;): &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;とはいえ、&lt;code&gt;color-scheme()&lt;/code&gt;はまだ実装が存在せず、&lt;code&gt;if()&lt;/code&gt;は実装状況が十分ではない。&lt;/p&gt;
&lt;p&gt;代替案として、現状ではSpace Toggleハックを使うと同様のことが実現できる。Space Toggleハックとは、&lt;code&gt;--foo: ;&lt;/code&gt;のようにカスタムプロパティの値として空白文字が有効であるという仕様を用いて、条件に応じた値の切り替えをする手法だ。たとえば次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;--toggler: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--red-if-toggler: var(--toggler) red;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;background: var(--red-if-toggler, green); &lt;/span&gt;&lt;span&gt;/* will be red! */&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;--toggler: initial;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--red-if-toggler: var(--toggler) red;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;background: var(--red-if-toggler, green); &lt;/span&gt;&lt;span&gt;/* will be green! */&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;--toggler&lt;/code&gt;を&lt;code&gt;--red-if-toggler&lt;/code&gt;に含めることで、&lt;code&gt;--toggler: ;&lt;/code&gt;の場合は&lt;code&gt;red&lt;/code&gt;が空白文字とともに出力され、&lt;code&gt;initial&lt;/code&gt;の場合は無効なプロパティとして評価されてフォールバック値が優先される仕組みになっている（コード例は&lt;a href=&quot;https://github.com/propjockey/css-sweeper?tab=readme-ov-file#basics-of-space-toggle&quot;&gt;propjockey/css-sweeper&lt;/a&gt;より）。値の前後に空白文字があっても実際の動作には影響せず無視されるというCSSの仕様によって成り立つものだ。&lt;/p&gt;
&lt;p&gt;これを応用して、次のように&lt;code&gt;prefers-color-scheme&lt;/code&gt;によって&lt;code&gt;--toggler&lt;/code&gt;に相当する値を切り替えると、&lt;code&gt;light-dark()&lt;/code&gt;と同様の仕組みを実現できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --light&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --dark&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;light&lt;/span&gt;&lt;span&gt; dark&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;prefers-color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --light&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --dark&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#element&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--color-bg-inverted&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: light-dark(&lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-weight&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--light&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--dark&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;デフォルトでは&lt;code&gt;--light&lt;/code&gt;が無効なので&lt;code&gt;500&lt;/code&gt;が採用され、&lt;code&gt;--dark&lt;/code&gt;は有効なので単なる空白文字になる。&lt;code&gt;color-scheme: light dark&lt;/code&gt;を併用することで&lt;code&gt;light-dark()&lt;/code&gt;も機能させられる。&lt;/p&gt;
&lt;p&gt;そして、&lt;code&gt;prefers-color-scheme&lt;/code&gt;を使わずに値を固定することで、常にライトモードもしくはダークモードに相当する状態も表現できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --light&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --dark&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;light&lt;/span&gt;&lt;span&gt; dark&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;prefers-color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --light&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --dark&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-theme&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;light&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --light&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --dark&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;light&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-theme&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;dark&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --light&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --dark&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color-scheme&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--light&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#aaa&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--dark&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#444&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これらの実装は&lt;a href=&quot;https://lightningcss.dev/transpilation.html#light-dark()-color-function&quot;&gt;Lightning CSSから着想を得た&lt;/a&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;カラースキーム以外のテーマを切り替える使い方もできる。たとえば、設定に応じて余白の大きさが変化する設計を考える。&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Base&lt;/th&gt;&lt;th&gt;Compact&lt;/th&gt;&lt;th&gt;Comfortable&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;050&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;100&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;150&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;data-*&lt;/code&gt;属性を使って切り替えるとすれば、次のように実装できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --is-size-base&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --is-size-compact&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --is-size-comfortable&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;[&lt;/span&gt;&lt;span&gt;data-size&lt;/span&gt;&lt;span&gt;=&apos;&lt;/span&gt;&lt;span&gt;compact&lt;/span&gt;&lt;span&gt;&apos;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-base&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-compact&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-comfortable&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;[&lt;/span&gt;&lt;span&gt;data-size&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;comfortable&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-base&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-compact&lt;/span&gt;&lt;span&gt;: ;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --is-size-comfortable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-050&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-base&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-compact&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-comfortable&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-100&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-base&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-compact&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-comfortable&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-150&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-base&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-compact&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--is-size-comfortable&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; data-size&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;comfortable&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bram.us/2025/02/18/css-at-function-and-css-if/&quot;&gt;CSS @function + CSS if() = 🤯 – Bram.us&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lea.verou.me/blog/2020/10/the-var-space-hack-to-toggle-multiple-values-with-one-custom-property/&quot;&gt;The -​-var: ; hack to toggle multiple values with one custom property • Lea Verou&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/the-css-custom-property-toggle-trick/&quot;&gt;The CSS Custom Property Toggle Trick | CSS-Tricks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>画像を囲うボーダーの色を半透明にしてコンテンツに馴染みやすくする</title><link>https://yuheiy.com/blog/2026/semi-transparent-image-borders</link><guid isPermaLink="true">https://yuheiy.com/blog/2026/semi-transparent-image-borders</guid><description>CSSのborderプロパティに半透明の色を指定すると、ボーダーが透過してその背景と合成される。</description><pubDate>Sat, 14 Feb 2026 04:45:00 GMT</pubDate><content:encoded>&lt;p&gt;CSSの&lt;code&gt;border&lt;/code&gt;プロパティに半透明の色を指定すると、ボーダーが透過してその背景と合成される。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; solid&lt;/span&gt;&lt;span&gt; oklch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.25&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rebeccapurple&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ボーダーの色はその背景に応じたものになり、単なる不透明なグレーを使うよりも馴染んで見えるようになる。背景が単色であれば、必ずしも半透明でなくても個別に調整した色を使うことができるが、特に画像やグラデーションが用いられている場合はこの手法が効果的である。&lt;/p&gt;
&lt;p&gt;しかし&lt;code&gt;img&lt;/code&gt;要素を使う場合、&lt;code&gt;border&lt;/code&gt;プロパティでは同様の効果を実現できない。&lt;code&gt;img&lt;/code&gt;要素のボーダーは画像の外側に描画されるが、前述の効果を実現するには、画像の上に覆い被さるように位置していなければならないからだ。&lt;code&gt;box-shadow&lt;/code&gt;プロパティと&lt;code&gt;inset&lt;/code&gt;キーワードを使ってボーダーを描画する手法もあるが、画像の後ろ側に隠れてしまう。&lt;/p&gt;
&lt;p&gt;そこで、代わりに&lt;code&gt;outline&lt;/code&gt;プロパティを使う。通常は要素の外側に描画されるが、&lt;code&gt;outline-offset&lt;/code&gt;プロパティで内側に移動させれば、画像の上にボーダーを重ねることができる。ラッパー要素などは不要だ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; solid&lt;/span&gt;&lt;span&gt; oklch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline-offset&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このようにして画像を半透明なボーダーで囲うと、ただ画像に馴染んで見えるだけではなく、不要なときには目立たなくなることも大きなメリットだ。&lt;/p&gt;
&lt;p&gt;というのも画像の周囲のボーダーは、画像と背景を同化させずに際立たせるためのものであり、たとえば白背景のページに白い画像を配置するときに境界線として機能させることを目的としている。一方黒い画像を使う場合、その画像があるだけで境界は明らかなので、ボーダーは必要ない。ところがCSSで一律ボーダーを適用していると、そのような区別がなく、不要なものにもボーダーが付与されて不恰好に見えることがよくある。&lt;/p&gt;
&lt;figure&gt;
&lt;figcaption&gt;不透明なボーダーを適用した例&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;だが半透明なボーダーであれば、黒い画像には同化して目立たなくなる。より正確に言えば、そのボーダーに近い色の画像と重なり合うと、ということだ。だからもし逆に、黒い背景に白い半透明なボーダーが使われていれば、白い画像に同化する。&lt;/p&gt;
&lt;figure&gt;
&lt;figcaption&gt;半透明なボーダーを適用した例&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;こうした仕組みによって、冗長にならない場合にのみボーダーが「見える」ことになる。&lt;/p&gt;</content:encoded></item><item><title>Astro Meetup Japan v0: パネルディスカッション</title><link>https://astrojp.connpass.com/event/376291/</link><guid isPermaLink="true">https://astrojp.connpass.com/event/376291/</guid><pubDate>Wed, 04 Feb 2026 00:00:00 GMT</pubDate></item><item><title>Tailwind CSSにおけるデフォルトのspacingとサジェストを無効化する方法</title><link>https://yuheiy.com/blog/2026/disable-default-spacing-and-suggestions-in-tailwindcss</link><guid isPermaLink="true">https://yuheiy.com/blog/2026/disable-default-spacing-and-suggestions-in-tailwindcss</guid><description>Tailwind CSSでは、テーマ変数の--spacingに応じてmarginユーティリティなどのスペースに関するCSSが生成される。--spacingではほかのテーマ変数と違って、値を一つひとつ指定するのではなく、乗数となる値を一つ指定することで、その計算式が値として用いられる仕組みだ。</description><pubDate>Sun, 01 Feb 2026 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tailwind CSSでは、テーマ変数の&lt;code&gt;--spacing&lt;/code&gt;に応じてmarginユーティリティなどのスペースに関するCSSが生成される。&lt;code&gt;--spacing&lt;/code&gt;ではほかのテーマ変数と違って、値を一つひとつ指定するのではなく、乗数となる値を一つ指定することで、その計算式が値として用いられる仕組みだ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.m-8&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、ユーティリティのキーには自由な数値を指定できるようになっている。&lt;/p&gt;
&lt;p&gt;一方エディタでの編集時には、事前設定された値がサジェストされるようになっている。これによって、デフォルトの選択肢を提案しつつもそこから外れた値も使用できるという緩い制約として機能する。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/suggestions.xMiTJTpv_ZJ1Nkr.webp&quot; alt=&quot;エディタでmt-クラスを入力中にサジェストが表示されている様子。mt-32、mt-36、mt-40、mt-44、mt-48、mt-52、mt-56、mt-60、mt-64、mt-72、mt-80、mt-96といった候補が縦に並ぶ&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;301&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;これらのサジェスト内容は&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v4.1.18/packages/tailwindcss/src/utilities.ts#L34-L69&quot;&gt;Tailwindのソースコード内に直接定義されているもの&lt;/a&gt;で、ユーザーがカスタマイズすることはできない。したがって、もしデザインの制約として独自のスペーシングセットを定義したいという場合でも、これを直接変更することはできない。&lt;/p&gt;
&lt;p&gt;しかし別の方法で設定することはできる。まず&lt;code&gt;--spacing&lt;/code&gt;を無効化したうえで、有効なスペーシングセットを明示的に指定する。これによって、デフォルトのサジェスト内容の代わりに独自のものが表示されるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@theme&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing: initial;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing-0: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing-050: calc(2 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing-100: calc(4 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing-150: calc(6 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* ... */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただしこの場合、クラス名として自由な値を直接記述することはできなくなる。代わりにarbitrary valuesとして記述する必要がある。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;m-150&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;m-[calc(3/16*1rem)]&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;また、デフォルトの&lt;code&gt;--spacing&lt;/code&gt;では常にrem単位が使用されるが、アクセシビリティの観点では好ましくない。具体的には、ブラウザやOSの文字サイズ拡大機能を使用した際、すべてにrem単位を使っていると文字サイズだけでなくすべてが同時に拡大してしまう（&lt;a href=&quot;https://x.com/adamwathan/status/2003638134840656373&quot;&gt;Tailwind作者のAdam Wathanもこの設計は失敗だったと認めている&lt;/a&gt;）。本来は、用途に応じてrem単位とpx単位を使い分けるべきだ。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--spacing&lt;/code&gt;を無効にすると、使用する単位をプロパティ別に設定できる。&lt;code&gt;--spacing-*&lt;/code&gt;はあらゆるユーティリティに影響するが、個別のユーティリティにだけ有効なテーマ変数も定義されている。これらのテーマ変数はドキュメント化されていないが、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v4.1.18/packages/tailwindcss/src/utilities.ts&quot;&gt;utilities.ts&lt;/a&gt;に実装されている&lt;code&gt;themeKeys&lt;/code&gt;の値を参考にすれば、次のようにしてプロパティ別のスペーシングを設定できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@theme&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing: initial;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --margin-0: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --margin-050: calc(2 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --margin-100: calc(4 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --margin-150: calc(8 / 16 &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* ... */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --padding-0: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --padding-050: 2px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --padding-100: 4px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --padding-150: 8px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* ... */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- margin: var(--margin-150); --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;m-150&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;!-- padding: var(--padding-150); --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;p-150&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>コンテンツに自律的に適応するレイアウトとコンポジション、アップデートされたレイアウトプリミティブ</title><link>https://yuheiy.com/blog/2025/flexible-layout-compositions</link><guid isPermaLink="true">https://yuheiy.com/blog/2025/flexible-layout-compositions</guid><description>「ウェブはデフォルトでレスポンシブ」とかねてより言われている。それを妨げるようなスタイリングをしないかぎり、ウェブページのレイアウトはブラウザの表示幅に合わせて自動的に調整される。</description><pubDate>Mon, 29 Dec 2025 23:10:00 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://web.archive.org/web/20170522090129/http://blog.andyhume.net/responsive-by-default/&quot;&gt;ウェブはデフォルトでレスポンシブ&lt;/a&gt;」とかねてより言われている。それを妨げるようなスタイリングをしないかぎり、ウェブページのレイアウトはブラウザの表示幅に合わせて自動的に調整される。&lt;/p&gt;
&lt;p&gt;レスポンシブデザインが普及する前、ウェブページは960pxの固定幅で作成することが一般的だった。当時のディスプレイ解像度は多くが1024pxの幅だったため、それに決め打ちした設計であった。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;960&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;固定幅のレイアウトは、余計なことをしなければ得られたはずのレスポンシブな性質を損なうものである。こうした固定的な設定を避けて、より柔軟なレイアウト手法で置き換えるのがレスポンシブデザインという戦略だ。&lt;/p&gt;
&lt;p&gt;たとえば固定幅のレイアウトは、代わりに最大幅を指定することで、表示幅が狭くてもそれに適応して収縮するようなレイアウトになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;960&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このとき、960pxという値は絶対のものではなくなる。幅は常に960pxではなく、広い範囲の中でのいずれかの値を取ることになる。960pxは境界値でしかない。あくまで妥当なレイアウトを実現するためのルールを提示しているのであって、唯一の解を強制するわけではないということ。&lt;/p&gt;
&lt;p&gt;そして、特定の表示幅だけを前提とするのではなく、いかなる表示幅にも適応させると考えると、最大幅が960pxであることの必然性も薄れていく。決まった画面サイズに当てはめるよりも、コンテンツをどのように見せたいか——つまり、コンテンツにとってどの最大幅が適切かに基づいて値を設定すべきである。&lt;/p&gt;
&lt;h2&gt;レイアウトプリミティブ&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://every-layout.dev/&quot;&gt;Every Layout&lt;/a&gt;（&lt;a href=&quot;https://www.borndigital.co.jp/book/24204/&quot;&gt;日本語訳版&lt;/a&gt;）では、レスポンシブなレイアウトの最小要素を独自に定義し、それらを&lt;a href=&quot;https://every-layout.dev/rudiments/composition/&quot;&gt;レイアウトプリミティブ&lt;/a&gt;と呼んでいる。レイアウトプリミティブの特徴は、メディアクエリ無しでレイアウトの自動調整ができることと、それらを組み合わせて複合的なレイアウトを作成できることだ。&lt;/p&gt;
&lt;p&gt;メディアクエリの問題は、特定の要素自身の大きさに基づいたルールを記述できないことにある。コンポーネントは300pxの幅のコンテナの中に配置されることもあれば、より広い500pxの幅のコンテナの中に配置されることもある。コンテナの大きさが異なれば、それに応じたレイアウトの調整が必要になることもあるが、メディアクエリではビューポートの幅に基づいたルールしか記述できない。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sidebar_viewport_constant.C15j_1Y3_Z26xUEr.svg&quot; alt=&quot;同じ幅の2つのビューポートを示す。1つ目のビューポートではコンポーネントが全幅を占め、2つ目のビューポートでは狭いコンテナによって幅が制限される&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;389&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://every-layout.dev/layouts/sidebar/&quot;&gt;The Sidebar: Every Layout&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;こうした問題を克服すべく、レイアウトプリミティブでは、要素自身の大きさに基づいたレイアウト調整ができるCSSの手法が組み込まれている。これによって要素の配置や折り返し位置が自動で処理されることになるため、個別にメディアクエリを記述して制御する必要がなくなる。&lt;/p&gt;
&lt;p&gt;さらにEvery Layoutでは、&lt;a href=&quot;https://every-layout.dev/rudiments/composition/&quot;&gt;ウェブにおけるレイアウトの多くはレイアウトプリミティブの組み合わせだけで実現できる&lt;/a&gt;と述べられている。メディアクエリに依存する従来のレイアウト手法では、個々の要素に応じた制御が難しく、その結果として汎用性や再利用性が制約されがちだった。しかしこれを打開することで、再利用の可能性が一気に開ける。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/composition_dialog_primitives.91Xp6NXH_2lP2PI.svg&quot; alt=&quot;ダイアログは、Cluster、Stack、Box、Centerといったレイアウトプリミティブを利用することで、個々のレイアウト要素に分解されている&quot; loading=&quot;lazy&quot; width=&quot;2394&quot; height=&quot;1191&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://every-layout.dev/rudiments/composition/&quot;&gt;Composition: Every Layout&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/composition_form.E3E_oKgx_Zd1hD1.svg&quot; alt=&quot;Cluster、Stack、Box、Centerといったレイアウトプリミティブと、ラベルと入力フィールドのペアをネストしたStackを使って作成された、3つのフィールドと送信ボタンを持つフォーム&quot; loading=&quot;lazy&quot; width=&quot;2786&quot; height=&quot;1428&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://every-layout.dev/rudiments/composition/&quot;&gt;Composition: Every Layout&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/composition_slide.BNqgGL42_fX6hr.svg&quot; alt=&quot;中央にテキスト、下部に前後のボタンが配置されたスライド。Cover、Box、Stack、Sidebarのプリミティブを使用して作成している&quot; loading=&quot;lazy&quot; width=&quot;4724&quot; height=&quot;2662&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://every-layout.dev/rudiments/composition/&quot;&gt;Composition: Every Layout&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;言い換えれば、コンテンツに自律的に適応するレイアウト要素を組み合わせることで、より複雑なレイアウトを自己組織化によって成立させられるということ。スクリーンサイズごとのレイアウトをトップダウンに規定するのではなく、局所的なルールから全体がボトムアップに立ち上がる。&lt;a href=&quot;https://terkel.jp/archives/2021/11/every-layout/&quot;&gt;内からの構成&lt;/a&gt;と言ってもよいだろう。&lt;/p&gt;
&lt;p&gt;このようなレイアウトプリミティブを駆使してレイアウトを構成することで、従来よりも効率的かつ堅牢なレスポンシブデザインが実現できるはずだ。&lt;/p&gt;
&lt;h2&gt;パターンの紹介&lt;/h2&gt;
&lt;p&gt;レイアウトプリミティブの妙は、パターンとしての完成度の高さにある。ウェブでよくあるレイアウト表現が、自律的に機能するプリミティブとしてうまく再定義されている。モジュラーな設計を実現するうえでは、こうした抽象化の精度こそが肝になる。&lt;/p&gt;
&lt;p&gt;Every Layoutの共著者である&lt;a href=&quot;https://bell.bz/links/&quot;&gt;Andy Bell&lt;/a&gt;は、後に&lt;a href=&quot;https://cube.fyi/&quot;&gt;CUBE CSS&lt;/a&gt;というCSS方法論を発表する。CUBE CSSには、レイアウトプリミティブにも似た&lt;a href=&quot;https://cube.fyi/composition.html&quot;&gt;Composition&lt;/a&gt;の概念があり、リファレンス実装として&lt;a href=&quot;https://piccalil.li/blog/a-css-project-boilerplate/&quot;&gt;それらのソースコードが公開されている&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;僕が思うに、これらはEvery Layoutの例と比べて、より実用的なパターン集になっている。多くはEvery Layoutと変わらないが、中でもあまり使われないものを取り除いたり、不足していたものを追加したりすることで、よく使うものが取り揃った使いやすいライブラリになった。これをアップデート版のレイアウトプリミティブと考えてもよいだろう。&lt;/p&gt;
&lt;p&gt;そこで、以降はCUBE CSSの例をもとに各レイアウトを簡単に紹介する。デモがついているので、ウインドウをリサイズしながら確認してほしい。これらのパターンを学ぶことで、開発者がより良い実装ができるだけでなく、デザイナーにとっても堅牢な設計パターンを理解するための助けになるだろう。&lt;/p&gt;
&lt;h2&gt;Flow (Stack)&lt;/h2&gt;
&lt;p&gt;垂直方向に積み重ねられた要素の間に一律したマージンを設定する。特定の要素の余白だけ調整することもできる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;flow&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Nullam id dolor id nibh ultricies vehicula ut id elit. Nulla vitae elit libero, a pharetra augue.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Nulla vitae elit libero, a pharetra augue. Cras justo odio, dapibus ac facilisis in, egestas eget quam.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;--flow-space: 3em&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;--flow-space&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt; set with an inline style to 3em: &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;style=&quot;--flow-space: 3em&quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;FLOW COMPOSITION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Like the Every Layout stack: https://every-layout.dev/layouts/stack/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Info about this implementation: https://piccalil.li/quick-tip/flow-utility/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.flow&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--flow-space&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/flow.css&quot;&gt;cube-boilerplate/src/css/compositions/flow.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/&quot;&gt;Axiomatic CSS and Lobotomized Owls – A List Apart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://24ways.org/2018/managing-flow-and-rhythm-with-css-custom-properties/&quot;&gt;Managing Flow and Rhythm with CSS Custom Properties ◆ 24 ways&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/stack/&quot;&gt;The Stack: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/my-favourite-3-lines-of-css/&quot;&gt;My favourite 3 lines of CSS - Piccalilli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapper (Center)&lt;/h2&gt;
&lt;p&gt;コンテンツを水平方向に中央配置して、最大幅を制限する。左右の余白を確保することで、狭い画面でもコンテンツが端に張り付かないようになっている。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrapper&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;I am centered and have a nice, consistent gutter.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;WRAPPER COMPOSITION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A common wrapper/container&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-inline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wrapper-max-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;vw&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/wrapper.css&quot;&gt;cube-boilerplate/src/css/compositions/wrapper.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/center/&quot;&gt;The Center: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/use-css-clamp-to-create-a-more-flexible-wrapper-utility/&quot;&gt;Use CSS Clamp to create a more flexible wrapper utility - Piccalilli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cluster&lt;/h2&gt;
&lt;p&gt;要素を水平方向に並べて、スペースが足りなくなったら自動的に折り返す。ラベルの長さに応じたレイアウトができる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;cluster&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 1&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 2&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 3&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 4&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 5&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 6&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 7&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 8&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CLUSTER&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;More info: https://every-layout.dev/layouts/cluster/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A layout that lets you distribute items with consitent&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;spacing, regardless of their size&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CUSTOM PROPERTIES AND CONFIGURATION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--gutter (var(--space-s-m)): This defines the space&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;between each item.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--cluster-horizontal-alignment (flex-start) How items should align&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;horizontally. Can be any acceptable flexbox aligmnent value.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--cluster-vertical-alignment How items should align vertically.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Can be any acceptable flexbox alignment value.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.cluster&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;wrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-s-m&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  justify-content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--cluster-horizontal-alignment&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;flex-start&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  align-items&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--cluster-vertical-alignment&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/cluster.css&quot;&gt;cube-boilerplate/src/css/compositions/cluster.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/cluster/&quot;&gt;The Cluster: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Repel&lt;/h2&gt;
&lt;p&gt;2つの要素を両端に配置し、スペースがあるときは引き離し、スペースが足りなくなったら積み重ねる。Every Layoutには存在しないパターン。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;repel&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 1 repels item 2&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 2 repels item 1&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;REPEL&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A little layout that pushes items away from each other where&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;there is space in the viewport and stacks on small viewports&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CUSTOM PROPERTIES AND CONFIGURATION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--gutter (var(--space-s-m)): This defines the space&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;between each item.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--repel-vertical-alignment How items should align vertically.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Can be any acceptable flexbox alignment value.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.repel&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;wrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  justify-content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;space-between&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  align-items&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--repel-vertical-alignment&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-s-m&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.repel&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-nowrap&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nowrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/repel.css&quot;&gt;cube-boilerplate/src/css/compositions/repel.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Sidebar&lt;/h2&gt;
&lt;p&gt;サイドバーとメインコンテンツを横に並べるパターン。サイドバーは指定した幅になり、メインコンテンツは残りのスペースを埋める。ビューポートの幅が狭くなり、両方を横に並べるスペースがなくなると、自動的に縦に積み重なる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;sidebar&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;I am the sidebar&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;flow&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;I am the content&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Etiam porta sem malesuada magna mollis euismod. Cras mattis consectetur purus sit amet fermentum.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum id ligula porta felis euismod semper.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;SIDEBAR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;More info: https://every-layout.dev/layouts/sidebar/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A layout that allows you to have a flexible main content area&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;and a &quot;fixed&quot; width sidebar that sits on the left or right.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;If there is not enough viewport space to fit both the sidebar&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;width *and* the main content minimum width, they will stack&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on top of each other&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CUSTOM PROPERTIES AND CONFIGURATION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--gutter (var(--space-size-1)): This defines the space&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;between the sidebar and main content.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--sidebar-target-width (20rem): How large the sidebar should be&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--sidebar-content-min-width(50%): The minimum size of the main content area&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EXCEPTIONS&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.sidebar[data-direction=&apos;rtl&apos;]: flips the sidebar to be on the right&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.sidebar&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;wrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-s-l&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.sidebar&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :first-child&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-basis&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--sidebar-target-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-grow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.sidebar&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :last-child&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-basis&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-grow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;999&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--sidebar-content-min-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/sidebar.css&quot;&gt;cube-boilerplate/src/css/compositions/sidebar.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/sidebar/&quot;&gt;The Sidebar: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/blog/sidebar-flex-basis-clamp/&quot;&gt;Complex conditional width using flex-basis with clamp: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/a-revisit-of-the-every-layout-sidebar-with-has-and-selector-performance/&quot;&gt;A revisit of the Every Layout sidebar with :has() and selector performance - Piccalilli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Switcher&lt;/h2&gt;
&lt;p&gt;2つの要素を横に並べ、コンテナの幅が指定した閾値を下回ると縦に積み重なる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;switcher&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 1&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 2&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;SWITCHER&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;More info: https://every-layout.dev/layouts/switcher/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A layout that allows you to lay **2** items next to each other&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;until there is not enough horizontal space to allow that.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CUSTOM PROPERTIES AND CONFIGURATION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--gutter (var(--space-size-1)): This defines the space&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;between each item&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--switcher-target-container-width (40rem): How large the container&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;needs to be to allow items to sit inline with each other&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--switcher-vertical-alignment How items should align vertically.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Can be any acceptable flexbox alignment value.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.switcher&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;wrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-s-l&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  align-items&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--switcher-vertical-alignment&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;flex-start&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.switcher&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-grow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-basis&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--switcher-target-container-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;999&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* Max 2 items,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;so anything greater than 2 is full width */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.switcher&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :nth-child&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;n + 3&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-basis&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/switcher.css&quot;&gt;cube-boilerplate/src/css/compositions/switcher.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://heydonworks.com/article/the-flexbox-holy-albatross/&quot;&gt;The Flexbox Holy Albatross: HeydonWorks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://heydonworks.com/article/the-flexbox-holy-albatross-reincarnated/&quot;&gt;The Flexbox Holy Albatross Reincarnated: HeydonWorks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/switcher/&quot;&gt;The Switcher: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Grid&lt;/h2&gt;
&lt;p&gt;グリッドレイアウトを自動的に生成する。指定した最小幅を保ちながら、利用可能なスペースに応じて列数を自動調整する。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;grid&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 1&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 2&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 3&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 4&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 5&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 6&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 7&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;Item 8&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/* AUTO GRID&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Related Every Layout: https://every-layout.dev/layouts/grid/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;More info on the flexible nature: https://piccalil.li/tutorial/create-a-responsive-grid-layout-with-no-media-queries-using-css-grid/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;A flexible layout that will create an auto-fill grid with&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;configurable grid item sizes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CUSTOM PROPERTIES AND CONFIGURATION&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--gutter (var(--space-s-m)): This defines the space&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;between each item.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--grid-min-item-size (14rem): How large each item should be&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ideally, as a minimum.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;--grid-placement (auto-fill): Set either auto-fit or auto-fill&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;to change how empty grid tracks are handled */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.grid&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;grid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-template-columns&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;repeat&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--grid-placement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;auto-fill&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    minmax&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--grid-min-item-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;fr&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--gutter&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-s-l&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* A split 50/50 layout */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.grid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-layout&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;halves&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --grid-placement&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto-fit&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --grid-min-item-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;vw&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;33&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* Three column grid layout */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.grid&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-layout&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;thirds&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --grid-placement&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto-fit&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --grid-min-item-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;33&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/Set-Creative-Studio/cube-boilerplate/blob/main/src/css/compositions/grid.css&quot;&gt;cube-boilerplate/src/css/compositions/grid.css at main ·
Set-Creative-Studio/cube-boilerplate&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;参考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tFKrK4eAiUQ&quot;&gt;Incredibly Easy Layouts with CSS Grid - YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/grid/&quot;&gt;The Grid: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/blog/create-a-responsive-grid-layout-with-no-media-queries-using-css-grid/&quot;&gt;Create a responsive grid layout - Piccalilli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>レスポンシブデザインの「間」を適切に設計するためのフルイドタイポグラフィとグリッドシステムの考え方——キャンバスインからコンテンツアウトへ</title><link>https://yuheiy.com/blog/2025/designing-the-in-between</link><guid isPermaLink="true">https://yuheiy.com/blog/2025/designing-the-in-between</guid><description>「PLAID Designer’s Advent Calendar 2025」の1日目の記事です。</description><pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://adventar.org/calendars/11716&quot;&gt;PLAID Designer’s Advent Calendar 2025&lt;/a&gt;」の1日目の記事です。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;レスポンシブデザインでウェブサイトの制作をする場合、デザイナーがモバイル幅とデスクトップ幅のデザインカンプを作成し、開発者がそれをもとに実装するというワークフローが一般的である。このとき決まって問題になるのが、デザインカンプとして定義されていない、モバイル幅とデスクトップ幅の「間」（もしくはその区間の外の幅）をどのように実装するかということだ。&lt;/p&gt;
&lt;p&gt;デザインカンプを作成する際は、標準的なビューポートとして決まったモバイル幅やデスクトップ幅に向けてデザインするだろう。しかし実際のユーザー環境は極めて多様で、&lt;a href=&quot;https://viewports.fyi/&quot;&gt;Set Studioの調査&lt;/a&gt;によれば12万件のデータから2300種類以上のビューポートのサイズが観測されたと言う。筆者がそのデータをもとに、典型的なデザインカンプの幅と一致するものを調べてみたところ、375pxは11.9%、1440pxはわずか1.15%だった。さらに、ユーザーは必ずしもブラウザを画面いっぱいに表示しているわけではなく、多くは好みの大きさにリサイズされているはずであり、昨今では&lt;a href=&quot;https://support.apple.com/ja-jp/guide/ipad/ipad08c9970c/ipados&quot;&gt;iPadでさえ複数ウインドウの表示が可能&lt;/a&gt;だ。&lt;/p&gt;
&lt;p&gt;この状況において、標準的なビューポートを定義することには無理がある。だからこそ、デザインカンプとして仮定された幅での見え方ばかりを重視するよりも、モバイル幅からデスクトップ幅までの幅広い範囲全体を分け隔てなく設計すると考えるべきだ。&lt;/p&gt;
&lt;p&gt;基本的に多くのデザインでは、モバイル幅では狭い描画領域に応じたシンプルなレイアウトで要素のサイズは小さく、デスクトップ幅では広い描画領域に応じた複雑なレイアウトで要素のサイズは大きくなる傾向がある。したがって、間の幅ではそれらの中間くらいになるようにデザインするのが適当と考えられる。&lt;/p&gt;
&lt;p&gt;しかし、その中間くらいのデザインをするには思いのほか手間がかかる。デスクトップ幅のデザインとモバイル幅のデザインを見比べながら、レイアウトや文字サイズ、余白などがちょうどよいところに落ち着くよう、周囲の要素とのバランスも鑑みながら一つひとつ調整していく作業はそれなりに大変なものだ。また、細かくブレークポイントを設けて個別にスタイルを上書きしていくことで、CSSの保守性も悪化してしまう。さらに複数の開発者が携わると、中間のデザインのバランス感覚や実装方式にブレが生じて問題が複雑化する。&lt;/p&gt;
&lt;p&gt;こうした諸問題は、個別のスタイル調整のたびに人の手を介在させなければならないことに由来して生じる。だがCSSの機能をうまく利用すれば、ブレークポイントの個別の調整なしに自ずとレスポンシブなレイアウトを構築できる。そして前述のような問題もかなり軽減されるのだ。今回は、そのために効果的なCSSの手法や考え方について紹介する。&lt;/p&gt;
&lt;h2&gt;フルイドタイポグラフィ&lt;/h2&gt;
&lt;p&gt;ビューポートの幅に応じて文字サイズを調整したいとき、まず思いつく手法は、メディアクエリを使ってブレークポイントごとに上書きしていくことだろう。このように実装すると、ブラウザをリサイズしてブレークポイントに達するたびに、レイアウトがガクンと切り替わるような見え方になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;768&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1280&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1600&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/fluid_typography-breakpoints.BlHI12rr_hcdXJ.webp&quot; alt=&quot;ブレークポイントでのフォントサイズの変化を表すグラフ。フォントサイズが階段状で急激に増加している&quot; loading=&quot;lazy&quot; width=&quot;1992&quot; height=&quot;1098&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot;&gt;Modern Fluid Typography Using CSS Clamp — Smashing
Magazine&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;この手法には大きく2つの問題がある。&lt;/p&gt;
&lt;p&gt;一つは、ブレークポイントごとにCSSを上書きする作業が必要なこと。一つの要素だけ調整するのなら簡単だが、実際はサイト全体に渡るあらゆる要素においてこの作業をすることになる。保守性の観点でも好ましくない。&lt;/p&gt;
&lt;p&gt;もう一つは、ブレークポイントに近づくにつれて見栄えが悪くなってしまうこと。モバイル幅からデスクトップ幅にかけてCSSを記述する場合、ビューポートの幅が広がることにともなって文字サイズの調整が必要になるたび、ブレークポイントを設けてCSSを上書きすることになる。しかし現実的な問題として、ブレークポイントを無数に設けるわけにはいかないので、見た目の理想状態から離れて許容できる限界に達するまでタイミングを間引いてから上書きするという判断になるがちだ。その結果、ブレークポイントの付近ではよい見た目にならない傾向がある。&lt;/p&gt;
&lt;p&gt;こうした問題の解決策として、文字サイズなどの値を自動調整する手法がある。CSSのvw単位などを駆使して、指定した最小値から最大値までをビューポートの幅に応じて線形に変化させる計算式を使った仕組みだ。これはフルイドタイポグラフィ（fluid typography）、もしくは単に文字を指してフルイドタイプ（fluid type）と呼ばれる（参考: &lt;a href=&quot;https://css-tricks.com/snippets/css/fluid-typography/&quot;&gt;Fluid Typography | CSS-Tricks&lt;/a&gt;）。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/fluid_typography-fluid.BftjS6Hh_nqSA2.webp&quot; alt=&quot;最小値から最大値までフォントサイズが滑らかに増加する様子を表すグラフ&quot; loading=&quot;lazy&quot; width=&quot;1850&quot; height=&quot;1106&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot;&gt;Modern Fluid Typography Using CSS Clamp — Smashing
Magazine&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;実装例はいくつかあるが、よりネイティブなアプローチとして、CSSの&lt;code&gt;@function&lt;/code&gt;を使ったものを紹介しておこう。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Creates fluid typography that scales with viewport.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;NOTE: This function is mathematically equivalent to `--responsive-value()` but optimized for typography. Use this for `font-size`, `--responsive-value()` for other properties.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@param {Length} --min - Minimum font size.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@param {Length} --max - Maximum font size.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@param {Length} --min-viewport - Minimum viewport width.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@param {Length} --max-viewport - Maximum viewport width.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@returns {Length} Fluid font size.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@example font-size: --fluid-type(16px, 24px, 320px, 1280px);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@function&lt;/span&gt;&lt;span&gt; --fluid-type(--min, --max, --min-viewport: 320px, --max-viewport: 1280px) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	--slope: calc((var(--max) - var(--min)) / (var(--max-viewport) - var(--min-viewport)));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	--intercept: calc(var(--min) - var(--slope) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; var(--min-viewport));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	result: clamp(var(--min), calc(var(--intercept) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; var(--slope) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 100vw), var(--max));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://github.com/sindresorhus/css-extras/blob/v0.4.0/index.css#L295-L311&quot;&gt;css-extras/index.css at v0.4.0 ·
sindresorhus/css-extras&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これによって、前述のブレークポイントを使った例は次のように置き換えられる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-size&lt;/span&gt;&lt;span&gt;: --fluid-type(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このフルイドタイポグラフィの良いところは、今まで通りのワークフローをほとんど変えることなく取り入れられることだ。モバイル幅の値を最小値に、デスクトップ幅の値を最大値にそのまま指定することで、ブレークポイントベースの実装をほぼ機械的に置き換えられる。&lt;/p&gt;
&lt;p&gt;そしてフルイドタイポグラフィは、文字サイズだけではなく、余白や要素の幅・高さなどにも併せて適用することでより効力を発揮する。ブレークポイントベースでの実装と同じく、全体のバランスが問題であり、そのためにはさまざまな要素を同時に調整することが肝心だからだ。&lt;/p&gt;
&lt;p&gt;注意点として、モバイル幅とデスクトップ幅で異なったあしらいが施されている場合、見たままの通りに最小値と最大値を指定して実装できないことがある。もしくは、間の幅に合わせてどちらでもないレイアウトをせざるを得ない場合もそうだ。そうした場合は、単に静的な値を指定するか、いい塩梅の変化になるように値を検証して指定する必要がある。&lt;/p&gt;
&lt;p&gt;前述の実装例は、好みのツールに合わせて書き換えるとよい。&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@function#browser_compatibility&quot;&gt;&lt;code&gt;@function&lt;/code&gt;の実装状況はまだ不十分なこと&lt;/a&gt;に加え、&lt;a href=&quot;https://github.com/web-platform-tests/interop/issues/513&quot;&gt;Firefoxでは異なる単位同士の乗算や除算に対応しておらず&lt;/a&gt;、そのままでは使用できないからだ。僕の場合はTailwind CSSを使うことが多いので、&lt;a href=&quot;https://github.com/yuheiy/sdenv/blob/main/postcss.config.ts&quot;&gt;独自のPostCSSプラグイン作成したうえで独自関数を導入している&lt;/a&gt;。これはTailwind CSSで次のように使用できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-[length:--fluid(2rem,5rem)]&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;もしくは次のようにしてもよい。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@theme&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-h1: --fluid(2rem, 5rem);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-h1&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;Utopia&lt;/h2&gt;
&lt;p&gt;フルイドタイポグラフィ自体は有用であるものの、しかしデザインのためのシステムとしてはこれだけでは不完全とも言える。レスポンシブデザインにおける値の指定は、単一の要素のみではなく、複数要素の組み合わせからなる問題だと考えると、その組み合わせをどのように生成すべきかが次の問いになるからである。&lt;/p&gt;
&lt;p&gt;具体的に言えば、レスポンシブデザインを前提としたタイプスケールや余白のバリエーションをどう設計するか。もしこれらを無秩序に設定すると、全体の統一感がなくなったり、ヒエラルキーを適切に表現できなくなったりしてしまう。だから、パターンをどこかのタイミングで決めて、その全体ルールに則って個別デザインを進めるのが定石だ。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/type-scale.BRmP-O4Y_26goGk.webp&quot; alt=&quot;タイプスケールの定義についての表。H1からH6、Subtitle 1-2、Body 1-2、Button、Caption、Overlineまでの各階層について、書体、ウェイト、サイズ、大文字小文字の扱い、文字間隔を示している&quot; loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;1025&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://m2.material.io/design/typography/the-type-system.html&quot;&gt;The type system - Material
Design&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;一方、レスポンシブデザインを前提とした方法論はあまり普及していないように思う。モバイル幅とデスクトップ幅でバリエーションは別々に定義するのか、それとも共通のセットが先にあってから幅によって値だけが変化するのか。モバイル幅での値とデスクトップ幅での値の比率はどのようになるのか。&lt;/p&gt;
&lt;p&gt;そこで、レスポンシブデザインのための体系的な方法論として生まれたのが&lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia&lt;/a&gt;である。Utopiaが提供するのは、タイプスケールと余白のバリエーションを生成するための2つのジェネレーターだ。&lt;/p&gt;
&lt;p&gt;Utopiaの狙いの一つは、デザイナーと開発者が同じ意図に基づいて制作すること。デザイナーはこれによって生成されたタイプスケールと余白を使ってデザインし、開発者はそうして指定された値をそのままコードに適用する。&lt;/p&gt;
&lt;p&gt;これらはフルイドタイポグラフィと同様の仕組みに基づいている。モバイル幅とデスクトップ幅を指定したうえで、それぞれのジェネレーターに固有の設定をすることで、そこからなるスケールとCSSが出力される。&lt;/p&gt;
&lt;p&gt;タイプスケールには&lt;a href=&quot;https://www.modularscale.com/&quot;&gt;モジュラースケール&lt;/a&gt;が採用されており、基準値と比率をそれぞれ幅ごとに指定する。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-type_scale_input.-aYoUgmG_21M3NT.webp&quot; alt=&quot;タイプスケール計算機の入力フォーム。Width、Font size、Typeの3つのフィールドが、左右に配置されている。左側はmin viewport、右側はmax viewport&quot; loading=&quot;lazy&quot; width=&quot;1048&quot; height=&quot;242&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/type/calculator/&quot;&gt;Fluid type scale calculator | Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-type_scale_table.zCEq2B1F_ZcvJhD.webp&quot; alt=&quot;タイプスケールを示す表。Scale stepの列には上から5から-1までが表示されている。Viewport widthが360と1240の列があり、行ごとに44.79と61.04、37.32と48.83のような値が表示されている&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;560&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/type/calculator/&quot;&gt;Fluid type scale calculator | Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;ステップごとの値を視覚化すると次のようになる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-type_scale_graph.DRYUtAbb_Z15slNB.webp&quot; alt=&quot;ビューポート幅に応じた5つのタイプスケールの変化を示すグラフ。横軸はビューポート幅で320pxから1500pxまで、縦軸はフォントサイズで10pxから70pxまで。H1からPまでの5本の平行な直線が右上がりに伸び、それぞれ320px、1024px、1500pxの3点でマーカーが配置されている。H1は35.25px、50.34px、63.15pxと変化し、H2は29.38px、39.35px、47.37px、H3は24.48px、30.75px、35.54px、H4は20.40px、24.04px、26.66px、Pは17.00px、18.79px、20.00pxとそれぞれ増加する&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/blog/designing-with-fluid-type-scales&quot;&gt;Designing with fluid type scales |
Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;余白はもっと単純で、単なる基準値の乗算になっている。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-space_table.BFaAIilS_Z2ekYA6.webp&quot; alt=&quot;余白の値の表。3XS、2XS、XS、S、M、L、XL、2XL、3XLの9つの行があり、@minの列と@maxの列に対応する値が表示されている&quot; loading=&quot;lazy&quot; width=&quot;952&quot; height=&quot;1015&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/space/calculator/&quot;&gt;Fluid space calculator | Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;こうして生成されたタイプスケールと余白を使って、まずデザイナーがデザインカンプを作成する。「タイトルはステップ2」「その周囲の余白はS」というように、パレットから値を選ぶようにしてデザインする。開発者がそれを実装すると、間の幅も含めた全体が自ずと出来上がる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-example_component.BHRCsmgT_Z1RSUn5.webp&quot; alt=&quot;Utopiaによって生成されたフルイドなタイプスケールと余白を使用したカードコンポーネントの例。左側(@min)は最小幅での表示、右側(@max)は最大幅での表示で、同じ構成だが文字サイズや余白がより大きい。余白がS、2XS、文字サイズがStep -1、Step 2など、定義されたスケールに基づいて各要素が最小幅から最大幅へと滑らかに変化することを示している&quot; loading=&quot;lazy&quot; width=&quot;1190&quot; height=&quot;842&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.figma.com/community/file/1385968382811167454&quot;&gt;Utopian project kickstarter with modes |
Figma&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/the-result.DhdDLQq0_Z1ALIBl.webp&quot; alt=&quot;320px、1024px、1500pxの3つのビューポート幅におけるレイアウトの比較。320pxと1500pxのデザインをデザインすると、Utopiaによって1024pxのデザインが自動的に生成されることを示している&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/blog/designing-with-fluid-type-scales&quot;&gt;Designing with fluid type scales |
Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;実際にUtopiaを使うとどのようなウェブページができるかについては、&lt;a href=&quot;https://demo.utopia.fyi/&quot;&gt;公式のデモ&lt;/a&gt;や&lt;a href=&quot;https://utopia.fyi/showcase/&quot;&gt;Showcaseの事例&lt;/a&gt;を開いてリサイズしてみるとわかりやすい。&lt;/p&gt;
&lt;p&gt;Utopiaは、デザイナーの設計の一部を自動化すると同時に制約するアプローチでもある。これが良い方向に働くこともあれば、そうならないこともあるだろう。特にモジュラースケールは思うように機能しないことも多いので、そのまま採用すると不便かもしれない。これについて、&lt;a href=&quot;https://standard.shiftbrain.com/blog/music-math-typography&quot;&gt;モジュラースケールの等比数列の代わりに調和数列を使うことを提案するものもある&lt;/a&gt;。また現在開発中のBootstrap 6では、&lt;a href=&quot;https://github.com/twbs/bootstrap/blob/353324bd9409ef6c026738b702e59ddd05bf6f1a/scss/_root.scss#L69-L78&quot;&gt;小さいサイズは固定値で大きいサイズはフルイドタイポグラフィという使い分けがなされている&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;いずれにしても、レスポンシブを前提とした値のセットをデザイナーと開発者で共有するアイデアは有用に感じる。というのも、モバイル幅とデスクトップ幅で別々にデザインカンプを作成していると、それぞれで別物のデザインをしているような意識になることがしばしばある。そうしたとき、文字サイズや余白に対して、モバイル幅とデスクトップ幅で共通のスケールを適用するという意識が生まれることで、良き制約として機能することを期待できるからだ。&lt;/p&gt;
&lt;h2&gt;キャンバスインからコンテンツアウトへ&lt;/h2&gt;
&lt;p&gt;これまでの僕の経験として、デザインカンプにおけるグリッドシステムの扱いには非常に苦心してきた。この指定をもとにCSSを作成しても、レスポンシブデザインとしてうまくいったと思えることはほとんどない。多くのデザイナーはそれを静的なアートボード上のガイドとしか捉えておらず、ビューポートが伸縮したときに機能するかどうかまでは基本的に考えていないからだ。&lt;/p&gt;
&lt;p&gt;そもそもグリッドシステムは、歴史的には紙媒体のデザインのなかで確立されてきた。紙媒体のデザインでは、レイアウトの出発点は紙面である。特定の紙面の寸法と比率に基づいて列と行のグリッドシステムに分割し、そのモジュール上にコンテンツを配置する。これは&lt;a href=&quot;https://markboulton.co.uk/journal/a-richer-canvas/&quot;&gt;キャンバスインとも呼ばれる&lt;/a&gt;。紙面という固定された明確な制約に基づいたプロセスであり、ビューポートのサイズが不定なウェブとは一線を画す。&lt;/p&gt;
&lt;p&gt;多くのレスポンシブデザインはいまだキャンバスインの発想で作成されている。デバイスのサイズごとに別々のアートボードを作成し、アートボードごとにグリッドを設定し、そのグリッドに合わせて要素を複製してサイズ調整する。これによって生じるのは、どのようなデバイスにもなめらかに適応するレスポンシブデザインではなく、ブレークポイントごとに分離された別々のデザインの出し分けだ。これについて『Every Layout』では次のように述べられている。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;Every Layoutでは、幅を基準としたメディアクエリの使用を避けています。メディアクエリでは、レイアウトの再構成をハードコーディングで表現することになります。さらに、要素やコンポーネントが実際に置かれている状況と結びついていないのです。先ほどの例のように、インターフェイスのスケーリングを個別の「ブレイクポイント」に基づいて行うのは恣意的な設定です。&lt;code&gt;960px&lt;/code&gt;は何か特別な幅なのでしょうか？　&lt;code&gt;959px&lt;/code&gt;で小さいサイズになることが本当に適切だといえるのでしょうか？&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/content_out-width_jump.-cd2awIx_UQwKs.webp&quot; alt=&quot;959pxと960pxの2つのビューポート幅におけるレイアウトを並べた図。1pxの違いでレイアウトが大きく変化することを示している&quot; loading=&quot;lazy&quot; width=&quot;816&quot; height=&quot;304&quot; /&gt;
&lt;figcaption&gt;ブレイクポイントを使用すると、1pxの違いが大きな「ジャンプ」になります。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/blockquote&gt;

&lt;figcaption&gt;— ヘイドン・ピカリング、アンディ・ベル著、安田祐平、横内宏樹監訳『&lt;cite&gt;Every Layout&lt;/cite&gt;』、株式会社ボーンデジタル、2021年&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;ウェブは流動的なメディアであり、ユーザーのビューポートのサイズを事前に知ることはできない。つまり、紙媒体のような固定的なグリッドシステムは成り立たない。だから発想を転換しなければいけない。&lt;/p&gt;
&lt;p&gt;グリッドには頼れない代わりに、ウェブではコンテンツからレイアウトを作成すると考える。ブレークポイントごとにページ全体を別物のレイアウトに書き換えてしまうのではなく、ビューポートの拡大にともなう個々のコンテンツの変化に着目し、必要に応じて個別に調整していくというアプローチだ。これを&lt;a href=&quot;https://markboulton.co.uk/journal/theinbetween/&quot;&gt;コンテンツアウトと呼ぶ&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;具体的な解説がweb.devにあったので引用しよう。特に動画の例を見るとわかりやすい。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;figure&gt;
&lt;figcaption&gt;画面が広くなると、ウィジェットの形状がそれに応じて変化します。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;（中略）&lt;/p&gt;
&lt;h3&gt;小さく始め、徐々に大きくして主要なブレークポイントを選択する&lt;/h3&gt;
&lt;p&gt;まず小さい画面サイズに収まるようにコンテンツを設計し、ブレークポイントが必要になるまで画面を拡大します。これにより、ページのブレークポイントの数を最小限に抑え、コンテンツに基づいて最適化できます。&lt;/p&gt;
&lt;p&gt;次の例では、このページの冒頭にある天気予報ウィジェットの例について説明します。最初のステップは、予報を小さい画面に適切に表示することです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/content_out-narrower.J-6Sc8Pt_Z2cfaTV.webp&quot; alt=&quot;モバイル幅の天気アプリ&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;1334&quot; /&gt;
&lt;figcaption&gt;幅が狭いアプリ。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;次に、要素間の空白が広すぎてウィジェットが見づらくなるまでブラウザのサイズを変更します。判断は主観的ですが、&lt;code&gt;600px&lt;/code&gt;を超えると明らかに広すぎます。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/content_out-wider.iGOo-Yy1_ZV5VyG.webp&quot; alt=&quot;アイテム同士の間隔が広い天気アプリ&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;720&quot; /&gt;
&lt;figcaption&gt;このサイズでは、アプリのレイアウトを変更する必要があります。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://web.dev/articles/responsive-web-design-basics?hl=ja&quot;&gt;レスポンシブ ウェブ デザインの基本  |  Articles  | 
web.dev&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;また「&lt;a href=&quot;https://ishadeed.com/article/container-queries-for-designers/&quot;&gt;CSS Container Queries For Designers&lt;/a&gt;」や「&lt;a href=&quot;https://ishadeed.com/article/css-container-query-guide/&quot;&gt;An Interactive Guide to CSS Container Queries&lt;/a&gt;」では、ほかにもいくつかのレイアウトパターンをContainer Queriesを使って作成する方法が紹介されている。&lt;/p&gt;
&lt;p&gt;しかし、このような設計をするにはコードを書きながらでないと難しい。デザイナーにとってはそこが障壁だろう。このやり方だけでページ全体のデザインを考えることも現実的ではないかもしれない。そこでデザインカンプでの作業を前提としたうえで、もう少し別の観点も紹介しよう。&lt;/p&gt;
&lt;h2&gt;グリッドシステムとの向き合い方&lt;/h2&gt;
&lt;p&gt;デザインカンプのグリッドシステムとしてもっとも典型的な設定は、ページを横に12分割するカラムだろう。一方、このようなグリッドに忠実なCSSを実装しても、あまり良い結果にならないことが多い。特定の幅のデザインカンプで良い見栄えになるとしても、レイアウトをそのカラムに沿わせたままウィンドウをリサイズするとたいてい不恰好だったり意図に反する結果になる。&lt;/p&gt;
&lt;p&gt;いくつか例を出そう。12カラムのうち左の3カラムがサイドバーになっているレイアウトがあるとする。そのサイドバーの幅がページ全体の3/12になるように実装すると、ページ全体の幅が少し狭まるだけでサイドバーの領域も狭まりすぎてしまうことが推測できる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-sidebar.y57NETW3_ZqoKfC.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;620&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;もしくは、12カラムの左右から2カラムずつずらして、8カラム分のコンテンツ領域を中央に配置するとする。この場合も同じく、そのコンテンツ領域をページ全体の8/12になるように実装すると、ページ全体の幅が少し狭まるだけでコンテンツ領域の幅も狭まりすぎてしまう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-centered.CuQDdTwq_1E7rTg.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;620&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;これについてのシンプルな解決策は、12カラムグリッドのようなガイドを拠り所にするのをやめることだ。コンテンツアウトの発想でデザインするなら、そのようなガイドがなくとも必要に応じたレイアウト調整がなされるだろう。&lt;/p&gt;
&lt;p&gt;では、前述の例をコンテンツアウトの発想で実装するならどうするか。サイドバーの幅には相対値の代わりに絶対値を指定した方が、より幅広い範囲のビューポートにおいて妥当に表示できそうだ。中央に配置された8カラムのコンテンツ領域は、相対値を指定する代わりに&lt;code&gt;max-width&lt;/code&gt;で絶対値を指定した方が、狭すぎる状態になることがなく、空間を無駄にせずに済む。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-sidebar_improved.jQucvKQQ_Z2vXizx.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;620&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-centered_improved.o6IssOuS_2jsP50.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;620&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;もっとも、これは周囲のコンテンツとの関係や別ページとの整合性にも関わる問題なので、一概にこの方が良いとは言い切れない。しかし、グリッドに沿わせるようにレイアウトすることは根本的にキャンバスインの発想で、ビューポートの変化に耐えられない傾向にある。&lt;/p&gt;
&lt;p&gt;一方で現実的には、レイアウトの当たりをつけるのにグリッドがなければ難しいこともある。だから落とし所として、「グリッドを使うのは良いがあくまで静的なレイアウトの目安として使う。実装の際には必ずしも採用しない」。デザイナーと開発者の間でそのように認識を合わせるのが良いのかもしれない。&lt;/p&gt;
&lt;p&gt;また、コンテンツアウトの発想での解釈ができるのは、必ずしもグリッドに沿わせなくてもレイアウトが成立する余地があるときだけだ。特にこのような調整が難しくなるのは、デザイナーがあらゆる要素をグリッドに沿わせようと固執しているときである。&lt;/p&gt;
&lt;p&gt;たとえば&lt;a href=&quot;https://spectrum.adobe.com/&quot;&gt;Spectrum&lt;/a&gt;では、グリッドにおける「レイアウト領域（layout regions）」の概念が文書化されている。これはグリッド上に配置される、任意のコンテンツを囲うコンテナのようなものを指す。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-layout_regions.C6oJJu4M_ZPXBP9.webp&quot; alt=&quot;グリッド上にレイアウト領域を配置した図。12カラムのグリッドがあり、その上に複数の灰色の矩形がレイアウト領域として配置されている。各レイアウト領域の境界はグリッドのカラムに沿っている&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;240&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://spectrum.adobe.com/page/responsive-grid/&quot;&gt;Responsive grid - Spectrum&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;このレイアウト領域のルールでは、レイアウト領域の縁はグリッドに沿わせるが、領域の内側のレイアウトは沿わせてはならない。そこまでもがグリッドに沿っていると、ボタンのラベルの長さなどの応じたレイアウトの調整が効かなくなってしまうからだ。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-layout_regions_do.b9Yki88e_Z1rT0j0.webp&quot; alt=&quot;グリッド上にレイアウト領域を配置し、その内側に要素を配置した良い例。12カラムのグリッドがあり、灰色のレイアウト領域が配置されている。領域の内側には、グリッドに沿わずに適切な間隔で配置されたボタンやテキストフィールドなどのUI要素が含まれている&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;192&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-layout_regions_dont.B9KKh7lN_HbeLe.webp&quot; alt=&quot;グリッド上にレイアウト領域を配置し、その内側に要素を配置した悪い例。12カラムのグリッドがあり、灰色のレイアウト領域が配置されている。領域の内側のUI要素がグリッドのカラムに沿って配置されており、要素間の間隔が不自然になっている&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;192&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://spectrum.adobe.com/page/responsive-grid/&quot;&gt;Responsive grid - Spectrum&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これは言い換えれば、レイアウト領域はキャンバスイン、レイアウト領域の内側はコンテンツアウトでレイアウトすることだとも言える。マクロなレイアウトはグリッドを拠り所にしてレイアウトしつつ、外側から規定しづらいミクロなレイアウトはコンテンツに応じて調整可能にするという使い分けである。その意味でキャンバスインとコンテンツアウトは、トップダウンとボトムアップの関係にあると言ってもよい。これは些細なことのようで、レスポンシブデザインを成立させるうえで重要な視点だ。&lt;/p&gt;
&lt;p&gt;レイアウト領域の内側では、グリッドに由来しない、何かしらの絶対的な値に基づいてレイアウトをする。そこで前述したUtopiaの例のように、利用する余白サイズのパターンを決めて用いることで、グリッドシステムにも似たシステム的なアプローチが実現される。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-proximity.0UrerKIO_ZrEkCg.webp&quot; alt=&quot;要素のレイアウトを余白サイズのパターンに基づいて構成する例。上部に小さな要素のグループ、下部に大きな要素のグループがあり、小さな要素のグループ内での余白はsm、大きな要素のグループ内での余白はmd、グループ同士の余白はlgが設定されている&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;333&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://gridless.design/for-designers&quot;&gt;gridless.design&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;また別の例を出そう。あるレイアウト領域のなかにまた別のレイアウトが入れ子になることがある。サイドバーと隣り合ったメインカラムのなかに、グリッド状のカードビューを表示するような場合だ。もしその内側のレイアウトも全体のグリッドに沿わせなければならないと解釈すれば、いろいろと窮屈なことになるだろう。しかしFigmaでは、外側のアートボードだけではなく、あらゆるフレームに対してグリッドを設定することができる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/grid_systems-figma_nested_grids.BEs-r05C_1QUkcb.webp&quot; alt=&quot;Figmaでの入れ子になったグリッドの設定画面。親フレームに12カラムのグリッドが設定され、その内側の子フレームには4カラムのグリッドが独立して設定されている様子を示している&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1336&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.figma.com/best-practices/everything-you-need-to-know-about-layout-grids/&quot;&gt;Everything you need to know about layout grids in
Figma&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これによってレイアウト領域の内側では、個々のコンテンツに応じたガイドの設定をすることもできる。アートボードレベルのグリッドに沿わせることだけが正しいやり方なのではない。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;「PLAID Designer’s Advent Calendar 2025」の2日目は、tomaさんによる「&lt;a href=&quot;https://note.com/tmtkd/n/n69238dcd83da&quot;&gt;プロダクトデザイナーが実践するtoB SaaSのNPS調査&lt;/a&gt;」です。&lt;/p&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://alistapart.com/article/responsive-web-design/&quot;&gt;Responsive Web Design – A List Apart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://markboulton.co.uk/journal/a-richer-canvas/&quot;&gt;A Richer Canvas - Mark Boulton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://markboulton.co.uk/journal/anewcanon/&quot;&gt;A New Canon - Mark Boulton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://markboulton.co.uk/journal/theinbetween/&quot;&gt;The In-Between - Mark Boulton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ethanmarcotte.com/books/responsive-design-patterns-and-principles/full/chap05/&quot;&gt;Responsive Design: Patterns and Principles, Chapter 5: Designing the Infinite Grid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atomicdesign.bradfrost.com/chapter-4/&quot;&gt;The Atomic Workflow | Atomic Design by Brad Frost&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://resilientwebdesign.com/chapter3/&quot;&gt;Resilient Web Design—Chapter 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/&quot;&gt;Relearn CSS layout: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2021/04/designing-developing-fluid-type-space-scales/&quot;&gt;Meet Utopia: Designing And Building With Fluid Type And Space Scales — Smashing Magazine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/new-responsive&quot;&gt;The new responsive: Web design in a component-driven world  |  Articles  |  web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gridless.design/&quot;&gt;gridless.design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/design/macro-layouts&quot;&gt;Macro layouts  |  web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/learn/design/micro-layouts&quot;&gt;Micro layouts  |  web.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ishadeed.com/article/responsive-design/&quot;&gt;The Guide To Responsive Design In 2023 and Beyond&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://piccalil.li/complete-css/&quot;&gt;Complete CSS - Piccalilli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>iframe要素の中身のコンテンツに応じて属性を自動設定できるAstroコンポーネントを作る</title><link>https://yuheiy.com/2025-09-13-smart-iframe</link><guid isPermaLink="true">https://yuheiy.com/2025-09-13-smart-iframe</guid><description>前回のブログを作成するにあたって、デモページをいくつも用意する必要があった。これまではCodePenで作成したものを埋め込んで表示していたが、ソースコードがブログと別の場所で管理されていることが煩わしかったので、代わりにデモページを同サイトに配置してiframe要素から読み込んで表示することにした。</description><pubDate>Fri, 12 Sep 2025 20:50:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2025-09-12-click-friendly-target-areas&quot;&gt;前回のブログ&lt;/a&gt;を作成するにあたって、デモページをいくつも用意する必要があった。これまではCodePenで作成したものを埋め込んで表示していたが、ソースコードがブログと別の場所で管理されていることが煩わしかったので、代わりにデモページを同サイトに配置して&lt;code&gt;iframe&lt;/code&gt;要素から読み込んで表示することにした。&lt;/p&gt;
&lt;p&gt;これをシンプルに実装すると次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/2025-09-12-click-friendly-target-areas/navbar-bad.html&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ナビゲーションバーのデモ（悪い例）&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;128&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;width: 100%&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このうち、&lt;code&gt;title&lt;/code&gt;属性にはそのドキュメントのタイトルを設定する。そして、&lt;code&gt;iframe&lt;/code&gt;要素をそのドキュメントとピッタリ同じ高さにするために、ドキュメントの高さを&lt;code&gt;height&lt;/code&gt;属性に設定する。これらを手動で設定するのはやや手間なので、次のようにして自動化した。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/content/blog/2025-09-12-click-friendly-target-areas.mdx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; SmartIframe &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;../../components/SmartIframe.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;SmartIframe&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/2025-09-12-click-friendly-target-areas/navbar-bad.html&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;src/components/SmartIframe.astro&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { HTMLAttributes } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;astro/types&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { chromium } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;playwright&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; invariant &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;tiny-invariant&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; Props&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; HTMLAttributes&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;iframe&apos;&lt;/span&gt;&lt;span&gt;&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  src&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;attrs&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Astro.props;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;invariant&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; src &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &apos;string&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;SmartIframe requires a src prop&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; url&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt;(src, Astro.url).href;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; browser&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; chromium.&lt;/span&gt;&lt;span&gt;launch&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; page&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; browser.&lt;/span&gt;&lt;span&gt;newPage&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page.&lt;/span&gt;&lt;span&gt;setViewportSize&lt;/span&gt;&lt;span&gt;({ width: &lt;/span&gt;&lt;span&gt;756&lt;/span&gt;&lt;span&gt;, height: &lt;/span&gt;&lt;span&gt;720&lt;/span&gt;&lt;span&gt; }); &lt;/span&gt;&lt;span&gt;// iframeを表示するページのコンテンツ幅を指定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page.&lt;/span&gt;&lt;span&gt;goto&lt;/span&gt;&lt;span&gt;(url);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; page.&lt;/span&gt;&lt;span&gt;waitForLoadState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; page.&lt;/span&gt;&lt;span&gt;evaluate&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  document.title,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  document.documentElement.offsetHeight,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; browser.&lt;/span&gt;&lt;span&gt;close&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ width: &lt;/span&gt;&lt;span&gt;&apos;100%&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;attrs}&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;span&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Playwrightを使ってドキュメントを実際に描画し、そのタイトルと高さを取得して&lt;code&gt;iframe&lt;/code&gt;要素に割り当てている。これによって、&lt;code&gt;src&lt;/code&gt;属性さえ指定すれば、それ以外の属性の手動指定は不要になる。&lt;/p&gt;
&lt;p&gt;また、&lt;code&gt;iframe&lt;/code&gt;要素のなかのドキュメントの高さは、&lt;code&gt;iframe&lt;/code&gt;要素が描画される際の幅によって変化することがある。&lt;code&gt;iframe&lt;/code&gt;要素の高さをこれに応じたものにするために、クライアントサイドでの動的な処理を追加する。ResizeObserverを使ってドキュメントの高さの変化を監視し、それを&lt;code&gt;iframe&lt;/code&gt;要素に反映する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ width: &lt;/span&gt;&lt;span&gt;&apos;100%&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;attrs}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  data-smart-iframe&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;iframe&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; iframe&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HTMLIFrameElement&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;span&gt;&apos;[data-smart-iframe]&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    iframe.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; resizeObserver&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; ResizeObserver&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;blockSize&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; entries[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].contentBoxSize[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        iframe.height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; blockSize.&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (iframe.contentDocument) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        resizeObserver.&lt;/span&gt;&lt;span&gt;observe&lt;/span&gt;&lt;span&gt;(iframe.contentDocument.documentElement);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、ドキュメントの実際の高さに応じて動的に変化するレスポンシブな&lt;code&gt;iframe&lt;/code&gt;要素を実現できた。&lt;/p&gt;</content:encoded></item><item><title>クリックしやすいターゲットエリアを実装する #web_ui_devs</title><link>https://yuheiy.com/2025-09-12-click-friendly-target-areas</link><guid isPermaLink="true">https://yuheiy.com/2025-09-12-click-friendly-target-areas</guid><description>この記事は、「Web UI 実装勉強会 #1」での同タイトルの発表をもとにしたものです。</description><pubDate>Thu, 11 Sep 2025 16:20:00 GMT</pubDate><content:encoded>&lt;p&gt;この記事は、「&lt;a href=&quot;https://ui-devs.connpass.com/event/364448/&quot;&gt;Web UI 実装勉強会 #1&lt;/a&gt;」での同タイトルの発表をもとにしたものです。&lt;/p&gt;
&lt;h2&gt;ターゲットエリアを広げる工夫&lt;/h2&gt;
&lt;p&gt;リンクやボタンなどのUI要素を押したときに反応する領域のことをターゲットエリアと呼びます。素朴に実装すれば、その要素の視覚的なサイズがそのままターゲットエリアになります。&lt;/p&gt;
&lt;p&gt;しかし場合によっては、これではあまり使い勝手が良くならないことがあります。要素自体のサイズが小さいと押すことができる領域も狭くなるので、その分正確性が求められて押しづらくなります。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;そういうときには、視覚的なサイズよりも余分にターゲットエリアを広げるテクニックを使います。&lt;code&gt;padding&lt;/code&gt;プロパティを使って要素自体のサイズを広げつつ、レイアウトが崩れないように同じ大きさのネガティブマージンを適用します。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一方、プレーンなテキストリンクと違って、背景色などがついたボタンなどの場合、&lt;code&gt;padding&lt;/code&gt;プロパティのテクニックは少し使いづらいです。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;そのような場合は、擬似要素を使って相対的な位置指定をするのが簡単です。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;::before {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position: &lt;/span&gt;&lt;span&gt;absolute&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    inset&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-6&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;iOSにおけるターゲットエリア&lt;/h2&gt;
&lt;p&gt;このような工夫は僕が独自に考えたものではなくて、昔からさまざまな実装に採用されています。たとえばiOSにおいては、ナビゲーションバーのボタンではボタン自体の大きさよりも大きなターゲットエリアが確保されているそうです（参考: &lt;a href=&quot;https://www.sociomedia.co.jp/4152&quot;&gt;iPhone の当たり判定を検証した&lt;/a&gt;）。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/navbar-marked.CMleGt7D_Zshiq8.webp&quot; alt=&quot;ナビゲーションバーの左側のEditボタンと右側の+ボタンには、その見た目よりも大きなターゲットエリアが設定されている&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; /&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://www.sociomedia.co.jp/4152&quot;&gt;ソシオメディア | iPhone の当たり判定を検証した&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;興味深いのは、キーボードのターゲットエリアはタイピングの途中で動的に変化するようになっていることです。ユーザーが一文字タイプするたびに、次にくる文字を予測して、キーごとに重み付けがされているのです。&lt;/p&gt;
&lt;p&gt;QWERTYキーボードのキーの幅はかなり狭く、通常時のターゲットエリアのサイズは32ptです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/keyboard-marked.CH-ARIXb_Z13Glq0.webp&quot; alt=&quot;Dキーのターゲットエリアの幅は32pt&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; /&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://www.sociomedia.co.jp/4152&quot;&gt;ソシオメディア | iPhone の当たり判定を検証した&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;しかし、「WORLD」という単語を打つつもりで「WORL」までをタイプすると、次に来る可能性が高いDキーのターゲットエリアが広がって44ptになるそうです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/keyboard-enlarged-marked.6MtqUBb2_ZroNmj.webp&quot; alt=&quot;Dキーのターゲットエリアの幅は44pt&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; /&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://www.sociomedia.co.jp/4152&quot;&gt;ソシオメディア | iPhone の当たり判定を検証した&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;このような高度な実装をしてまでも、ターゲットエリアの大きさを確保することが重要なのだと思います。&lt;/p&gt;
&lt;h2&gt;ターゲットサイズの基準&lt;/h2&gt;
&lt;p&gt;とはいえ、現実的にどこまで大きくすればいいかについては、基準がなければ判断が難しいものです。&lt;/p&gt;
&lt;p&gt;一つの目安として、WCAGにはターゲットサイズに関する達成基準が二つあります。そのなかで、最低限の基準としては24px × 24px、より高度な基準としては44px × 44pxを満たすことが定められています。また、Human Interface Guidelinesでは44pt × 44ptが基準になっています。&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ガイドライン&lt;/th&gt;&lt;th&gt;サイズ&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://waic.jp/translations/WCAG22/Understanding/target-size-minimum.html&quot;&gt;WCAG SC 2.5.8&lt;/a&gt;&lt;/td&gt;&lt;td&gt;24px × 24px&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://waic.jp/translations/WCAG22/Understanding/target-size-enhanced.html&quot;&gt;WCAG SC 2.5.5&lt;/a&gt;&lt;/td&gt;&lt;td&gt;44px × 44px&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/accessibility#Mobility&quot;&gt;Human Interface Guidelines&lt;/a&gt;&lt;/td&gt;&lt;td&gt;44pt × 44pt&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;これを踏まえて、24px × 24pxの大きさは最低限確保するようにしつつ、状況に応じて44px × 44pxも達成することを目指すと考えるとよいでしょう。&lt;/p&gt;
&lt;h2&gt;押せそうな場所は押せるようにする&lt;/h2&gt;
&lt;p&gt;また別の例として、本来押せそうに見えるのに押せないようになっている実装をたまに見かけることがあります。わかりやすい例としては、チェックボックスです。チェックボタンを押せば反応するけど、そのラベルを押しても反応しないようになっていることがあります。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;checkbox&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt; Do you need animations?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;慣習的に考えても、チェックボックスはラベルまで含めて押せるようになっているべきです。実装としては、&lt;code&gt;label&lt;/code&gt;要素で囲えばそれを実現できます。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;checkbox&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt; Do you need animations?&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;もう少し複雑な例として、アイコンと&lt;code&gt;input&lt;/code&gt;要素の組み合わせからなる検索フィールドの実装についても考えてみます。こうしたUI要素は、マークアップとして見れば複数の要素を組み合わせた複合的なコンポーネントですが、ユーザーにとっては単一の入力フィールドです。このことを意識せずに、見た目だけを再現するつもりで実装すると、限られたエリアしか押すことができない次のような実装になってしまいます。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search-field&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.search-field&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;inline-flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; solid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  input&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;transparent&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これは実装モデルに引っ張られてしまったがゆえの失敗です。理想的には、複数の要素の組み合わせであることをユーザーに意識させないような実装になっているべきです。つまり、普通の入力フィールドと同じようにどこを押してもフォーカスできるようになっているのが良いでしょう。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search-field&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.search-field&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  svg&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;absolute&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    pointer-events&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  input&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; solid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;無闇に広げすぎない&lt;/h2&gt;
&lt;p&gt;一方、ターゲットエリアは必ずしも広ければ広いほど良いわけでもありません。&lt;/p&gt;
&lt;p&gt;リスト項目が縦にスタックしたようなナビゲーションがあるとします。そのなかのリンクに&lt;code&gt;display: block&lt;/code&gt;を適用すると、コンテナサイズいっぱいまでターゲットエリアが拡張されます。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一見、ターゲットエリアが広がって良いことのように思えますが、別の側面から見れば、対象の要素と関係なさそうな場所を押しても反応してしまうことになります。すると、「リンクを押すつもりはないのに、意図せず押したことになってしまった」ということが起こり得ます。&lt;/p&gt;
&lt;p&gt;この改善例としては、&lt;code&gt;width: fit-content&lt;/code&gt;などを適用して必要以上に伸び広がらないように制御するのが良いでしょう。また&lt;code&gt;padding&lt;/code&gt;のテクニックも併用して、周辺に少しだけ広げる工夫もするとなお良いです。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-inline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-12&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-inline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fit-content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;似たような例として、入力フィールドのラベルのターゲットエリアがあります。レイアウトの都合で&lt;code&gt;display: block&lt;/code&gt;を適用することはよくありますが、その場合も同様の問題が起こります。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;こうしたときにも、&lt;code&gt;width: fit-content&lt;/code&gt;などを適用して範囲を制御するのがよいと思います。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fit-content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;今回の勉強会についての余談&lt;/h2&gt;
&lt;p&gt;今回のこの勉強会に参加したのは、開催者の&lt;a href=&quot;https://x.com/ktsn&quot;&gt;Katashinさん&lt;/a&gt;が以前投稿していた「&lt;a href=&quot;https://sizu.me/ktsn/posts/tsuuvsk35ed1&quot;&gt;UI の実装に関する勉強会をやりたい&lt;/a&gt;」という問題意識に共感したからだ。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;最近また勉強会に参加するようになって色々と行っているうちに、ふと UI の実装に関する勉強会ってほとんどないなと思った。UI デザインはちらほら見かけるけど、UI 実装は見かけない。&lt;/p&gt;
&lt;p&gt;たいてい TypeScript、Vue、Biome みたいに言語、ライブラリ、ツールをテーマにしたものが多くて、UI を切り口にしているものを見かけない。もちろん、他のテーマの勉強会で UI を絡めた話がある場合もあるけど、UI 実装をテーマに据えた会でいろいろな話を聞いてみたい。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://sizu.me/ktsn/posts/tsuuvsk35ed1&quot;&gt;UI の実装に関する勉強会をやりたい&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;同日に投稿されていた、&lt;a href=&quot;https://x.com/_baku89&quot;&gt;橋本麦さん&lt;/a&gt;の「&lt;a href=&quot;https://scrapbox.io/glisp/Jul_24,_2025:_%E8%A9%B0%E3%82%81%E6%96%B9%E3%81%AE%E5%95%8F%E9%A1%8C&quot;&gt;Jul 24, 2025: 詰め方の問題&lt;/a&gt;」にも感化された部分がある。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;結局のところ、ぼくが気になっているのは、ものごとを媒介する関係性や構造ばかりが語られて、肝心のそれ自体——グラフィックであれ、アニメーションであれ、画面に現れる&lt;a href=&quot;https://scrapbox.io/glisp/%E8%A1%A8%E5%B1%A4&quot;&gt;表層&lt;/a&gt;的な“何か”そのものが、語られることなくどこかへ押しやられていることだ。もちろん、それをめぐる構造、物語性、組織や経済がどう影響しているかは、無視できるわけじゃない。でも、だからといってそれ「ばかり」に意識を向けるのは、&lt;a href=&quot;https://scrapbox.io/glisp/Lev_Manovich&quot;&gt;Lev
Manovich&lt;/a&gt;が言うように、画面の「出力」だけを見てその背後にあるプログラムのアーキテクチャを問わないのと同じくらい片手落ちだ。それは生存戦略として間違っちゃいないにせよ、グラフィックデザイン文化としては貧しいと思う。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://scrapbox.io/glisp/Jul_24,_2025:_%E8%A9%B0%E3%82%81%E6%96%B9%E3%81%AE%E5%95%8F%E9%A1%8C&quot;&gt;Jul 24, 2025:
詰め方の問題&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;個人的には、自分の近辺のコミュニティで、UIそのものについて語られる機会が少ないと思っていた。みんなある程度のキャリアを経ると、そういう手前の話は「通過済み」のものとして理解されてしまうのかもしれない。けれど僕にとっては最初と変わらず喫緊の問題だし、いつまで経っても、世の中に新たな出来の悪いUIが生み出され続けていることに辟易としている。&lt;/p&gt;
&lt;p&gt;また、開発者のUIに対する無頓着さについても気になっていた。開発者としてコードを書いていると、何かを形にするための方法にばかり視点が向いてしまうことがある。しかしその方法としての妥当性の前に、形にしようとしているそのものに目を向けることが重要だし、それに付き従って構造ができるのだ。ダメなものの構造を強化しても仕方がない。これは分業体制下において抜け落ちがちな意識かもしれない。&lt;/p&gt;
&lt;p&gt;だからこそ、UIのデザイン意図と実装が交差するような話をしたいと思った。本来デザインと実装の垣根はないが、何かしらの肩書きのもとで植え付けられた意識が、そこに壁を作っている。そうしたナンセンスな区分けにとらわれずに、それそのものについてフラットな視点で考えられるようにしたい。&lt;/p&gt;
&lt;p&gt;こういう考え方がもっと広められると良いし、そのような機会があれば連帯したい。次回も開催されるようなので参加したいと思う。&lt;/p&gt;
&lt;p&gt;あと、久しぶりに会った&lt;a href=&quot;https://x.com/terkel&quot;&gt;terkelさん&lt;/a&gt;に「相変わらずfundamentalな話してる」と評されたのがうれしかった。&lt;/p&gt;</content:encoded></item><item><title>UIとフロントエンド技術が好きなエンジニア→デザインエンジニアを募集しています</title><link>https://note.com/_yuheiy/n/ne1107ea6b1f8</link><guid isPermaLink="true">https://note.com/_yuheiy/n/ne1107ea6b1f8</guid><pubDate>Sat, 06 Sep 2025 00:00:00 GMT</pubDate></item><item><title>package.jsonのdependenciesをアップデートする技術</title><link>https://tech.plaid.co.jp/techniques-for-updating-dependencies-in-packagejson</link><guid isPermaLink="true">https://tech.plaid.co.jp/techniques-for-updating-dependencies-in-packagejson</guid><pubDate>Wed, 13 Aug 2025 00:00:00 GMT</pubDate></item><item><title>Figmaのstylesをもとにして「Tailwind CSSのテーマ」「tailwind-mergeの設定」「Storybookのドキュメント」などの各種ファイルを自動生成する</title><link>https://tech.plaid.co.jp/figma-styles-to-tailwindcss-and-storybook</link><guid isPermaLink="true">https://tech.plaid.co.jp/figma-styles-to-tailwindcss-and-storybook</guid><pubDate>Tue, 12 Aug 2025 00:00:00 GMT</pubDate></item><item><title>Tailwind CSS 4をShadow DOM内で動作させる方法</title><link>https://yuheiy.com/2025-08-10-tailwindcss4-in-shadow-dom</link><guid isPermaLink="true">https://yuheiy.com/2025-08-10-tailwindcss4-in-shadow-dom</guid><description>ページ内のスタイルの競合を避けるため、Shadow DOMのなかでTailwind CSSを読み込みたいという場面があった。これはTailwind 3では問題なく実現できたが、Tailwind 4では一部のスタイルが適用されなくなる問題が発生した。</description><pubDate>Sun, 10 Aug 2025 08:50:00 GMT</pubDate><content:encoded>&lt;p&gt;ページ内のスタイルの競合を避けるため、Shadow DOMのなかでTailwind CSSを読み込みたいという場面があった。これはTailwind 3では問題なく実現できたが、Tailwind 4では一部のスタイルが適用されなくなる問題が発生した。&lt;/p&gt;
&lt;p&gt;具体的な実装は次のようなものである。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; root &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react-shadow&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// ViteのExplicit URL Importsを使用する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// https://vite.dev/guide/assets.html#explicit-url-imports&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; styleSheetURL &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./index.css?url&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 px-4 py-2 font-semibold text-white shadow-lg transition duration-300 hover:scale-105 hover:from-blue-600 hover:to-purple-700&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      Save changes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Inside a shadow root:&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;root.div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;styleSheetURL&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;root.div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Outside a shadow root:&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;期待される描画結果は次の通り。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/preview.CVYdnajp_ZWxbgY.webp&quot; alt=&quot;shadow rootの内側のボタンにはそれらしいスタイルが適用され、外側のボタンはユーザーエージェントスタイルシートそのままの見た目になっている&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;192&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;しかし実際には背景のグラデーションが適用されず、不完全な状態で表示されてしまった。&lt;/p&gt;
&lt;p&gt;原因を調べてみると、どうやらTailwind 4で使用されている@propertyルールがShadow DOMのなかで動作しないせいであるようだ。Shadow DOMのなかでは@propertyルールが無視されて、カスタムプロパティが未定義扱いになってしまう。&lt;/p&gt;
&lt;p&gt;Tailwind 4では、@propertyルールを使ってカスタムプロパティを定義している。一部のユーティリティはこれによって定義されたカスタムプロパティに依存しているため、Shadow DOMのなかでは正しく機能しない。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@layer&lt;/span&gt;&lt;span&gt; utilities {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .bg-gradient-to-r&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --tw-gradient-position&lt;/span&gt;&lt;span&gt;: to &lt;/span&gt;&lt;span&gt;right&lt;/span&gt;&lt;span&gt; in oklab;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    background-image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-stops&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .from-blue-500&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --tw-gradient-from&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--color-blue-500&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --tw-gradient-stops&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-via-stops&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-position&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-from&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-from-position&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-to&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-to-position&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .to-purple-600&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --tw-gradient-to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--color-purple-600&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --tw-gradient-stops&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-via-stops&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-position&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-from&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-from-position&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-to&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-gradient-to-position&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-position {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-from {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;color&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-via {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;color&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-to {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;color&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-stops {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-via-stops {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-from-position {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;&lt;/span&gt;&lt;span&gt;length-percentage&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: 0%;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-via-position {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;&lt;/span&gt;&lt;span&gt;length-percentage&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: 50%;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@property&lt;/span&gt;&lt;span&gt; --tw-gradient-to-position {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  syntax: &quot;&amp;lt;&lt;/span&gt;&lt;span&gt;length-percentage&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial-value&lt;/span&gt;&lt;span&gt;: 100%;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@layer&lt;/span&gt;&lt;span&gt; properties {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @supports&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;-webkit-hyphens&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;margin-trim&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt;))) &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;-moz-orient&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;rgb&lt;/span&gt;&lt;span&gt;(from &lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt; r g b)))) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    *&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;::before&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;::after&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;::backdrop&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-from&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-via&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#0000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-stops&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-via-stops&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initial&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-from-position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-via-position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      --tw-gradient-to-position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、@propertyルールが使用できない環境のためのフォールバックも実装されている。&lt;code&gt;@layer properties&lt;/code&gt;の内側にあるフィーチャークエリがそれである。&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v4.1.11/packages/tailwindcss/src/ast.ts#L697-L705&quot;&gt;MozillaとSafariのためとコメントされている&lt;/a&gt;が、ブラウザを問わずこれが有効になるように書き換えられればうまく動作するはずだ。&lt;/p&gt;
&lt;p&gt;やり方はいくつか考えられるが、PostCSSプラグインとしてASTを経由して書き換えることにした。要件としては次の2つだ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@propertyルールを削除する（削除しなくても動作するが、不要なものはない方がよいため）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@layer properties&lt;/code&gt;のなかにある宣言ブロックを有効にするために、その間にあるフィーチャークエリだけを取り除く&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これに準じて次のように実装した。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;postcss.config.ts&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Plugin } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;postcss&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Config } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;postcss-load-config&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// @propertyルールはShadow DOM内で動作しないため削除する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; removeAtProperty&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  postcssPlugin: &lt;/span&gt;&lt;span&gt;&apos;remove-at-property&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  AtRule: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    property&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;atRule&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      atRule.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// `@layer properties`の内側にあるフィーチャークエリを取り除いて、@propertyルールのフォールバックをすべてのブラウザで有効化する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// https://github.com/tailwindlabs/tailwindcss/blob/v4.1.11/packages/tailwindcss/src/ast.ts#L697-L705&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; transformPropertiesLayer&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  postcssPlugin: &lt;/span&gt;&lt;span&gt;&apos;transform-properties-layer&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  AtRule: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    layer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;atRule&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (atRule.params &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &apos;properties&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      atRule.&lt;/span&gt;&lt;span&gt;walkAtRules&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;supports&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;supportsRule&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; supportedRules&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; supportsRule.nodes;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        supportsRule.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (supportedRules) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          atRule.&lt;/span&gt;&lt;span&gt;append&lt;/span&gt;&lt;span&gt;(supportedRules);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Config&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [removeAtProperty, transformPropertiesLayer],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; config;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>Tailwind CSS 4において意図せずoutline-colorがtransitionしてしまう問題の対処法</title><link>https://yuheiy.com/2025-07-21-workaround-for-unintended-transition-of-outline-color-in-tailwindcss4</link><guid isPermaLink="true">https://yuheiy.com/2025-07-21-workaround-for-unintended-transition-of-outline-color-in-tailwindcss4</guid><description>Tailwind CSS 4では、transitionユーティリティおよびtransition-colorsユーティリティのtransition-propertyプロパティにoutline-colorプロパティが含まれるように変更された（参考: Upgrade guide - Getting started - Tailwind CSS）。</description><pubDate>Mon, 21 Jul 2025 09:55:00 GMT</pubDate><content:encoded>&lt;p&gt;Tailwind CSS 4では、&lt;code&gt;transition&lt;/code&gt;ユーティリティおよび&lt;code&gt;transition-colors&lt;/code&gt;ユーティリティの&lt;code&gt;transition-property&lt;/code&gt;プロパティに&lt;code&gt;outline-color&lt;/code&gt;プロパティが含まれるように変更された（参考: &lt;a href=&quot;https://tailwindcss.com/docs/upgrade-guide#transitioning-outline-color&quot;&gt;Upgrade guide - Getting started - Tailwind CSS&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;変更前（Tailwind CSS 3）:&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.transition&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;, opacity, box-shadow, transform, filter, &lt;/span&gt;&lt;span&gt;-webkit-backdrop-filter&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;, opacity, box-shadow, transform, filter, backdrop-filter;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;, opacity, box-shadow, transform, filter, backdrop-filter, &lt;/span&gt;&lt;span&gt;-webkit-backdrop-filter&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-timing-function&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-duration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.transition-colors&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-timing-function&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-duration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;変更後（Tailwind CSS 4）:&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.transition&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, outline-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, &lt;/span&gt;&lt;span&gt;-webkit-backdrop-filter&lt;/span&gt;&lt;span&gt;, backdrop-filter, display, visibility, content-visibility, &lt;/span&gt;&lt;span&gt;overlay&lt;/span&gt;&lt;span&gt;, pointer-events;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-timing-function&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-ease&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--default-transition-timing-function&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-duration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-duration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--default-transition-duration&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.transition-colors&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt;, background-color, border-color, outline-color, text-decoration-color, &lt;/span&gt;&lt;span&gt;fill&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stroke&lt;/span&gt;&lt;span&gt;, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-timing-function&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-ease&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--default-transition-timing-function&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-duration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-duration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--default-transition-duration&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これにともなって、要素をフォーカスしたときのインジケータの色が一瞬&lt;code&gt;currentcolor&lt;/code&gt;になってしまう問題が生じることがある。&lt;/p&gt;
&lt;p&gt;Chromeのユーザーエージェントスタイルシートでは、フォーカス時の&lt;code&gt;outline-color&lt;/code&gt;プロパティの値として&lt;code&gt;-webkit-focus-ring-color&lt;/code&gt;が指定されている。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:focus-visible&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; -webkit-focus-ring-color&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://chromium.googlesource.com/chromium/src.git/+/refs/tags/140.0.7306.1/third_party/blink/renderer/core/html/resources/html.css#1344&quot;&gt;third_party/blink/renderer/core/html/resources/html.css - chromium/src.git - Git at
Google&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;通常は即座にこの色に変化するが、&lt;code&gt;transition&lt;/code&gt;の対象になっていると、&lt;code&gt;currentcolor&lt;/code&gt;を開始点として&lt;code&gt;-webkit-focus-ring-color&lt;/code&gt;に遷移するという挙動になる。これによって、一瞬黒く見えてしまう。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;このような挙動になるのはChromeのみで、SafariやFirefoxではtransitionが有効にならない。とはいえ、このような挙動は好ましくないため、何かしらの方法で修正する必要がある。&lt;/p&gt;
&lt;p&gt;Tailwind CSSのUpgrade Guideでは、そのための回避策として、条件にかかわらず&lt;code&gt;outline-color&lt;/code&gt;の値が変化しないように指定することが提案されている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&amp;lt;button class=&quot;transition hover:outline-2 hover:outline-cyan-500&quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&amp;lt;button class=&quot;outline-cyan-500 transition hover:outline-2&quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://tailwindcss.com/docs/upgrade-guide#transitioning-outline-color&quot;&gt;Upgrade guide - Getting started - Tailwind
CSS&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;しかし、フォーカス可能なすべての要素に対してこのような指定をすることは現実的ではない。このような個別の指定が必要になると、必然的に間違いが起こるだろう。そのため、代わりとなる別のアプローチをいくつか挙げて検討してみる。&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;transition-property&lt;/code&gt;のデフォルト値を上書きする&lt;/h2&gt;
&lt;p&gt;最初に考えられる方法としては、&lt;code&gt;transition-property&lt;/code&gt;プロパティの値から&lt;code&gt;outline-color&lt;/code&gt;プロパティを取り除くことだ。そのために、それぞれのユーティリティの実装を調べてみる。&lt;/p&gt;
&lt;p&gt;まず、&lt;code&gt;transition&lt;/code&gt;ユーティリティは次のように実装されている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;functionalUtility&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;transition&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  defaultValue:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  themeKeys: [&lt;/span&gt;&lt;span&gt;&apos;--transition-property&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  handle&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    decl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;transition-property&apos;&lt;/span&gt;&lt;span&gt;, value),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    decl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;transition-timing-function&apos;&lt;/span&gt;&lt;span&gt;, defaultTimingFunction),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    decl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;transition-duration&apos;&lt;/span&gt;&lt;span&gt;, defaultDuration),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v4.1.11/packages/tailwindcss/src/utilities.ts#L4502-L4511&quot;&gt;tailwindcss/packages/tailwindcss/src/utilities.ts at v4.1.11 ·
tailwindlabs/tailwindcss&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;もう一方の、&lt;code&gt;transition-colors&lt;/code&gt;ユーティリティは次のように実装されている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;staticUtility&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;transition-colors&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;transition-property&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  [&lt;/span&gt;&lt;span&gt;&apos;transition-timing-function&apos;&lt;/span&gt;&lt;span&gt;, defaultTimingFunction],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  [&lt;/span&gt;&lt;span&gt;&apos;transition-duration&apos;&lt;/span&gt;&lt;span&gt;, defaultDuration],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v4.1.11/packages/tailwindcss/src/utilities.ts#L4478&quot;&gt;tailwindcss/packages/tailwindcss/src/utilities.ts at v4.1.11 ·
tailwindlabs/tailwindcss&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これらを上書きするには、&lt;code&gt;@utility&lt;/code&gt;ディレクティブを使ってCSSを記述する。ここで指定されている値から&lt;code&gt;outline-color&lt;/code&gt;プロパティを取り除いて指定する。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@utility&lt;/span&gt;&lt;span&gt; transition {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    color, &lt;/span&gt;&lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;border-color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;text-decoration-color&lt;/span&gt;&lt;span&gt;, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, &lt;/span&gt;&lt;span&gt;box-shadow&lt;/span&gt;&lt;span&gt;, transform, translate, scale, rotate, &lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;, -webkit-backdrop-filter, &lt;/span&gt;&lt;span&gt;backdrop-filter&lt;/span&gt;&lt;span&gt;, display, visibility, &lt;/span&gt;&lt;span&gt;content-visibility&lt;/span&gt;&lt;span&gt;, overlay, &lt;/span&gt;&lt;span&gt;pointer-events&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@utility&lt;/span&gt;&lt;span&gt; transition-colors {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition-property&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    color, &lt;/span&gt;&lt;span&gt;background-color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;border-color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;text-decoration-color&lt;/span&gt;&lt;span&gt;, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、これらユーティリティのプロパティ値は予期せず変更される可能性があるため、Tailwind CSSのアップデートにともなう変更を監視して追従できるようにしておく必要がある。&lt;/p&gt;
&lt;h2&gt;2. &lt;code&gt;transition&lt;/code&gt;ユーティリティの対象プロパティを個別に指定する&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;transition&lt;/code&gt;ユーティリティ自体の実装に手を入れずとも、arbitrary valuesとしてユーティリティの値を調整することはできる。次のようにして対象のプロパティだけを個別に指定すれば、意図しないプロパティのtransitionが有効になってしまうことを回避できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;bg-blue-500 transition-[background-color] hover:bg-indigo-500&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;3. フォーカス可能な要素すべての&lt;code&gt;outline-color&lt;/code&gt;プロパティにデフォルト値を指定する&lt;/h2&gt;
&lt;p&gt;先ほどの説明の繰り返しになるが、&lt;code&gt;outline-color&lt;/code&gt;プロパティの値が変化しないように指定すれば意図しないtransitionを回避できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&amp;lt;button class=&quot;transition hover:outline-2 hover:outline-cyan-500&quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&amp;lt;button class=&quot;outline-cyan-500 transition hover:outline-2&quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;a href=&quot;https://tailwindcss.com/docs/upgrade-guide#transitioning-outline-color&quot;&gt;Upgrade guide - Getting started - Tailwind
CSS&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;この例はカスタムの&lt;code&gt;outline-color&lt;/code&gt;プロパティを適用するものだが、ブラウザデフォルトのフォーカスリングを適用したい場合には次のようにする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;outline-[-webkit-focus-ring-color] transition&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、要素がフォーカスされてフォーカスリングが表示される際のtransitionを無効化できる。&lt;/p&gt;
&lt;p&gt;Firefoxではフォーカス時の&lt;code&gt;outline-color&lt;/code&gt;プロパティの値は&lt;code&gt;currentcolor&lt;/code&gt;になっているが、このように&lt;code&gt;-webkit-focus-ring-color&lt;/code&gt;を指定した場合はパースエラーとして扱われて無視される。&lt;/p&gt;
&lt;h2&gt;4. すべての要素の&lt;code&gt;outline-color&lt;/code&gt;プロパティのデフォルト値を上書きする&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;outline-color&lt;/code&gt;プロパティを個別に設定するアプローチでは、やはり作業の抜け漏れが生じる可能性がある。それよりも、あらかじめすべての要素のプロパティ値を指定してしまうのが良いだろう。次のようにして実装する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@layer&lt;/span&gt;&lt;span&gt; base {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  *&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ::after&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ::before&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ::backdrop&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ::&lt;/span&gt;&lt;span&gt;file-selector-button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    outline-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-webkit-focus-ring-color&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>サードパーティウィジェットのアクセシビリティ</title><link>https://talks.yuheiy.com/2025/accessibility-of-third-party-widgets/#/1</link><guid isPermaLink="true">https://talks.yuheiy.com/2025/accessibility-of-third-party-widgets/#/1</guid><pubDate>Fri, 27 Jun 2025 00:00:00 GMT</pubDate></item><item><title>VSCodeのGenerate alt textを使って代替テキストを自動生成する</title><link>https://yuheiy.com/2025-05-25-generate-alt-text-in-vscode</link><guid isPermaLink="true">https://yuheiy.com/2025-05-25-generate-alt-text-in-vscode</guid><description>VSCodeエクステンションのGitHub Copilot Chat 0.27にて、画像の代替テキストを自動生成できる機能が追加された。</description><pubDate>Sun, 25 May 2025 07:10:00 GMT</pubDate><content:encoded>&lt;p&gt;VSCodeエクステンションのGitHub Copilot Chat 0.27にて、&lt;a href=&quot;https://marketplace.visualstudio.com/items/GitHub.copilot-chat/changelog#user-content-generate-alt-text-in-html-or-markdown&quot;&gt;画像の代替テキストを自動生成できる機能が追加された&lt;/a&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;この機能を使用するには、HTMLファイルまたはMarkdownファイル内の画像がある行までカーソルを移したうえで、キーボードショートカットの&lt;kbd&gt;⌘.&lt;/kbd&gt;を使うか、行の上部に表示される電球アイコンを選択する。&lt;code&gt;alt&lt;/code&gt;属性の値が空であれば「Generate alt text」、すでに存在すれば「Refine alt text」がコードアクションとして表示される。&lt;/p&gt;
&lt;p&gt;アクションを実行するとインラインチャットが立ち上がり、「Generate alt text」の場合は次のプロンプトが実行される。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Create an alt text description that is helpful for screen readers and people who are blind or have visual impairment. Never start alt text with &quot;Image of...&quot; or &quot;Picture of...&quot;. Please clearly identify the primary subject or subjects of the image. Describe what the subject is doing, if applicable. Please add a short description of the wider environment. If there is text in the image please transcribe and include it. Please describe the emotional tone of the image, if applicable. Do not use single or double quotes in the alt text.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;「Refine alt text」であれば次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Refine the existing alt text for clarity and usefulness for screen readers. Create an alt text description that is helpful for screen readers and people who are blind or have visual impairment. Never start alt text with &quot;Image of...&quot; or &quot;Picture of...&quot;. Please clearly identify the primary subject or subjects of the image. Describe what the subject is doing, if applicable. Please add a short description of the wider environment. If there is text in the image please transcribe and include it. Please describe the emotional tone of the image, if applicable. Do not use single or double quotes in the alt text.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;チャットからのレスポンスが返ってきたタイミングで、その結果が値として挿入される。&lt;/p&gt;
&lt;h2&gt;自動生成の使いどころと注意点&lt;/h2&gt;
&lt;p&gt;いくつかの記事や画像に対してこの機能を試してみたが、内容にかかわらずそこそこの精度の出力ができた。たとえば複雑な図表でも、プロンプトが優秀なのかかなり正確に内容を出力できる。少なくとも、ほとんどの人にとっては自力で記述するよりも良い結果になる可能性が高い。注意点もあるので順を追って説明する。&lt;/p&gt;
&lt;p&gt;まず、この自動生成機能は、ブログなどの記事中で登場する画像のために使うのが適切だと思われる。代替テキストの書き方にはいくつかパターンがあるが、&lt;a href=&quot;https://www.w3.org/WAI/tutorials/images/decision-tree/ja&quot;&gt;&lt;code&gt;alt&lt;/code&gt;ディシジョンツリー&lt;/a&gt;に則れば、「&lt;a href=&quot;https://www.w3.org/WAI/tutorials/images/informative/&quot;&gt;意味のある画像&lt;/a&gt;」もしくは「&lt;a href=&quot;https://www.w3.org/WAI/tutorials/images/complex/&quot;&gt;複雑な画像&lt;/a&gt;」に当てはまる例に適用するのに向いている。&lt;/p&gt;
&lt;p&gt;それ以外のパターンについては、この自動生成を用いると冗長になりがちであり、人が自ら記述したほうがうまくいく。たとえば単なる「&lt;a href=&quot;https://www.w3.org/WAI/tutorials/images/textual/#styled-text-decorative-effect&quot;&gt;文字画像&lt;/a&gt;」や、ボタンのなかで使用するアイコンなど（「&lt;a href=&quot;https://www.w3.org/WAI/tutorials/images/functional/&quot;&gt;機能を持つ画像&lt;/a&gt;」）については、制作者自身にとっては記述内容は自明なのでわざわざAIに頼る必要もないだろう。言い換えれば、自動生成ができるとしても、&lt;code&gt;alt&lt;/code&gt;ディシジョンツリーくらいは最低限理解しておく必要がある。&lt;/p&gt;
&lt;p&gt;この自動生成では、対象の画像に含まれる情報だけでなく、同ファイルの記述内容も考慮した結果になる。このおかげで、単なる画像単体の説明ではなく、ある程度前後の文脈も踏まえた出力ができているようだ。したがって、同一ファイルに本文もいっしょに記述された形式であることが望ましい。つまり、マークダウンファイルで記事データが管理されているブログなどに向いている。先に本文を書き切ってから、最後に代替テキストを生成すると精度が上がるだろう。&lt;/p&gt;
&lt;p&gt;あとは、代替テキストがたまに英語で出力されてしまうことがある。プロンプトが英語であるせいでそれに引っ張られてしまうようだ。インラインチャットで続けて「as japanese」などと要求すれば日本語にしてくれる。もしくは、&lt;a href=&quot;https://docs.github.com/ja/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot&quot;&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/a&gt;に「代替テキストを作成する際は、日本語として出力すること。」のように書いておけばだいたい日本語になる。&lt;/p&gt;
&lt;p&gt;運が悪いと、マークアップの代替テキスト部分だけでなく、タグ全体を丸ごと出力結果で置き換えてしまうことがある。プロンプトが改善されればこのような現象は回避できるかもしれないが、とりあえず今の時点ではそのたび手動で直していくのが早い。&lt;/p&gt;
&lt;p&gt;そして、一般的に良いモデルを使うほど良い結果になる。Claude 3.7 SonnetとClaude Sonnet 4で結果を比較してみたが、後者のほうが明らかに的確な内容が出力された。&lt;/p&gt;
&lt;h2&gt;Generate alt textの実装を調べる&lt;/h2&gt;
&lt;p&gt;この機能はコードアクションとして提供されているものだが、たとえばVSCode以外のエディタやその他のAIエージェントなど、別の経路からも実行できると便利だ。そこで、これがどのように実装されているかを理解するため、エクステンションのソースコードを調べてみる。&lt;/p&gt;
&lt;p&gt;GitHub Copilotのリポジトリは公開されていないので、ローカルにある&lt;code&gt;~/.vscode/extensions/&lt;/code&gt;を対象に「Generate alt text」の文字列を検索すると、&lt;code&gt;~/.vscode/extensions/github.copilot-chat-0.27.0/dist/extension.js&lt;/code&gt;のなかにその記述が見つかった。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;extension.js&lt;/code&gt;はminifyされているため、Prettierでフォーマットする。検索で見つかった該当箇所は次のようになっていた。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;provideAltTextQuickFix&lt;/span&gt;&lt;span&gt;(e, t) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; e.&lt;/span&gt;&lt;span&gt;lineAt&lt;/span&gt;&lt;span&gt;(t.start.line).text,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    o &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; kbe&lt;/span&gt;&lt;span&gt;(n),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    s &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; kbe&lt;/span&gt;&lt;span&gt;(n, &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;o &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;s)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (o) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isValidUrl&lt;/span&gt;&lt;span&gt;(o),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        c &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; o &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; s$.default.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(s$.default.&lt;/span&gt;&lt;span&gt;dirname&lt;/span&gt;&lt;span&gt;(e.uri.fsPath), o);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        title: Ao.l10n.&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Generate alt text&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        kind: Ao.CodeActionKind.QuickFix,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        resolvedImagePath: c,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        type: &lt;/span&gt;&lt;span&gt;&quot;generate&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        isUrl: a,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        isAI: &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (s) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isValidUrl&lt;/span&gt;&lt;span&gt;(s),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        c &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; a &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; s &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; s$.default.&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;(s$.default.&lt;/span&gt;&lt;span&gt;dirname&lt;/span&gt;&lt;span&gt;(e.uri.fsPath), s);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        title: Ao.l10n.&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Refine alt text&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        kind: Ao.CodeActionKind.QuickFix,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        resolvedImagePath: c,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        type: &lt;/span&gt;&lt;span&gt;&quot;refine&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        isUrl: a,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        isAI: &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この&lt;code&gt;provideAltTextQuickFix&lt;/code&gt;が呼び出されている場所を調べると、次のソースコードが出てくる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;provideCodeActions&lt;/span&gt;&lt;span&gt;(e, t, n) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    !&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.configurationService.&lt;/span&gt;&lt;span&gt;getConfig&lt;/span&gt;&lt;span&gt;(ee.EnableCodeActions) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.ignoreService.&lt;/span&gt;&lt;span&gt;isCopilotIgnored&lt;/span&gt;&lt;span&gt;(e.uri))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; s &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    a &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Ao.window.activeTextEditor;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;a) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; s;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; c &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;provideAltTextQuickFix&lt;/span&gt;&lt;span&gt;(e, t);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    (c &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ((c.command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        title: c.title,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        command: &lt;/span&gt;&lt;span&gt;&quot;github.copilot.chat.generateAltText&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        arguments: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            type: c.type,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            resolvedImagePath: c.resolvedImagePath,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            isUrl: c.isUrl,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      s.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(c)),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.reviewService.&lt;/span&gt;&lt;span&gt;isCodeFeedbackEnabled&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;a.selection.isEmpty)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ) {&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;github.copilot.chat.generateAltText&lt;/code&gt;というコマンドに目をつけて調べると、次のような定義が見つかった。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Ot.commands.&lt;/span&gt;&lt;span&gt;registerCommand&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;github.copilot.chat.generateAltText&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;N&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;N&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    M&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    typeof&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; &quot;object&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;isUrl&quot;&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;resolvedImagePath&quot;&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    typeof&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;span&gt;.resolvedImagePath &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &quot;string&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;type&quot;&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; Y&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &apos;Create an alt text description that is helpful for screen readers and people who are blind or have visual impairment. Never start alt text with &quot;Image of...&quot; or &quot;Picture of...&quot;. Please clearly identify the primary subject or subjects of the image. Describe what the subject is doing, if applicable. Please add a short description of the wider environment. If there is text in the image please transcribe and include it. Please describe the emotional tone of the image, if applicable. Do not use single or double quotes in the alt text.&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      k &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        M&lt;/span&gt;&lt;span&gt;.type &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &quot;generate&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          ?&lt;/span&gt;&lt;span&gt; Y&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          :&lt;/span&gt;&lt;span&gt; `Refine the existing alt text for clarity and usefulness for screen readers. ${&lt;/span&gt;&lt;span&gt;Y&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      D&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; M&lt;/span&gt;&lt;span&gt;.isUrl&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ?&lt;/span&gt;&lt;span&gt; me.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;.resolvedImagePath)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        :&lt;/span&gt;&lt;span&gt; me.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;.resolvedImagePath);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Ot.commands.&lt;/span&gt;&lt;span&gt;executeCommand&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;vscode.editorChat.start&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      message: k,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      attachments: [&lt;/span&gt;&lt;span&gt;D&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      autoSend: &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      initialRange: Ot.window.activeTextEditor?.selection,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;どうやらこの実装では、インラインチャットに対して、先ほどのプロンプトといっしょに画像もコンテキストとして渡しているようだ。&lt;/p&gt;
&lt;p&gt;試しにこのプロンプトだけを別のところにコピペして実行してみたらあまり良い結果にならなかったのだが、それは画像のコンテキストもいっしょに渡していないことが原因だったと分かった。当然と言えば当然だが。&lt;/p&gt;
&lt;p&gt;これらを踏まえて、良い代替テキストを出力するために重要なポイントをまとめよう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代替テキストの要件をプロンプトとして明快に記述する&lt;/li&gt;
&lt;li&gt;画像へのリファレンスを渡す&lt;/li&gt;
&lt;li&gt;同ファイルの記述内容から当該画像のコンテキストを読み取れるようにする&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>aria-label属性は何にでもつけられるわけじゃない</title><link>https://yuheiy.com/2025-05-19-aria-label-and-naming-prohibited</link><guid isPermaLink="true">https://yuheiy.com/2025-05-19-aria-label-and-naming-prohibited</guid><description>ARIA in HTMLでは、要素の名前付けの可否について次のようにある。</description><pubDate>Mon, 19 May 2025 07:15:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/html-aria/&quot;&gt;ARIA in HTML&lt;/a&gt;では、要素の名前付けの可否について次のようにある。&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;著者は、名前付けできない暗黙のWAI-ARIAロールをもつ要素で、&lt;code&gt;aria-label&lt;/code&gt;または&lt;code&gt;aria-labelledby&lt;/code&gt;を指定&lt;em&gt;してはならない&lt;/em&gt;。著者由来の名前付けを禁止する暗黙のWAI-ARIAロールは、&lt;a href=&quot;https://momdo.github.io/html-aria/#docconformance&quot;&gt;&lt;bdi&gt;4. &lt;/bdi&gt; HTMLでARIA属性を使用するための文書適合性要件&lt;/a&gt;で特定されたHTML要素で特定される。&lt;/p&gt;
&lt;p&gt;次のマークアップの例は、著者由来の名前付けを禁止する暗黙のARIAロールをもつHTML要素の選択を示す。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://momdo.github.io/html-aria/#example-elements-with-implicit-aria-roles-which-prohibit-naming-from-authors&quot;&gt;例&lt;bdi&gt;14&lt;/bdi&gt;&lt;/a&gt;：著者由来の名前付けを禁止する暗黙のARIAロールをもつ要素&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- DO NOT do the following! --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; aria-labelledby&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://momdo.github.io/html-aria/#docconformance-naming&quot;&gt;ARIA in HTML 日本語訳 § 4.1
要素に名前を付けるためのARIA属性の使用に関する要件&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;つまり、&lt;code&gt;aria-label&lt;/code&gt;属性や&lt;code&gt;aria-labelledby&lt;/code&gt;属性を適用できる要素の種類は決まっていて、何にでもつけられるわけじゃない。名前付けができないロールは次のように定義されている。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;h2&gt;&lt;bdi&gt;5.2.8.6&lt;/bdi&gt; 名前を付けることができないロール（名前は禁止されている）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#caption&quot;&gt;&lt;code&gt;caption&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#code&quot;&gt;&lt;code&gt;code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#deletion&quot;&gt;&lt;code&gt;deletion&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#emphasis&quot;&gt;&lt;code&gt;emphasis&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#generic&quot;&gt;&lt;code&gt;generic&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#insertion&quot;&gt;&lt;code&gt;insertion&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#paragraph&quot;&gt;&lt;code&gt;paragraph&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#presentation&quot;&gt;&lt;code&gt;presentation&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#strong&quot;&gt;&lt;code&gt;strong&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#subscript&quot;&gt;&lt;code&gt;subscript&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#superscript&quot;&gt;&lt;code&gt;superscript&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#namefromprohibited&quot;&gt;Accessible Rich Internet Applications (WAI-ARIA) 1.2 日本語訳 § 5.2.8.6
名前を付けることができないロール（名前は禁止されている）&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;HTML要素にはそれぞれデフォルトのロール（暗黙のWAI-ARIAロール）が定義されているが、例として挙げられている&lt;code&gt;p&lt;/code&gt;要素、&lt;code&gt;span&lt;/code&gt;要素、&lt;code&gt;code&lt;/code&gt;要素、&lt;code&gt;div&lt;/code&gt;要素のロールはこの&lt;q&gt;名前をつけることができないロール&lt;/q&gt;に該当する。&lt;/p&gt;
&lt;p&gt;逆に、これらに該当しないロールであれば、名前付けをすることは妥当とみなされる。&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;次のマークアップの例は、著者由来の名前付けを可能にする明示的なWAI-ARIAロールを持つ要素を示す。これらの要素で指定される明示的なロールのために、&lt;code&gt;aria-label&lt;/code&gt;および&lt;code&gt;aria-labelledby&lt;/code&gt;属性は許可される。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://momdo.github.io/html-aria/#example-elements-with-explicit-aria-roles-which-allow-naming-from-authors&quot;&gt;例&lt;bdi&gt;15&lt;/bdi&gt;&lt;/a&gt;：著者由来の名前付けを可能にする明示的なARIAロールをもつ要素&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;link&quot;&lt;/span&gt;&lt;span&gt; tabindex&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0&quot;&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt; tabindex&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0&quot;&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;article&quot;&lt;/span&gt;&lt;span&gt; aria-labelledby&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://momdo.github.io/html-aria/#docconformance-naming&quot;&gt;ARIA in HTML 日本語訳 § 4.1
要素に名前を付けるためのARIA属性の使用に関する要件&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;では、名前付けができないロールに対して&lt;code&gt;aria-label&lt;/code&gt;属性を使用することは何が問題か。それは、ブラウザや支援技術において安定して動作しないことである。仕様では&lt;a href=&quot;https://momdo.github.io/wai-aria-1.2/#namecalculation&quot;&gt;&lt;q&gt;要素は著者由来の名前をサポートしない&lt;/q&gt;&lt;/a&gt;と明言されており、基本的にブラウザや支援技術はこれに則って実装される。一部の実装ではこの限りではないが、あくまでそれは例外であり、サポート状況の幅広さや前方互換性の観点で堅牢さに欠ける。&lt;/p&gt;
&lt;p&gt;しかし、そのように使用したい場面が存在することもまた事実である。たとえば、スタイリングの都合により、文字を一文字ずつ分割して&lt;code&gt;span&lt;/code&gt;要素で囲うような場合。このように分割されたノードは、利用環境によっては連続した文字列として正しく読み上げられないことがある。そのための対処として&lt;code&gt;aria-label&lt;/code&gt;属性がよく使用される。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- 次のようにしてはならない！ --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Lorem ipsum&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;L&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;o&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;r&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;e&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;m&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;i&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;p&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;s&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;u&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;m&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この場合の妥当な代替策は、&lt;a href=&quot;https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html&quot;&gt;visually hidden&lt;/a&gt;を使うことだ。次のようにすれば、&lt;code&gt;aria-label&lt;/code&gt;属性を使うことなく代替コンテンツを設定できる。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .visually-hidden&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;absolute&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    clip-path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;inset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    overflow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    white-space&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nowrap&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;visually-hidden&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Lorem ipsum&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;L&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;o&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;r&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;e&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;m&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;i&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;p&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;s&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;u&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;m&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>当エンジニアブログで実施したアクセシビリティ改善の方法についての解説</title><link>https://tech.plaid.co.jp/how-we-improved-the-accessibility-of-our-engineer-blog</link><guid isPermaLink="true">https://tech.plaid.co.jp/how-we-improved-the-accessibility-of-our-engineer-blog</guid><pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate></item><item><title>アクセシビリティ学習の手引きとしての入門講座</title><link>https://tech.plaid.co.jp/introduction-to-accessibility</link><guid isPermaLink="true">https://tech.plaid.co.jp/introduction-to-accessibility</guid><pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate></item><item><title>ブロックリンク（もしくはカードUI）のアクセシビリティ上の問題と解決策</title><link>https://yuheiy.com/2025-04-17-building-better-block-links</link><guid isPermaLink="true">https://yuheiy.com/2025-04-17-building-better-block-links</guid><description>複数のテキスト要素や画像がグループ化されて、そのグループ全体が一つのリンクになっているというUI表現がある。ブロックリンクやカードUIなどと呼ばれるものだ。たとえば次のような実装になる。</description><pubDate>Thu, 17 Apr 2025 02:05:00 GMT</pubDate><content:encoded>&lt;p&gt;複数のテキスト要素や画像がグループ化されて、そのグループ全体が一つのリンクになっているというUI表現がある。ブロックリンクやカードUIなどと呼ばれるものだ。たとえば次のような実装になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;news.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;Budget Debate Continues in Parliament&amp;lt;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;subhead&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alertimg&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alerticon.png&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Breaking News&quot;&lt;/span&gt;&lt;span&gt; height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      Members of Parliament continued vigorous debate on three challenging issues surrounding the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      upcoming year&apos;s budget.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Read more&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これはWCAGの&lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Techniques/html/H30&quot;&gt;Technique H30&lt;/a&gt;にかつて存在した例だが、&lt;a href=&quot;https://github.com/w3c/wcag/pull/2481&quot;&gt;良い例でないとして現在は削除されている&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;ブロックリンクの問題としては、&lt;a href=&quot;https://adrianroselli.com/2020/02/block-links-cards-clickable-regions-etc.html&quot;&gt;リンク選択時の読み上げが冗長になることが指摘されている&lt;/a&gt;。リンクのタイトルだけでなく、説明文などの副次的な情報も合わせて一気に読み上げられてしまうので、最後まで読み終わるのに余計な時間が掛かる。かつスクリーンリーダーによっては、名前の読み上げが終わるまでそれがリンクであることがわからない。&lt;/p&gt;
&lt;p&gt;また、リンクの入れ子の問題もある。もしこのブロックリンクの内側に、別のリンクも含めたいという要件があった場合、このように&lt;code&gt;a&lt;/code&gt;要素で全体を囲うマークアップでは実現が難しい。JavaScriptを使って無理やり実現することもできなくはないが、やはりアクセシビリティ上の問題を孕んでしまうだろう。&lt;/p&gt;
&lt;p&gt;こうした事情を踏まえて、&lt;code&gt;a&lt;/code&gt;要素の疑似要素を使ってターゲット領域を広げる手法が用いられることがある。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: relative&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;news.html&quot;&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;stretched-link&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Budget Debate Continues in Parliament&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;subhead&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alertimg&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alerticon.png&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Breaking News&quot;&lt;/span&gt;&lt;span&gt; height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Members of Parliament continued vigorous debate on three challenging issues surrounding the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    upcoming year&apos;s budget.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colophon&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;By &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;author.html&quot;&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;isolation: isolate&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;John Smith&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.stretched-link::after&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;absolute&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  inset&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://getbootstrap.com/docs/5.3/helpers/stretched-link/&quot;&gt;stretched linkはBootstrapで採用されているものだ&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;これによって、前述の問題は回避できる。しかしこの場合でも、リンクのテキスト選択ができなくなってしまうというまた別の問題が生じる。&lt;/p&gt;
&lt;p&gt;通常、リンクのテキストを選択しようとしても、リンク自体を掴むようなインタラクションが生じてテキスト選択ができないが、&lt;kbd&gt;Alt&lt;/kbd&gt;を押しながらだとテキスト選択ができる機能がある。ところが、この例のように疑似要素で覆い被されているとそれが機能しなくなる。&lt;/p&gt;
&lt;p&gt;そこで、代わりにJavaScriptを使って、リンクの外側がクリックされたときにもリンクが反応するように実装する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;card&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;news.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Budget Debate Continues in Parliament&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;subhead&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alertimg&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alerticon.png&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Breaking News&quot;&lt;/span&gt;&lt;span&gt; height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;30&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Members of Parliament continued vigorous debate on three challenging issues surrounding the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    upcoming year&apos;s budget.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;colophon&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;By &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;author.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;John Smith&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; card&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.card&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; link&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; card.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;h3 a&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  card.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;pointerup&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (event.target.&lt;/span&gt;&lt;span&gt;closest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;a:any-link&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (document.&lt;/span&gt;&lt;span&gt;getSelection&lt;/span&gt;&lt;span&gt;().isCollapsed) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      link.&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;加えて、JavaScriptが利用可能な環境においてのみ要素全体に&lt;code&gt;cursor: pointer&lt;/code&gt;を適用するため、メディアクエリの&lt;code&gt;scripting&lt;/code&gt;を使用する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;scripting&lt;/span&gt;&lt;span&gt;: enabled) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cursor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;pointer&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、ブロックリンクのどこをクリックされても、リンクがクリックされたときと同等の振る舞いがエミュレーションされる。本物のリンクがクリックされたときには、処理が二重に実行されることを防ぐべく、&lt;code&gt;event.target&lt;/code&gt;がリンクであれば早期リターンする。&lt;/p&gt;
&lt;p&gt;そして、ユーザーがテキスト選択しようとしているときにはリンクを反応させず、テキスト選択ができるようにするために、現在の選択状態をもとに処理を分岐する。&lt;code&gt;pointerup&lt;/code&gt;イベントの段階でもしテキストが選択されていれば、何もしない。&lt;/p&gt;
&lt;p&gt;しかしながらこれでは、修飾キーの入力を無視した振る舞いになってしまう。たとえば通常の場合、&lt;kbd&gt;Command&lt;/kbd&gt;を押しながらリンクをクリックすると、リンク先が新しいタブで開く。一方この実装では、その類の機能が無効化されてしまい、常に普通のページ遷移になってしまう。&lt;/p&gt;
&lt;p&gt;修飾キーに紐づく機能を無効化させないためには、修飾キーの入力に応じた処理の分岐を行う必要があるが、ブラウザやOSごとの仕様の違いを網羅するのは容易ではない。したがって、既存のライブラリで解決できると好ましい。&lt;a href=&quot;https://www.npmjs.com/package/@react-aria/utils&quot;&gt;@react-aria/utils&lt;/a&gt;にある&lt;a href=&quot;https://github.com/adobe/react-spectrum/blob/%40react-aria/utils%403.28.2/packages/%40react-aria/utils/src/openLink.tsx#L89-L114&quot;&gt;&lt;code&gt;openLink&lt;/code&gt;関数&lt;/a&gt;がまさにそれだ。これを使って&lt;code&gt;link.click()&lt;/code&gt;を置き換える。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { openLink } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@react-aria/utils&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; card&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.card&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; link&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; card.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;h3 a&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  card.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;pointerup&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (event.target.&lt;/span&gt;&lt;span&gt;closest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;a:any-link&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (document.&lt;/span&gt;&lt;span&gt;getSelection&lt;/span&gt;&lt;span&gt;().isCollapsed) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      openLink&lt;/span&gt;&lt;span&gt;(link, event);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;おそらくこれが、僕が考え得るかぎりで最もマシな実装だと思う。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;このような要件に対応するための&lt;a href=&quot;https://open-ui.org/components/link-area-delegation-explainer/&quot;&gt;Link Area Delegation&lt;/a&gt;という提案もある。将来的には、これを採用することでより適切な実装ができるようになるかもしれない。&lt;/p&gt;
&lt;h2&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://inclusive-components.design/cards/&quot;&gt;Card — Inclusive Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adrianroselli.com/2020/02/block-links-cards-clickable-regions-etc.html&quot;&gt;Block Links, Cards, Clickable Regions, Rows, Etc. — Adrian Roselli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cssnite.doorkeeper.jp/events/163736&quot;&gt;「リンクの中にあるリンク」「ボタンの中にあるボタン」を正しく実装する #朝までマークアップ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>リンクの下線の視覚的ノイズを軽減する</title><link>https://yuheiy.com/2025-03-18-underline-with-less-visual-noise</link><guid isPermaLink="true">https://yuheiy.com/2025-03-18-underline-with-less-visual-noise</guid><description>“Designing dyslexia-friendly navigational components”によれば、text-decoration: underlineによるデフォルトの下線はアクセシブルではないと言う。理由としては大きく二つ。</description><pubDate>Mon, 17 Mar 2025 19:30:00 GMT</pubDate><content:encoded>&lt;p&gt;“&lt;a href=&quot;https://uxdesign.cc/improving-hyperlink-accessibility-and-readability-7f14ba4cb6c2&quot;&gt;Designing dyslexia-friendly navigational components&lt;/a&gt;”によれば、&lt;code&gt;text-decoration: underline&lt;/code&gt;によるデフォルトの下線はアクセシブルではないと言う。理由としては大きく二つ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下線がディセンダーと重なることで文字の判読性や可読性が損なわれる&lt;/li&gt;
&lt;li&gt;コンテンツの邪魔になって読み手の気を散らす&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これは特にディスレクシアなど、認知機能に困難があるユーザーへの影響が大きい。&lt;/p&gt;
&lt;p&gt;加えて、デフォルトの下線は美観としても好ましくないという評価を受けることが多い。視覚的に煩雑な印象を与えるからという、上記と類似する理由によるものだろう。&lt;/p&gt;
&lt;p&gt;件の記事では、そうした問題を踏まえた改善案が紹介されている。これを参考に、このサイトでは次のような実装を採用した。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* 下線と文字の距離を離す */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-underline-offset&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;:any-link&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* UAスタイルシートに含まれているが、説明のために記述 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-decoration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;underline&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* リンクの文字色よりも薄い下線の色。背景と3:1以上のコントラスト比を確保する */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-decoration-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;oklch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.67&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* 下線を細くする */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-decoration-thickness&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;また、文字と下線の距離が離れることに合わせて、&lt;code&gt;line-height&lt;/code&gt;プロパティを少し広めに設定している。&lt;/p&gt;
&lt;p&gt;これにより視覚的なノイズや判読性の問題を軽減できるほか、美観としても好ましくなったように思う。&lt;/p&gt;
&lt;h2&gt;参考情報&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uxdesign.cc/improving-hyperlink-accessibility-and-readability-7f14ba4cb6c2&quot;&gt;Designing dyslexia-friendly navigational components: accessibility insights and atomic design patterns | UX Collective&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://katjag.com/projects/NavigationalComponents.html&quot;&gt;Katja G | Navigational components - accessibility enhancements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://waic.jp/translations/WCAG21/Understanding/non-text-contrast.html&quot;&gt;達成基準 1.4.11: 非テキストのコントラストを理解する&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>特定の要素の外側すべてをinertにする</title><link>https://yuheiy.com/2025-02-24-make-the-outside-inert</link><guid isPermaLink="true">https://yuheiy.com/2025-02-24-make-the-outside-inert</guid><description>モーダルダイアログを実装する場合は、その外側を不活性化する必要がある。ここで言う不活性とは、</description><pubDate>Sun, 23 Feb 2025 19:50:00 GMT</pubDate><content:encoded>&lt;p&gt;モーダルダイアログを実装する場合は、その外側を不活性化する必要がある。ここで言う不活性とは、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要素にクリックやタッチなどで作用（interact）しても反応しない&lt;/li&gt;
&lt;li&gt;フォーカスできない&lt;/li&gt;
&lt;li&gt;支援技術での読み上げ対象からも除外されている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;という状態を指している。&lt;/p&gt;
&lt;p&gt;この要件を満たすには、&lt;code&gt;dialog&lt;/code&gt;要素を&lt;code&gt;.showModal()&lt;/code&gt;で呼び出して使うとよい。それだけで外側の要素は自動的に不活性化される。しかし実装上の制約などにより、&lt;code&gt;dialog&lt;/code&gt;要素を使えないこともあるだろう。そうした場合は、不活性化の処理を独自に実装することになる。&lt;/p&gt;
&lt;p&gt;要素を不活性化するには&lt;code&gt;inert&lt;/code&gt;属性を使う。要素に&lt;code&gt;inert&lt;/code&gt;属性が適用されると、その子孫まで不活性状態が継承される。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button1&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Button 1&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button1&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;I am not inert&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button2&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Button 2&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button2&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;I am inert&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert#html&quot;&gt;HTMLElement: inert property - Web APIs |
MDN&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;そのため、モーダルダイアログ以外のすべての要素が一つの要素の中に囲われていれば、&lt;code&gt;inert&lt;/code&gt;属性を一箇所に適用するだけで済む。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;Outside the dialog&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Button&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;I am inert&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog&quot;&lt;/span&gt;&lt;span&gt; aria-labelledby&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog_label&quot;&lt;/span&gt;&lt;span&gt; aria-modal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;とはいえ、いつもこのような構造を実現できるとはかぎらない。場合によっては、モーダルダイアログが別の要素の中に入れ子になった、もっと複雑な文書構造になることもあるだろう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Button&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;I am not inert&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;... &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://example.com/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;awesome link&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt; ...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog&quot;&lt;/span&gt;&lt;span&gt; aria-labelledby&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog_label&quot;&lt;/span&gt;&lt;span&gt; aria-modal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このような場合、モーダルダイアログを除いた上端にあたる要素すべてに&lt;code&gt;inert&lt;/code&gt;属性を適用しなければならない。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Button&amp;lt;/&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;I am not inert&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; inert&lt;/span&gt;&lt;span&gt;&amp;gt;... &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://example.com/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;awesome link&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt; ...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog&quot;&lt;/span&gt;&lt;span&gt; aria-labelledby&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog_label&quot;&lt;/span&gt;&lt;span&gt; aria-modal&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これを実現するには、次のような処理を実装する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; setInert&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; undos&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  crawlSiblingsUp&lt;/span&gt;&lt;span&gt;(el, (&lt;/span&gt;&lt;span&gt;sibling&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;sibling.inert) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      sibling.inert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      undos.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sibling.inert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    while&lt;/span&gt;&lt;span&gt; (undos.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) undos.&lt;/span&gt;&lt;span&gt;pop&lt;/span&gt;&lt;span&gt;()();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; crawlSiblingsUp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;callback&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (el.&lt;/span&gt;&lt;span&gt;isSameNode&lt;/span&gt;&lt;span&gt;(document.body) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;el.parentNode) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sibling&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; el.parentNode.children) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (sibling.&lt;/span&gt;&lt;span&gt;isSameNode&lt;/span&gt;&lt;span&gt;(el)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      crawlSiblingsUp&lt;/span&gt;&lt;span&gt;(el.parentNode, callback);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      callback&lt;/span&gt;&lt;span&gt;(sibling);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これを次のように使用する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; dialogEl&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;[role=&quot;dialog&quot;]&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; undoInert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; openDialog&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  undoInert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; setInert&lt;/span&gt;&lt;span&gt;(dialogEl);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; closeDialog&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  undoInert&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  undoInert &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;モーダルダイアログを開くタイミングで&lt;code&gt;setInert&lt;/code&gt;関数を実行する。これにより、モーダルダイアログを除くすべての要素の上端に&lt;code&gt;inert&lt;/code&gt;属性が適用される。閉じるタイミングでその戻り値を実行することで、開くときに適用した&lt;code&gt;inert&lt;/code&gt;属性はすべて取り除かれる。なおこの実装は、&lt;a href=&quot;https://alpinejs.dev/plugins/focus&quot;&gt;Alpine.jsのFocusプラグイン&lt;/a&gt;を参考にしたものである。&lt;/p&gt;
&lt;p&gt;モーダルダイアログを実装する際にはそれ以外にも考慮すべき点がある。ここでは取り上げないので、詳しくは&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/&quot;&gt;ARIA Authoring Practices Guide&lt;/a&gt;を参照のこと。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;code&gt;inert&lt;/code&gt;属性を使う以外のやり方としては、特定の要素の中だけにフォーカスを閉じ込めるフォーカス・トラップというテクニックがある。しかし、フォーカス・トラップではドキュメントの外側にフォーカスを移せなくなるので、通常のタブ・ナビゲーションのようにブラウザのアドレスバーなどにフォーカスする操作ができなくなってしまうことが問題である。これについては次の資料でも言及されている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/whatwg/html/issues/8339&quot;&gt;Allow modal dialogs to trap focus, avoiding tabbing to the URL bar · Issue #8339 · whatwg/html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tpgi.com/the-current-state-of-modal-dialog-accessibility/&quot;&gt;The current state of modal dialog accessibility - TPGi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scottohara.me/blog/2019/03/05/open-dialog.html&quot;&gt;Having an open dialog (archival post) | scottohara.me&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;また、フォーカス・トラップを採用するにしても、モーダルダイアログの外側の要素に&lt;code&gt;aria-hidden&lt;/code&gt;属性を適用する処理は依然必要になることがある。WAI-ARIA 1.1で導入された&lt;code&gt;aria-modal&lt;/code&gt;属性をサポートする支援技術では、&lt;code&gt;aria-modal=&quot;true&quot;&lt;/code&gt;が指定された要素より外側は、不活性状態として読み上げ対象から除外される。しかし&lt;code&gt;aria-modal&lt;/code&gt;属性をサポートしない支援技術では、そのままでは外側の要素も通常通り操作できてしまうため、明示的に&lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt;を適用しなければならない。これについては、&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/#:~:text=Notes%20on%20aria%2Dmodal%20and%20aria%2Dhidden&quot;&gt;ARIA Authoring Practices Guideの“Notes on aria-modal and aria-hidden”&lt;/a&gt;で解説されている。&lt;a href=&quot;https://a11ysupport.io/tech/aria/aria-modal_attribute&quot;&gt;&lt;code&gt;aria-modal&lt;/code&gt;属性のサポート状況&lt;/a&gt;はまだ十分ではないため、このようなフォールバック処理は重要だ。&lt;/p&gt;</content:encoded></item><item><title>「段落と段落の間」と「段落と画像の間」の余白を均一にする</title><link>https://yuheiy.com/2025-01-03-space-rect</link><guid isPermaLink="true">https://yuheiy.com/2025-01-03-space-rect</guid><description>垂直方向に複数の段落と画像が並ぶとき、間隔を均一にしたければ、それぞれ同じ大きさのmarginプロパティを適用するだろう。</description><pubDate>Fri, 03 Jan 2025 14:40:00 GMT</pubDate><content:encoded>&lt;p&gt;垂直方向に複数の段落と画像が並ぶとき、間隔を均一にしたければ、それぞれ同じ大きさの&lt;code&gt;margin&lt;/code&gt;プロパティを適用するだろう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-default&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rlh&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-default&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-default&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;しかし実際には、このようにすると段落と画像の間隔がやや詰まって見える。段落同士の間隔に比べて、段落と画像の間隔が少し狭く感じる。&lt;/p&gt;
&lt;p&gt;これはハーフレディングが原因の現象だ。&lt;code&gt;line-height&lt;/code&gt;プロパティの仕様上、テキストの行の上端と下端には余白が設けられる。たとえば&lt;code&gt;font-size&lt;/code&gt;プロパティが&lt;code&gt;16px&lt;/code&gt;で、&lt;code&gt;line-height&lt;/code&gt;プロパティが&lt;code&gt;24px&lt;/code&gt;であれば、上下に&lt;code&gt;4px&lt;/code&gt;の余白ができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;(24px - 16px) / 2 = 4px&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;段落が連続する場合、&lt;code&gt;margin&lt;/code&gt;プロパティの値に加えてこのハーフレディングを加えた分の大きさの余白ができる。前の段落の下端のハーフレディングと、後の段落の上端のハーフレディングが組み合わされるため、ハーフレディングの2倍分の余白が含まれることになる。&lt;code&gt;margin&lt;/code&gt;プロパティが&lt;code&gt;24px&lt;/code&gt;で、ハーフレディングが&lt;code&gt;4px&lt;/code&gt;であれば、大きさは合計で&lt;code&gt;32px&lt;/code&gt;になる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/1.DqnP2Sw2_CpTXm.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1428&quot; height=&quot;1120&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;4px + 24px + 4px = 32px&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一方、段落の直後に画像が続く場合、片方のハーフレディングしか含まれない。したがって、余白の大きさはその分小さい&lt;code&gt;28px&lt;/code&gt;になる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/2.sV76B5hX_1vYq7x.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1428&quot; height=&quot;1120&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;4px + 24px = 28px&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;段落と画像の間隔が詰まって見えるのはこのような理由である。&lt;/p&gt;
&lt;p&gt;この問題を解決するには、画像の周囲にハーフレディングの分を余分に含めた余白を設定するとよい。ハーフレディングの値は&lt;code&gt;(1rlh - 1rem) / 2&lt;/code&gt;で算出できるので、これを通常の&lt;code&gt;margin&lt;/code&gt;プロパティの値と足し合わせる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-rect&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-default&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rlh&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;) / &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これによって、画像の前後にも段落の間と同じ間隔が適用されるようになる。&lt;/p&gt;
&lt;p&gt;便宜上、ここまでは画像を例に解説したが、そのほかの要素においても同様の問題が生じることがある。たとえば、背景色がついていたりボーダーで囲われていたりして、周囲との境界がはっきりしている要素。このサイトで言えば、&lt;code&gt;hr&lt;/code&gt;要素、&lt;code&gt;pre&lt;/code&gt;要素、&lt;code&gt;table&lt;/code&gt;要素などだ。これらについても同様に&lt;code&gt;margin&lt;/code&gt;プロパティを設定する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;hr&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;pre&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-block-end&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、これらの要素が連続して配置されると、やはりほかより狭い間隔になってしまう。&lt;code&gt;--space-rect&lt;/code&gt;には片方のハーフレディングの分の余白しか含まれていないが、これらの要素が連続した際にはハーフレディングの余白は存在しないため、ほかと均一にするには両方のハーフレディングの分の余白を含める必要がある。そこで、要素が連続した際には&lt;code&gt;margin&lt;/code&gt;プロパティを上書きすることで余白を均一にすることができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --space-rect2&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-default&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;rlh&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:is&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hr&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;pre&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp; + &amp;amp; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    margin-block-start&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--space-rect2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;この記事での解説は、&lt;a href=&quot;https://github.com/hail2u/hail2u.net/blob/v9.33.5/static/css/index.css&quot;&gt;Hail2uのCSS&lt;/a&gt;で採用されている手法を参考にしたものである。&lt;/p&gt;</content:encoded></item><item><title>僕のホームページ遍歴</title><link>https://yuheiy.com/2024-12-25-my-history-of-homepages</link><guid isPermaLink="true">https://yuheiy.com/2024-12-25-my-history-of-homepages</guid><description>「個人ホームページ訪問 Advent Calendar 2024」の23日目の記事です。遅刻しました。</description><pubDate>Wed, 25 Dec 2024 01:05:00 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://adventar.org/calendars/10172&quot;&gt;個人ホームページ訪問 Advent Calendar 2024&lt;/a&gt;」の23日目の記事です。遅刻しました。&lt;/p&gt;
&lt;h2&gt;ハムスター島&lt;/h2&gt;
&lt;p&gt;初めてのホームページは、&lt;a href=&quot;https://w.atwiki.jp/legendworld/pages/414.html&quot;&gt;ハムスター島&lt;/a&gt;というサービスを使って作った。2006年ごろだったと思う。ホームページを作ることがブームになってから、少しピークを過ぎたくらいの時期だ。当時は、ウェブ技術についての知識がなくても簡単に自分のホームページを立ち上げられるサービスがいくつもあり、ハムスター島もその一つだった。サーバーの概念もHTMLの書き方も何も知らなかったけど、数クリックするだけで自分のホームページを作ることができた。&lt;/p&gt;
&lt;p&gt;僕はまだ小学生だった。幼少期に父親からパソコンの使い方を教わって以来、ずっとパソコンの前に張り付いているような子供だった。最初の頃は、インターネットに公開されているフリーゲームで遊ぶのがおもな使い方だったが、そのうち自分でも何か作ってみたいと思うようになった。まずはよく遊んでいたFlashゲームを自分でも作れないかと考えたが、子供がいきなり独学するにはあまりにハードルが高く、頓挫した。だから、とにかく何でもいいので自分にも作れるものがないかと探して、そうして見つけたハムスター島が僕にとってのウェブ制作への入り口になった。&lt;/p&gt;
&lt;p&gt;ハムスター島のホームページでできるのは、決まったテンプレートにテキストコンテンツを入力して掲載したり、アクセスカウンターや掲示板を設置することくらいだった。それでも最初は、自分で入力したものがウェブに公開されているというだけで嬉しかった。&lt;/p&gt;
&lt;p&gt;一方、実際に作ってみて気づいたのが、ホームページはそこに掲載するコンテンツがないと成り立たないということだった。というのも、普通は何かを発表したいと思った人がホームページを作るのだろうが、僕の場合は作ること自体が目的だったので載せるものがなかった。絵を描いたり音楽を作ったりできる人はそれを載せていたけど、僕には何もない。もっとも、ホームページブームだったのでそういう人は少なくなかっただろう。だからみんな無益な自己紹介を書いたり100の質問に回答したりしていた。もう思い出したくもないけど、僕は魔法のiらんどで小説を書いていたこともある。今となっては味わい深い気もするが。&lt;/p&gt;
&lt;h2&gt;リヴリーアイランド&lt;/h2&gt;
&lt;p&gt;それから少し経って、&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B4%E3%83%AA%E3%83%BC%E3%82%A2%E3%82%A4%E3%83%A9%E3%83%B3%E3%83%89&quot;&gt;リヴリーアイランド&lt;/a&gt;というオンラインゲームに夢中になった。ユーザー同士のコミュニケーションに重点を置いたゲームで、ギルドのような制度があり、それが当時かなりの盛り上がりを見せていた。人気のあるギルドには例外なく専用のホームページがあり、ギルドの制度や交流イベント、主要メンバーのプロフィールなどが、なかなかの熱量をもって紹介されていた。また、リヴリーアイランドは可愛らしいキャラクターや装飾のデザインがコンテンツとしての目玉であり、ユーザーがそれを二次創作的に素材集サイトにまとめて配布することも一般的になっていた。そのほかにもファンサイトは無数に存在し、充実した攻略コンテンツがあったり、非常に凝ったよくできたデザインになっていたりするものも多かった。&lt;/p&gt;
&lt;p&gt;そうしたコミュニティの影響もあり、ここぞとばかりに僕も自分のギルドのホームページを作った。そのとき使ったのは、WYSIWYGで独自デザインのページを作成できるサービスだった。相変わらず技術的なことはよくわからなかったが、リンクの概念やタグの仕組み、スタイリングのやり方などは、手を動かしているうちに感覚として覚えた。&lt;/p&gt;
&lt;p&gt;（なお、リヴリーアイランドは一度サービス終了した後にリニューアルして再開しているが、当時とはまったく別物になっている。）&lt;/p&gt;
&lt;h2&gt;ネパワ界&lt;/h2&gt;
&lt;p&gt;また少し時間が経って、僕は&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%AF%E3%83%95%E3%83%AB%E3%83%97%E3%83%AD%E9%87%8E%E7%90%83%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA&quot;&gt;パワフルプロ野球シリーズ&lt;/a&gt;（以下、パワプロ）にハマった。やはりこれもファンサイトを通したコミュニティが非常に活発だったゲームである。ある収集家によると、&lt;a href=&quot;https://www.s41poke.com/zukan/top.html&quot;&gt;今までに4300以上のファンサイトの存在が確認されている&lt;/a&gt;と言う。これらの界隈は「ネパワ界」（ネットパワプロ界の略）と呼ばれていた。&lt;/p&gt;
&lt;p&gt;僕はゲームの攻略情報を調べるために、自分がプレイしていたシリーズを扱うあるホームページによくアクセスするようになり、いつからかその掲示板でほかの訪問者たちと交流するのが日課になった。掲示板では攻略情報を交換したり、強い選手のパスワードを共有したり、パワプロの好きなところを語り合ったりした。さらには、各人が運用するファンサイトや、その作り方について話す人もいて、それに影響された僕はまた新たなホームページを作ることになった。&lt;/p&gt;
&lt;p&gt;かれこれするうちに僕はもう自分でHTMLをコーディングするようになっていた。&lt;a href=&quot;https://www.w-frontier.com/software/ezhtml.html&quot;&gt;ez-HTML&lt;/a&gt;を使ってテーブルコーディングで制作したことを覚えている。2008年ごろなのでトレンド的には時代遅れではあるが。&lt;/p&gt;
&lt;p&gt;コンテンツはいくつか作ったけど、特によく覚えているのは、ホームページに設置した&lt;a href=&quot;https://ja.wikipedia.org/wiki/CGI%E3%82%B2%E3%83%BC%E3%83%A0&quot;&gt;CGIゲーム&lt;/a&gt;を改造して遊んでいたこと。そう、当時は自分のホームページにCGIゲームを設置するという文化があったのだ。&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E7%AE%B1%E5%BA%AD%E8%AB%B8%E5%B3%B6&quot;&gt;箱庭諸島&lt;/a&gt;なんかが有名どころだけど、ネパワ界では&lt;a href=&quot;http://osktaka.in.coocan.jp/arekore.html&quot;&gt;劇空間ぱわふるリーグ&lt;/a&gt;（以下、劇ぱわ）というCGIゲームが人気だった。劇ぱわでは、選手ごとに「パワー」「ミート」「走塁」「守備」のようなパタメータに数値を割り振って自分のチームを作る。そして、別のチームとの対戦を実行すると、試合の様子が実況チックなテキストとして1回から最終回まで一度に表示される。試合の中での盛り上がりどころは赤くて大きな文字で表示されたりするけど、それ以外にグラフィカルな要素はなく、見た目としてはちょっと騒がしいテキストサイトくらいの感じだった。それでも昔はそれに熱中する人がたくさんいた。僕はこれを改造して、自分のホームページのトンマナに合った見た目になるようにしたり、パラメータをいじって場外ホームランが出やすいようにしたりと、ちょっとしたアレンジを重ねてオリジナリティを加えていった。技術的に難しいことではなかったが、そうした細かい工夫が楽しかった。結果、数十人くらいの人に登録して遊んでもらうことができた。&lt;/p&gt;
&lt;p&gt;ところでCGIゲームを設置するには、それに対応したサーバーを用意する必要があった。無料のレンタルサーバーの中にもCGI/Perlに対応したものはいくつかあったが、無料の代わりにバナー広告が挿入されたりするのがうっとうしくて、あるとき思い切って有料のレンタルサーバーを契約した。当時中学生なのでほとんどお金がなく、両親の理解を得られそうもないので、溜め込んだお年玉を持ってこっそりコンビニに行って支払いをしたことを覚えている。さくらのレンタルサーバだった。後日、自宅にその郵便が届いたせいで母親に怒られた。&lt;/p&gt;
&lt;h2&gt;ライブドアブログ&lt;/h2&gt;
&lt;p&gt;前述のホームページ作りと並行して、いくつかのブログも運用していた。初めて立ち上げたブログは、使っていたサービスはもう名前も思い出せないが、ハムスター島と同じく素人でも簡単に扱えるようなものだった。いま思えばあのような、技術への入り口となるようなサービスは偉大だったと思う。周囲の誰かに教えてもらえるわけでもなく、かといって自分で調べる技術もそれほど身についていなかった時期に、偶然自分のレベルに合ったサービスに巡り会えたことは運がよかった。&lt;/p&gt;
&lt;p&gt;そんなブログの内容は、たいしたことのない、ただの子供の痛々しい雑記でしかなかった。インターネットを使って何か生産的なことをしてみたいという気持ちだけはあったけど、何の技術もない僕にかろうじてできたのは、テキストを書いて公開することくらいだった。幸い、書くことは好きだった。好きになった。&lt;/p&gt;
&lt;p&gt;ブログを書く傍らで、テーマをカスタマイズすることも楽しくてのめり込んでいった。ブログのサービスによってこの辺りの作りに違いがあることが気になって、主要なレンタルブログサービスに片っ端から登録して、いろんなブログを作ってカスタマイズして遊ぶようになった。そして、それぞれのブログサービスを比較するために、個別にいろんなネタを決めて運用していた。普段の生活の日記、当時作っていたホームページの運用日誌、2ちゃんねるのまとめブログ、気になったニュース記事をピックアップしてコメントとともに紹介するニュースまとめブログ、などなど。ちなみにその頃の日課は、RSSリーダーに登録した大量のニュースやブログにひと通り目を通して消化することだった。そういう経緯もあって、普段から注意深くブログにできるネタがないかを探す癖がついた。&lt;/p&gt;
&lt;p&gt;やがていくつかのレンタルブログサービスを経て、最終的にはライブドアブログに行き着いた。ライブドアブログがほかと一線を画すのは、テーマのカスタマイズ性の高さだった。多くのレンタルブログサービスでは、HTMLテンプレートは決まった構造から変更ができず、できるのは独自のCSSを適用するくらいのことだったが、ライブドアブログでは独自のHTMLテンプレートを作成することができた。XMLのようなライブドアブログ独自の記法でイテレーションやパーシャルの出力が表現できるようになっていて、それがわかりやすいドキュメントで解説されていた。当時の僕にはプログラミングの技術はなかったが、おかげでHTMLコーディングの延長としてすんなりテーマ作成の考え方を理解できた。これを機に僕はブログテーマ制作に夢中になって、以来いくつもの独自テーマを作ることになった。パソコンの前を離れてもずっとそのことを考えていて、学校でも紙のノートにワイヤーフレームを書いたりCSSを書いたりしていた。&lt;/p&gt;
&lt;p&gt;（いま調べたら、ライブドアブログのデザインのカスタマイズについてのドキュメントがなくなっていて、「サポート対応の範囲外」ということになっていた。悲しい。）&lt;/p&gt;
&lt;p&gt;テーマ作りに力を入れるにつれて、デザインとコンテンツの関係、つまり側（がわ）と中身の卵鶏問題について強く意識せざるを得なくなってきた。ブログを書くのはそれなりに好きだったが、それだけでなく、テーマのデザインを成り立たせるために書かなければならない、言うなれば鞄のあんこ（詰め物）のようなものとしても捉えていた。思えばこの頃から、ブログを書くこと自体は目的ではなく、ブログテーマを作るための手段としてブログを書いているという感覚がずっとある。&lt;/p&gt;
&lt;p&gt;しかし、そうして病的な熱量でパソコンに張り付いている日々が続いて、ある日、遂に母親にパソコンを取り上げられてしまった。学校の成績が著しく悪化したせいだ。中二の頃だった。僕のホームページ道はそのとき一度終わった。&lt;/p&gt;
&lt;h2&gt;ポートフォリオサイト&lt;/h2&gt;
&lt;p&gt;高校を卒業してから、僕はウェブデザインの専門学校に進学した。専門学校に行けば一日中パソコンを触っていられると聞いたからだ。&lt;/p&gt;
&lt;p&gt;専門学校では、基本的なコーディングやソフトウェアの操作方法についての授業のほか、ポートフォリオの制作が主なウェイトを占めていた。支給されたコンテンツに応じてデザインとコーディングをしたり、何かしらの企画を立ててそれに基づいたウェブサイトを作ったりして、作品の数を増やしていた。&lt;/p&gt;
&lt;p&gt;しかし、残念ながらその頃の僕はウェブサイトの企画を立てるのが苦手で、あまり気が乗らなかった。それよりも当時は、ウェブデザインのギャラリーサイトを眺めてスクラップしたり真似したりすることのほうが好きだった。こういうかっこいいデザインや面白いモーションを使って、それありきで好きなようにウェブサイトを作るにはどうすればいいだろうと考えた結果、やがて自分のポートフォリオサイトを作ることに力を入れ始めた。まあ言ってみれば、クリエイターにとってポートフォリオサイトというのは、好きなようにやっていい場所なのである。そんなわけで、ポートフォリオサイトに載せられる作品はほとんどないのに、ポートフォリオサイトばかりを作るようになった。在学中に作ったポートフォリオサイトは10個くらいあって、新しいポートフォリオサイトには作品集として古いポートフォリオサイトをいくつも並べていた。&lt;/p&gt;
&lt;p&gt;そう言えば、この時期に出会った&lt;a href=&quot;https://hail2u.net/&quot;&gt;Hail2u&lt;/a&gt;にはかなりの影響を受けたと思う。技術的な側面よりも、ウェブデザインのスタイルとしてミニマリズムを体現しているのが新鮮に感じて、以来ずっと頭の片隅にあった。在学中に作ったポートフォリオサイトでも、最初は派手な装いを好んでいたけど、バージョンを重ねるにつれてシンプルになっていった。それがきっかけだったのかはわからないけど、デザインだけにかぎらず、技術的にもオーバーエンジニアリングを避ける方向に進んだ。当時作った、WordPressのREST APIを使ったシングルページアプリケーションや、発表されたばかりのReactのサーバーサイドレンダリング機能を取り入れたウェブサイトは、トレンドを取り入れるタイミングとしてはかなり早くて、技術的にはけっこうおもしろかったと思う。けど、それを使って作るもののしょぼさを思うと、オーバースペックなものはイケてないと考えるようになっていった。結局、最後のバージョンのポートフォリオサイトは手書きのHTMLで作った。そのこだわりをプレゼンする技術はなかったので、就活の戦略としては失敗した。&lt;/p&gt;
&lt;h2&gt;モダンフロントエンド&lt;/h2&gt;
&lt;p&gt;専門学校の卒業後には、ウェブサイト制作の仕事を始めた。いわゆるモダンフロントエンドの概念が勢力を増してきた時代だった。&lt;/p&gt;
&lt;p&gt;どの技術を使ってウェブサイトを開発するかという判断が非常に難しかった。タスクランナーにgulp.jsを使って、SassとBabelでコンパイルする、くらいの共通認識はあったけど、ちょうどいいサイトジェネレーターが見つからなかったので、自分で作ったものを案件ごとに調整したりして使っていた。開発環境構築をする担当者によってビルドの仕組みやディレクトリ構成がまるで違うのでいろいろ苦労していた。&lt;/p&gt;
&lt;p&gt;そうこうしているうちに、Next.jsやNuxtが台頭してきた。最初はそれらはウェブアプリケーションのための技術だと考えられていたが、ウェブサイト制作にも使えるという論調が強まってきた。僕としては、一時はその流れに賛同していたが、やはりこれは何か間違っていると違和感を抱くようになった。この手のフレームワークは、ウェブサイト制作に流用することもできるというだけであって、そのために作られたものではない。ウェブサイト制作に必要なのは、JavaScriptフレームワークではない。ウェブサイト制作の開発環境の問題は、細かなツール群を寄せ集めて組み合わせるという責務をユーザーが担わされれていることであって、必要なのはウェブサイト制作のためにチューニングされたワンストップソリューションなのだ。&lt;/p&gt;
&lt;p&gt;しかしその夢のようなワンストップソリューションは長らく登場せず、その間、&lt;a href=&quot;https://github.com/yuheiy/shifted&quot;&gt;僕はさまざまなツールを組み合わせたボイラープレート&lt;/a&gt;を、一番マシな開発環境として同僚たちに共有することで開発の一貫性を維持していた。でもこのやり方は、技術的にはあまり美しくない。スケールしづらいし、開発手法として広いコミュニティの中で一般化させることは難しい。それが不満だった。&lt;/p&gt;
&lt;p&gt;そんなとき、突如として&lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;が登場した。初めてAstroが世に出たタイミングでの&lt;a href=&quot;https://css-tricks.com/astro/&quot;&gt;CSS-Tricksの紹介記事&lt;/a&gt;を見たとき、ずっと求めていたものがついにと電撃が走ったようだった。Astroの前は僕は&lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;を使っていて、これもサイトジェネレーターとしては悪くないのだが、あくまでプレーンなHTMLを生成することだけを責務としていて、その周辺ツールとの接続方法はユーザーに委ねられている。そして誤解を恐れずに言えば、古臭い。モダンなフロントエンド技術との相互運用性を考慮した作りになっていない。一方、Astroは従来的なサイトジェネレーターではなく、必要なもの全部入りのワンストップソリューションであることが画期的なのである。そして世界に平穏が訪れた。&lt;/p&gt;
&lt;h2&gt;ベースライン&lt;/h2&gt;
&lt;p&gt;仕事を始めてからも、僕はずっとこの自分のウェブサイト、いや、ホームページを更新して維持し続けている。専門学生時代と同じく、たまに全部作り直したりしてきていて、それなりのバージョン数になっている。&lt;/p&gt;
&lt;p&gt;けど今のバージョンになってからは、しばらく大きな作り直しをしていない。今のバージョンを作るときに、ずっと古くならないように作ろうと考えたからだ。これまでは「気分で作り直せばいいや」くらいのノリだったが、今回はもう最終版だと思って作った。後ろ向きな理由としては、もう頻繁に作り直すのも疲れるからやりたくないというのがあるけど、前向きな理由としては、これからのブログのベースラインになるような模範的なものを作りたいという想いがある。&lt;/p&gt;
&lt;p&gt;僕は単にテキストを書くことも好きで、うまく書けるようになるための努力もしてきたが、それ以上に、そのテキストのレイアウトの仕方を考えることが好きだ。僕は、テキストのデザインについてはそれなりの関心がある。昨今、これだけテキストばかりのホームページというのも珍しいが、だからこそ、テキストを中心に据えた環境における理想を追求したいと思う。&lt;/p&gt;
&lt;p&gt;ブログは「テーマのデザインを成り立たせるために書かなければならない」ものだと前述したが、翻って、今だと別の解釈ができるかもしれない。ブログを書くことで、そのテーマにコンテンツが適用されている様を見て楽しむことができる。テーマを楽しむためにブログを書く。それが楽しいから書く。僕は依然として、より良いブログテーマを作るためにブログを書いている感覚でいる。けれど。よいレイアウトで文字を並べられるようになっているかどうか、血の通ったテキストを書くことでそれを確かめられる。逆に、テキストを書くことで、心地よく文字が並んでいることが感じられる。書き手が自らレイアウトを調整できると、そういう嬉しさに通ずることができる。&lt;/p&gt;
&lt;p&gt;僕は自分のホームページを持つことにささやかな誇りがあるが、同業者の中ではそういうことに興味を持つ人が減ってきているようだ。時代の流れだろうか。もしかすると、自分のホームページを所持することに憧れを持つことができるのは、僕くらいの世代が最後なのかもしれない。そう思うと寂しいが、それでも僕は、自分自身の喜びのためにこれを続けていくだろう。&lt;/p&gt;</content:encoded></item><item><title>line-clampとhanging-punctuationを併用するときはoverflow:hiddenの代わりにclipを使う</title><link>https://yuheiy.com/2024-12-06-overflow-clip-for-line-clamp</link><guid isPermaLink="true">https://yuheiy.com/2024-12-06-overflow-clip-for-line-clamp</guid><description>CSSの-webkit-line-clampプロパティを使うとき、通常はoverflow: hiddenを併用することが多い。</description><pubDate>Thu, 05 Dec 2024 15:40:00 GMT</pubDate><content:encoded>&lt;p&gt;CSSの&lt;code&gt;-webkit-line-clamp&lt;/code&gt;プロパティを使うとき、通常は&lt;code&gt;overflow: hidden&lt;/code&gt;を併用することが多い。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/1.DOTKejZu_Z1IqTx3.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;198&quot; height=&quot;176&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-webkit-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-box-orient&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vertical&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-line-clamp&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  overflow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、&lt;code&gt;hanging-punctuation&lt;/code&gt;プロパティを併用する場合、&lt;code&gt;overflow: hidden&lt;/code&gt;が指定されているとはみ出した役物が見切れてしまう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/2.C00-Ljej_wJDSK.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;198&quot; height=&quot;176&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-webkit-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-box-orient&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vertical&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-line-clamp&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  overflow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  hanging-punctuation&lt;/span&gt;&lt;span&gt;: allow-end;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;-webkit-line-clamp&lt;/code&gt;プロパティを機能させるには、本来は&lt;code&gt;overflow-y&lt;/code&gt;プロパティを使うだけで十分だ。ただし&lt;code&gt;overflow-y: hidden&lt;/code&gt;を指定すると、その副作用として横方向にスクロール可能な状態になってしまう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/3.CfUBkbV1_Z1gbeKw.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;178&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-webkit-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-box-orient&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vertical&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-line-clamp&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  overflow-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  hanging-punctuation&lt;/span&gt;&lt;span&gt;: allow-end;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この問題を解決するのが、&lt;code&gt;clip&lt;/code&gt;である。&lt;code&gt;clip&lt;/code&gt;を使うと、単方向の切り取りを実現しつつ、もう一方はスクロール不能な状態のままにできる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/4.CAHxRIwm_Z2pFh0T.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;198&quot; height=&quot;176&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;-webkit-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-box-orient&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vertical&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -webkit-line-clamp&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  overflow-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;clip&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  hanging-punctuation&lt;/span&gt;&lt;span&gt;: allow-end;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;hanging-punctuation&lt;/code&gt;プロパティにかぎらず、横方向にコンテンツのはみ出しが発生する場面においては有効だろう。&lt;/p&gt;</content:encoded></item><item><title>なぜ僕はデザイナーの作ったデザインを「見づらい」と感じてしまうのか</title><link>https://yuheiy.com/2024-12-01-i-am-not-a-visual-thinker</link><guid isPermaLink="true">https://yuheiy.com/2024-12-01-i-am-not-a-visual-thinker</guid><description>「PLAID Design Advent Calendar 2024」の1日目の記事です。</description><pubDate>Sun, 01 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://adventar.org/calendars/10389&quot;&gt;PLAID Design Advent Calendar 2024&lt;/a&gt;」の1日目の記事です。&lt;/p&gt;
&lt;p&gt;僕はこれまでフロントエンドの開発者として働いてきた傍ら、自身をウェブデザイナーとしても自認して、自分なりのやり方でデザインに携わってきました。この記事は、特定の組織での経験をテーマにしたものではなく、そうした自分の成り立ちを省みるためのものです。&lt;/p&gt;
&lt;p&gt;また記事は、書籍『ビジュアル・シンカーの脳』の内容をもとにした自分の感想や思索についてまとめたものです。特定の属性の人物やその主義主張に優劣をつける意図はありません。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;デザイナーが作ったデザインを見たとき、「見づらい」や「わかりにくい」といった印象を抱くことが僕にはそれなりにある。おおまかに言って、そう感じるのには大きく2通りの原因がある。&lt;/p&gt;
&lt;p&gt;一つは、視覚的にボヤッとして見づらいとき。主要な文字のサイズが小さすぎたり色が薄すぎたり、周辺空間が騒々しくて目的の情報をすんなり読み取れなかったり、何かしらの機能を示す装飾がさりげなすぎて気づけないようなとき。あるいは、情報のヒエラルキーやグループ関係を読み取れないとき。&lt;/p&gt;
&lt;p&gt;もう一つは、UI表現として自分が認識しているパターンを裏切られたとき。「こういうUI要素はこのようなコンテキストの中でこのような挙動をするだろう」という自分の脳内にあるモデルを逸脱するような設計が想定されているとき。&lt;/p&gt;
&lt;p&gt;そうした自分の見解について話すと、まれに「いや、わかるでしょ」とでも言わんばかりの反応が返ってくることがある。つまりデザイナー本人からしてみれば、十分見やすくわかりやすく作っているつもりなので、それに難癖をつけられる道理が理解できないというわけだ。僕としては本当に「わからない」のだが、さも何かの意地悪で極論を言っているかのように捉えられるのである。&lt;/p&gt;
&lt;p&gt;僕はこれまで、それは自分の作ったものを見慣れすぎているという作り手のバイアスが原因の問題だと考えていた。もっとも、それも問題の一因ではあるだろう。しかし、僕が明らかに見づらくわかりづらいと思うものに対して、初見の人が真逆の感想を述べる光景も数多く目にしてきた。&lt;/p&gt;
&lt;p&gt;この感覚の違いの正体はいったい何なのか。誰かが嘘をついているのか。自分の心を正しく解釈できていないだけなのか。それとも彼らは、自分とはまったく違うレンズで世界を見ているのか。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;最近、『&lt;a href=&quot;https://www.nhk-book.co.jp/detail/000000819422023.html&quot;&gt;ビジュアル・シンカーの脳&lt;/a&gt;』という本を読んだ。そこには、まさに先のような問いに対する答えがあった。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;なお以降の項での記述は、最後を除いてすべて本書の要約と抜粋から構成されていることを断っておく。僕自身の見解ではなく、本書の内容をもとにして編集されたものである。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;いわく、ある種の人々は、言葉の代わりに「絵」を使って思考する。一般に、言語は思考の土台であると考えられている。しかし一部の人々は、情報処理のプロセスとして脳の視覚の回路を使って思考する。つまり、通常の言語思考とは異なる考え方をするのである。本書ではそれを視覚思考者（ビジュアル・シンカー）と呼ぶ。&lt;/p&gt;
&lt;p&gt;視覚思考者は、頭の中で言語を経由せずに直接イメージを思い浮かべる。Googleで画像検索をするように、視覚的なイメージを高速で連想する。地図や絵画、迷路が好きで、道には迷わない。一方で、子供のころに言葉を話し始めるのが遅く、学校での教え方についていくには苦労する傾向がある。視覚化できない抽象概念を理解することも苦手である。&lt;/p&gt;
&lt;p&gt;その対である言語思考者は、物事を順序立てて理解するので、学校での体系化された勉強は得意な傾向にある。一般的な概念を理解するのが得意で、時間感覚に優れているが、方向感覚は良いとは言えない。書類やファイルはきちんと整理整頓する性分である。何かしらの問題に向き合う際には、講じる対策を明確にして、解決や決定にたどり着く。声に出さずに自分の心に語りかける。口が達者で、言葉を巧みに使いこなす能力が必要な職業に就くことが多い。&lt;/p&gt;
&lt;p&gt;この二者の違いを端的に理解できる一つの研究がある。ある心理学者が、書類をファイルに閉じてキャビネットにきちんと並べる人と、書類の山に囲まれている人との比較を行った。すると、書類をファイルしない人にきちんと整理させたら、何も見つけられなくなってしまったと言う。なぜか。彼らは、整理しなくても、散らかった状態のままでそれらを組織化し、頭の中で見ているからだ。無理に整理すると、構築したイメージが損なわれてしまう。これが視覚思考である。&lt;/p&gt;
&lt;p&gt;本書の著者テンプル・グランディンは動物学博士だ。彼女は自閉スペクトラム症であるとともに、視覚思考者である。本書は、その視覚思考者の考え方というものが存在することを解明して世に知らしめるものなのだ。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;人が視覚思考タイプかどうかを判定するテストはいくつかある。著者が考案した「イケア・テスト」では、家具を買ってきて組み立てると仮定する。そのとき自分なら、説明書の文を読むかイラストを見るかを想像する。視覚思考者の著者にとっては、言葉で書かれた説明文を読んでも連続した手順についていけずちんぷんかんぷんだが、イラストを見れば一目瞭然だと言う。イケアでは説明書はすべてイラストで示されているが、これは創業者がディスレクシアであり言葉より絵を優先するタイプだからだそうだ。&lt;/p&gt;
&lt;p&gt;また別の「視覚空間型思考判定テスト」では、18の質問に「はい」か「いいえ」で答える。「はい」が10個以上なら視覚思考タイプの可能性がかなり高いと言う。著述家や編集者、弁護士はたいてい「はい」が10よりずっと少なく、想像力が極めて高い人や数学が好きな人には「はい」が多い。たいていの人はその間のどこかに当てはまり、2種類の思考の混合タイプになるだろう。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;考えるときには、言葉ではなく、おもに絵を使う。&lt;/li&gt;
&lt;li&gt;物事がわかるが、その方法や理由は説明できない。&lt;/li&gt;
&lt;li&gt;ふつうと違う方法で問題を解決する。&lt;/li&gt;
&lt;li&gt;物事をありありと想像する。&lt;/li&gt;
&lt;li&gt;目で見たことはおぼえているけれど、耳で聞いたことは忘れる。&lt;/li&gt;
&lt;li&gt;単語をつづるのが苦手。&lt;/li&gt;
&lt;li&gt;物体をいろいろな視点から思い浮かべることができる。&lt;/li&gt;
&lt;li&gt;整理整頓が苦手。&lt;/li&gt;
&lt;li&gt;時間の経過がわからなくなることがよくある。&lt;/li&gt;
&lt;li&gt;行く先は言葉で説明してもらうより、地図を見るほうがわかる。&lt;/li&gt;
&lt;li&gt;一度しか行ったことのない場所でも道順をおぼえている。&lt;/li&gt;
&lt;li&gt;字を書くのが遅く、字はほかの人に読みづらい。&lt;/li&gt;
&lt;li&gt;ほかの人の気持ちがわかる。&lt;/li&gt;
&lt;li&gt;音楽か美術か機械が得意。&lt;/li&gt;
&lt;li&gt;周囲が思っている以上に物知り。&lt;/li&gt;
&lt;li&gt;人前で話すのは苦手。&lt;/li&gt;
&lt;li&gt;歳を重ねるごとに賢くなっていると思う。&lt;/li&gt;
&lt;li&gt;コンピューターに熱中する。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;視覚思考者の割合については、まだ十分なデータがない。小学生を対象に行った調査によれば、ほぼ3分の1が明確な視覚思考タイプで、およそ4分の1が明確な言語思考タイプ、残りの半分弱が混合タイプだった。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;視覚思考者には、2種類のタイプがいる。物体視覚思考者と空間視覚思考者だ。それぞれ次のような特徴がある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物体視覚思考者: 写真のように正確なイメージで周りの世界を見る
&lt;ul&gt;
&lt;li&gt;グラフィックデザイナーや画家、目端の効く商人、建築家、発明家、機械工学士、設計士などに多い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;空間視覚思考者: パターンと抽象的な概念で周りの世界を見ている
&lt;ul&gt;
&lt;li&gt;音楽や数学が得意&lt;/li&gt;
&lt;li&gt;統計学者、科学者、電気技師、物理学者、コンピュータープログラマーなどに多い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;プログラマーに空間視覚思考者が多いのは、コードにパターンが見えるからだそう。この二者の思考を区別すれば、「物体視覚思考者はコンピューターを組み立て、空間視覚思考者はプログラムを作成する」という風になる。&lt;/p&gt;
&lt;p&gt;はたまた、極端な言語思考者の中には、写真や略図を見てもどう解釈すればいいのかがさっぱりわからない人がいる。物体視覚思考者や空間視覚思考者にとっては自明のテストをしたとき、言語思考者はまるででたらめのような回答をしたと言う。ほかにも、次のような事例が紹介されている。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;ある研究では、視覚思考者と言語思考者に説明文と写真を見せて、新しいことを学ぶテストをした。視線を追跡すると、当然ながら、視覚思考者は写真に、言語思考者は説明文に注目した。言語思考者が写真を見たときには、写真の縁など新しい情報を得るのに何の役にも立たないところを見ることが多かった。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;また別の研究では、美術、理科、語学のどれか一つが得意な学生をグループにして、それぞれに未知の惑星の絵を描かせた。研究の目的を知らされていない専門家が作品を評価したところ、結果は次のようになった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;美術の得意な生徒（物体視覚思考タイプ）
&lt;ul&gt;
&lt;li&gt;色鮮やかでファンタスティックな3つの惑星を描いた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;理科の得意な生徒（空間視覚思考タイプ）
&lt;ul&gt;
&lt;li&gt;自分たちの描く惑星に明確なコンセプトを持っていた&lt;/li&gt;
&lt;li&gt;惑星は球体で色がなく、どちらかというとよくあるタイプだった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;語学の得意な生徒（言語思考タイプ）
&lt;ul&gt;
&lt;li&gt;描いた惑星は想像力に欠け、点描の抽象画のように見えた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そして研究をさらに進めて、生徒たちが惑星を描くときにどのようにアイデアを展開したかを調べた。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;美術と理科の生徒はどちらも最初に「中心になる独創的なアイデア」を展開した。美術の生徒は、惑星の外観を話し合い、理科の生徒は、惑星の重力や化学的性質、生物の種類など構成要素を話し合った。語学の生徒は描いた惑星に名前をつけたが、制作にあたって練った構想を説明できなかった。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;この3種類の考え方は、本書にある3つの思考スタイル（物体視覚思考、空間視覚思考、言語思考）と一致する。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;我々の業界におけるデザイナーとエンジニアの関係を理解するうえで、本書の「建築家と建築エンジニア」の話は興味深かった。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;通常、建築家は注目され、大胆な設計だとか、美しく調和が取れているなどと称賛される。設計に命を吹き込み、その結果できあがった建物を安全に使えるようにするのは、建築エンジニアの領分だ。私の経験と観察からすると、建築家はたいてい物体視覚思考タイプで、建物の姿を頭の中で見るが、建築エンジニアはたいてい空間視覚思考タイプで、数学の得意な脳で電気系統を操作し、建物にかかる風圧や雪の重さを計算する。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;近代高層ビルの生みの親ウィリアム・ル・バロン・ジェニーは、建築家と建築エンジニアの二刀流だった。1884年に完成させたホーム・インシュアランス・ビルは、当時の米国内でもっとも高く、内部フレームにレンガと石ではなく鉄と鉄鋼が初めて使われた。「12世紀にゴシック様式が大聖堂に取り入られれて以来のもっとも大きな革新だ」と語られている。&lt;/p&gt;
&lt;p&gt;しかし物体視覚思考者の著者にとっては、このビルはいかにも建築エンジニアが設計したように見えると言う。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;機能一点張りの背の高い長方形で、ちっとも美しくない。察するに、ジェニーは建築家ではあったが、おもに数学に強い空間視覚思考タイプで、最大の関心は、崩れ落ちない鋼鉄製のフレームで建築することだったのだろう。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;この建築家と建築エンジニアの考え方の相違は、学校での建築とエンジニアリングの教え方の違いに象徴されている。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;教室という物理的な空間を見ても、エンジニアリングの教室では、無味乾燥な部屋に机が整然と並べられている。建築の教室では、学生が大きな作業台のあちこちでそれぞれに作業し、壁には絵画や下絵が貼られ、教室というより画家のアトリエに見える。エンジニアリングのカリキュラムは「がんじがらめ」で、一度に一つずつ技術的なスキルの問題に取り組む。一方、建築のカリキュラムは制約が少なく、創造性を重視する。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;工業デザインの分野でも似たような話がある。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;工業デザインの企画では、美術と作図に重点が置かれ、数学はそれほど重視されない。工業デザイナーは、製品が作動する仕組みや外観の構想を練る。一方、機械エンジニアは、負荷テストや物理的な力という数字に関わる面に目を向けて製品の機能を計算する。工業デザイナーは設計し、機械エンジニアはそれを機能させる。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;機械エンジニアと工業デザイナーが周りの世界をどのように見ているのかを調査した研究がある。被験者はいろいろな種類の椅子の写真を見て、機能性、創造性、美しさを5段階で評価する。&lt;/p&gt;
&lt;p&gt;調査の結果、機械エンジニアにとっては、外観と性能は密接に関係しているようだった。それぞれの椅子の機能性を美しさと一緒くたに評価する傾向があった。ところが工業デザイナーは、美しさと機能性を分けて考えた。つまり、エンジニアにとって形と機能を切り離すことは難しいが、デザイナーは美しさと機能性を上手に区別した。&lt;/p&gt;
&lt;p&gt;この研究から、美しさと機能性は見る人によって大きく評価が異なることが明らかになった。さらに言えば、機械エンジニアは空間視覚思考タイプで、工業デザイナーは物体視覚思考タイプだと推測できる。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;本書からのこれらの情報を踏まえたとき、僕の経験の中には思い当たることがいくつもある。一つひとつ紐解いていこう。&lt;/p&gt;
&lt;p&gt;まず、僕は言語思考者としての傾向が強い。日頃ずっと言葉が頭の中を渦巻いていて、無意識に心でしゃべっている。学生時代の得意教科は国語と英語だった。プログラマーとしてコードにパターンが見えるという感覚はわかるので、空間視覚思考者でもあるのかもしれないが、言語思考ほど強い傾向は見られない。&lt;/p&gt;
&lt;p&gt;視覚的な情報を読み取ることが苦手だという心当たりはある。子供のころから母には「物をよく見ていないでぼうっとしている」と言われてきた。酷い方向音痴でもあり、道を覚えるのはめっぽう苦手だ。地図を見ても位置関係を理解するまでしばらくかかる。ついでに言えば、人が良いと言う絵や写真を見てもいまいちピンとこない。&lt;/p&gt;
&lt;p&gt;両親の思考タイプはどうだったか。母はたぶん言語思考と物体視覚思考の混合タイプで、よくしゃべるが言葉に厳しく、僕がおかしな日本語を使ったり要領を得ない話をしたりするたびに目ざとく非難した。絵を描くのが得意で製図の仕事をしていた。父は空間視覚思考者で、経理の仕事をしていた。家ではいつもパソコンの前にいて、父とはまともな会話をした記憶がない。母が僕に施した美術的教育は実を結ばなかったが、シビアな言語感覚は受け継がれた。&lt;/p&gt;
&lt;p&gt;これまで僕が仕事をしてきたデザイナーの中に物体視覚思考者が多かったことは想像に難くない。その物体視覚思考者が作り出すものが、言語思考者にとって易しくないものであることは、ごく自然な帰結に思える。要するに、受け取りやすい情報の形式が大きく違うのだろう。その感覚に相手との齟齬があるという意識がないから、話がすれ違う。&lt;/p&gt;
&lt;p&gt;では、UI表現のパターンについて、僕が人一倍厳密に考えてしまうのはなぜだろう。これは空間視覚思考者としての傾向が働いているせいだと解釈できないか。普段から物事を抽象的に捉える癖があり、目に見えるものもパターンで認識しようとする強い意識がある。だからこそ、細微な違いにつまづいて意味を取り違えてしまうことも多い。それがある意味、僕の融通の効かなさを形成している。&lt;/p&gt;
&lt;p&gt;そして僕の感覚とは裏腹に、ある種のデザイナーは、デザインの問題を指摘するときによく「文字文字しい」という表現を用いる。文字の量が多すぎたりその存在感が強すぎたりするという意味だ。だから、そのテキスト部分を無くしたり減らしたり隠したりしたいという話につながる。これは程度問題なのでどちらが正しいと言い切ることはできないが、そのバランス感覚の違いに言語思考者と視覚思考者の隔たりが表れていると思う。&lt;/p&gt;
&lt;p&gt;僕が業界を志したころ、ウェブデザインの心得として強く影響を受けたのが「&lt;a href=&quot;https://justinjackson.ca/words.html&quot;&gt;Words&lt;/a&gt;」である（もしくは、「&lt;a href=&quot;https://motherfuckingwebsite.com/&quot;&gt;Motherfucking Website&lt;/a&gt;」）。人によってはまるで理解されないだろう。海外のギャラリーサイトで紹介されるようなきらびやかなウェブページにも憧れはあったが、結局そこにのめり込むことはなく、やがて言葉やタイポグラフィに惹かれるようになっていった。「&lt;a href=&quot;https://ia.net/topics/the-web-is-all-about-typography-period&quot;&gt;Web Design is 95% Typography&lt;/a&gt;」を胸に刻み、マテリアル・オネスティ（参考: 「&lt;a href=&quot;https://alistapart.com/article/material-honesty-on-the-web/&quot;&gt;Material Honesty on the Web&lt;/a&gt;」）を信条として、テキストをデザインのための拠り所として理解し、実践してきた。&lt;/p&gt;
&lt;p&gt;自分の過去を否定するつもりはない。しかし、それらの選択が一つの「逃げ」だったこともまた事実である。視覚的な美しさを追求することが僕にはできないと悟ったとき、デザインの答えを自分の中でなく客観性の中に求めた。さまざまな素材や理論と一体化した合理性の中に美しさを見い出すようになっていった。そうだったと思う。本当のところ、何が卵で何が鶏だったかはもはやわからない。ほかにもいろいろ書いたけど消した。&lt;/p&gt;
&lt;p&gt;視覚思考者であるかどうかで人を理解することはできないが、少なくとも、他者にとっての世界の見え方を想像するための助けにはできる。そして、彼が見たくてやまない世界を、僕は見ることができているのかもしれないと想い浮かべることもできる。それでよしとしよう。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;「PLAID Design Advent Calendar 2024」の2日目は、tomaさんによる「&lt;a href=&quot;https://note.com/tmtkd/n/n4d2d0d993f30&quot;&gt;ユーザーインタビューのときに考えている5つのこと&lt;/a&gt;」です。&lt;/p&gt;</content:encoded></item><item><title>npmパッケージの代わりに独自の仕組みを構築して定数ファイルを配布する運用に切り替えた経緯と移行プロセス</title><link>https://tech.plaid.co.jp/build-original-system-to-distribute-constants-files-instead-of-using-npm-packages</link><guid isPermaLink="true">https://tech.plaid.co.jp/build-original-system-to-distribute-constants-files-instead-of-using-npm-packages</guid><pubDate>Thu, 14 Nov 2024 00:00:00 GMT</pubDate></item><item><title>npmパッケージじゃない仕組みで共有ライブラリを管理する</title><link>https://talks.yuheiy.com/2024/bye-npm-packages/</link><guid isPermaLink="true">https://talks.yuheiy.com/2024/bye-npm-packages/</guid><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate></item><item><title>Feature-Sliced Designのディレクトリ規約をAstroのプロジェクトに適用する</title><link>https://yuheiy.com/2024-08-17-feature-sliced-design-for-astro</link><guid isPermaLink="true">https://yuheiy.com/2024-08-17-feature-sliced-design-for-astro</guid><description>昨今、特にフロントエンドのアプリケーションにおいて、技術的な役割よりもフィーチャー（feature）を起点としてディレクトリを構成する（package by feature）というする考えが一般化しつつある。たとえば、技術的な役割を起点としたディレクトリ構成（package by layer）は次のようなものだ。</description><pubDate>Sat, 17 Aug 2024 05:50:00 GMT</pubDate><content:encoded>&lt;p&gt;昨今、特にフロントエンドのアプリケーションにおいて、技術的な役割よりもフィーチャー（feature）を起点としてディレクトリを構成する（package by feature）というする考えが一般化しつつある。たとえば、技術的な役割を起点としたディレクトリ構成（package by layer）は次のようなものだ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;my-system&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├─&lt;/span&gt;&lt;span&gt; controllers&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; user-controller.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; order-controller.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; payment-controller.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├─&lt;/span&gt;&lt;span&gt; services&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; user-service.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; order-service.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; payment-service.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├─&lt;/span&gt;&lt;span&gt; models&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; user-model.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; order-model.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; payment-model.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/projectstructre/breakintcomponents.md&quot;&gt;Structure your solution by
components&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;この代わりに、次のように構成すべきという考えである。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;my-system&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├─&lt;/span&gt;&lt;span&gt; apps&lt;/span&gt;&lt;span&gt; (components)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; orders&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  │&lt;/span&gt;&lt;span&gt; ├─&lt;/span&gt;&lt;span&gt; package.json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  │&lt;/span&gt;&lt;span&gt; ├─&lt;/span&gt;&lt;span&gt; api&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  │&lt;/span&gt;&lt;span&gt; ├─&lt;/span&gt;&lt;span&gt; domain&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  │&lt;/span&gt;&lt;span&gt; ├─&lt;/span&gt;&lt;span&gt; data-access&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; users&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; payments&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├─&lt;/span&gt;&lt;span&gt; libraries&lt;/span&gt;&lt;span&gt; (generic &lt;/span&gt;&lt;span&gt;cross-component&lt;/span&gt;&lt;span&gt; functionality&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; logger&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;  ├─&lt;/span&gt;&lt;span&gt; authenticator&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/projectstructre/breakintcomponents.md&quot;&gt;Structure your solution by
components&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;同様の議論はこれまでにいくつもあって、方向性としては僕も賛同するが、それだけでは体系として不十分であるとも感じていた。フィーチャーの観点だけでアプリケーションが構成できるわけではないので、もっと踏み込んだ整理が必要だと思っていた。&lt;/p&gt;
&lt;p&gt;そんなわけで、コミュニティにも同様のモチベーションがあってか、より包括的な議論も目にするようになってきた。しかしその中で僕が一際目を引かれたのが、&lt;a href=&quot;https://feature-sliced.design/&quot;&gt;Feature-Sliced Design&lt;/a&gt;という方法論だ。&lt;/p&gt;
&lt;p&gt;Feature-Sliced Designにおいてもっとも特徴的なのが、「Layers」「Slices」「Segments」という階層構造になったスキームである。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/feature-sliced-design-scheme.C3Un7htC_1ddemF.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1030&quot; height=&quot;573&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://feature-sliced.design/&quot;&gt;Feature-Sliced Design&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;各Layersには決まった役割があり、その中にSlicesとSegmentsがそれぞれ入れ子になる。ただし、appとsharedにはSlicesを含めずにSegmentsが直接配置される。またSegmentsの名前としては、「ui」「api」「model」「lib」「config」が基本的な例として提案されている。&lt;/p&gt;
&lt;p&gt;このスキームが秀逸な点は、大体のソースコードはこのLayersの中にすっきり分類できてしまうこと。加えて、SlicesとSegmentsがあることで、ディレクトリを跨いでも階層構造が同様になるよう統一できることだ。&lt;/p&gt;
&lt;p&gt;そして、Feature-Sliced Designはフロントエンドのアプリケーション全般に適用できる方法論として紹介されている。その典型的な例としては、ReactやVue.jsなどを使ったSPAが当てはまるのだろうが、今回は、同様の問題を抱えがちなAstroを使ったプロジェクトに適用する例について考えてみたい。&lt;/p&gt;
&lt;p&gt;というのも、Astroの&lt;a href=&quot;https://docs.astro.build/en/basics/project-structure/&quot;&gt;Project Structure&lt;/a&gt;として紹介されている構成は、まさに技術的な役割が起点になっている例である。これに従ってウェブサイトを構築していくと、たとえば次のような形になり得る。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; about/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; profile.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; ad.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; home/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; hero.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; shared/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     ├──&lt;/span&gt;&lt;span&gt; favicon.ico&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; logo.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; about/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; Carousel.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; Sidebar.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; home/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; Hero.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; shared/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     ├──&lt;/span&gt;&lt;span&gt; Button.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; Header.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; content/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; hello.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; layouts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; Layout.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; pages/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; [slug].astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; about.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; scripts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; main.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; styles/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; main.css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; consts.ts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この構成はフレームワークによって強制されているわけではなく、多くは好みに応じて自由に変更できる。&lt;/p&gt;
&lt;p&gt;この例では、「about」「blog」「home」などの共通する関心事に基づいたファイルがそれぞれのレイヤーに分散してしまって扱いづらいため、Feature-Sliced Designに基づいて再整理してみる。次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; pages/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; [slug].astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; about.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; content/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; hello.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; app/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; layouts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; Layout.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; scripts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; main.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; styles/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; main.css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; views/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; about/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; profile.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; Carousel.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; home/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; hero.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;         └──&lt;/span&gt;&lt;span&gt; Hero.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; widgets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; blog-sidebar/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; ad.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt;         └──&lt;/span&gt;&lt;span&gt; index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; shared/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; favicon.ico&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; logo.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; config/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; Button.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        └──&lt;/span&gt;&lt;span&gt; Header.astro&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このようにすると、おおむね関心事に基づいた構成にできたように思う。&lt;/p&gt;
&lt;p&gt;前提として、Astro固有の制約によって&lt;code&gt;pages&lt;/code&gt;のディレクトリ名は変更できないので、妥協してFeature-Sliced Designのレイヤー名の方を「views」に変更した。また可能であれば、&lt;code&gt;pages&lt;/code&gt;と&lt;code&gt;content&lt;/code&gt;はappレイヤーに含めたいが、これもAstroの制約により断念。&lt;/p&gt;
&lt;p&gt;appレイヤーには、アプリケーションを動作させるための構成要素を含める。この例では、ページ全体のレイアウトを決める&lt;code&gt;layouts&lt;/code&gt;や、グローバルスタイルやクライアントサイドで実行されるスクリプトを含めている。&lt;/p&gt;
&lt;p&gt;続いて、特定のページでだけ使うAstroコンポーネントや画像ファイルなどが存在することはよくあるが、Astroの通常のディレクトリ構造ではそのためにちょうどいい置き場がない。そこで、viewsレイヤーがあることでコロケーションができるようになる。逆に、プロジェクト全体で共通して参照されているファイルについては、sharedレイヤーに配置することでその用途を明示することができる。&lt;/p&gt;
&lt;p&gt;ページを跨いで共有されるAstroコンポーネントは一部widgetsレイヤーに含めている。汎用コンポーネントならsharedレイヤーに配置できるが、これは特定の機能に紐づいたものなのでここに配置した。&lt;/p&gt;
&lt;p&gt;また、Astroを使うプロジェクトでは、SPAなどと違って、UIのソースコードが大部分を占めることになるだろう。そのため、レイヤーによってはセグメントの存在がやや冗長に感じられる。たとえば、viewsレイヤーにおいてはセグメントを省略する運用にしてもいいかもしれない。&lt;/p&gt;
&lt;p&gt;加えて、Feature-Sliced Designでは画像ファイルの置き場は考慮されていないが、Astroの運用スタイルを踏まえて、assetsというセグメントを新たに作成した。&lt;/p&gt;
&lt;p&gt;ここまでいろいろ述べてきたものの、Feature-Sliced Designを使った後者の例の方が必ずしも優れているとは限らない。特に小規模でシンプルなプロジェクトであればあるほど、前者の一般的な例の方が簡単で迷いづらいという見方もできる。しかし、特に規模が大きく複雑なプロジェクトにおいては、Feature-Sliced Designは検討に値するだろう。&lt;/p&gt;</content:encoded></item><item><title>yuheiy</title><link>https://yuheiy.com/2024-08-17-yuheiy</link><guid isPermaLink="true">https://yuheiy.com/2024-08-17-yuheiy</guid><description>このサイトの名前を「Yuhei Yasuda」から「yuheiy」に変更した。「Yuhei Yasuda」だと人名そのままだからサイト名っぽくなくて微妙だったけど、「yuheiy」ならそれっぽいかなと思ったので。</description><pubDate>Fri, 16 Aug 2024 21:40:00 GMT</pubDate><content:encoded>&lt;p&gt;このサイトの名前を「Yuhei Yasuda」から「yuheiy」に変更した。「Yuhei Yasuda」だと人名そのままだからサイト名っぽくなくて微妙だったけど、「yuheiy」ならそれっぽいかなと思ったので。&lt;/p&gt;
&lt;p&gt;最初にこのサイトを作ったとき、「yuheiy.com」のようにドメイン名をそのまま使うという手もあった。けどそれもなんだかメタ的な感じがするので、もっと普通のサイト名っぽくしたいと思っていた。それから時を経てふと先日、ドメイン名からトップレベルドメインの「.com」を取り除いたらどうだろうと考えてみて、そこそこしっくり来たのでこれに決めた。ついでに、文字の造形としてもアイコニックな感じがしてそれっぽい。&lt;/p&gt;
&lt;p&gt;そもそも「yuheiy.com」というドメインに決めた理由は、「yuhei.com」が取れなかったからだ。大昔に「yuhei.info」を取ったこともあるけど、そこで運用していたウェブサイトをしばらく放置しっぱなしにしてしまって、ドメインごと破棄した。その後、改めて自分のドメインを取ることになり、今度は「.com」がいいなと思ったので「yuhei」の末尾に苗字の「y」を付与してなんとか取った。以来、SNSなどのIDにも「yuheiy」を使うようになっていった。&lt;/p&gt;
&lt;p&gt;ただそうすると、ハンドルネームをそのままサイト名に採用しているような感じにもなってしまって、けっきょく「Yuhei Yasuda」と変わらないような気がする。でも、これはインターネット人格用の名前だし、そのためのウェブサイトなのだと考えると、まあいいやという気にもなってきた。少なくとも、「Yuhei Yasuda」よりはマシだ。&lt;/p&gt;
&lt;p&gt;そういうわけで、これからはyuheiyというサイト名でやっていく。また別のアイデアが出たら変えるかもしれない。&lt;/p&gt;</content:encoded></item><item><title>ソースコードを解析して社内向けUIライブラリの使用状況を自動で集計する</title><link>https://tech.plaid.co.jp/analyze-internal-ui-library-usage</link><guid isPermaLink="true">https://tech.plaid.co.jp/analyze-internal-ui-library-usage</guid><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate></item><item><title>Astroのアセットをdynamic importで読み込んで間接的な変数をなくす</title><link>https://yuheiy.com/2024-08-05-using-dynamic-import-for-astro-assets</link><guid isPermaLink="true">https://yuheiy.com/2024-08-05-using-dynamic-import-for-astro-assets</guid><description>Astroにおいて、コンポーネントから画像などのアセットを読み込む場合、import文を使うことが一般的だ。</description><pubDate>Sun, 04 Aug 2024 23:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Astroにおいて、コンポーネントから画像などのアセットを読み込む場合、import文を使うことが一般的だ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Image } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;astro:assets&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; logoImage &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;../assets/logo.svg&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;logoImage&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Yuhei Yasuda&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、このような書き方をするとやや不便なことがある。それはimport文を使うと、実際にアセットを使用する箇所から離れた部分に記述が分散するため、一見して対応関係がわかりづらくなること。かつ、編集が面倒になる。例のように行数の少ないファイルではそれほどではないが、行数の多いファイルでは、離れた箇所に記述すると非常に煩わしいことになる。&lt;/p&gt;
&lt;p&gt;そこで、代わりにdynamic importを使うことで、アセットの読み込みをインライン化することができる。&lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt;コンポーネントの&lt;code&gt;src&lt;/code&gt; propはPromiseに対応しているため、ここで直接dynamic importを使用できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Image } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;astro:assets&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../assets/logo.svg&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Yuhei Yasuda&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;普通JavaScriptのアプリケーションでは、code splittingを有効にするためにdynamic importを使うが、この場合ではビルドへの影響はない。&lt;/p&gt;
&lt;p&gt;ただし、Promiseの解決のためにawaitが必要な場合は都合が悪い。Astroコンポーネントにおいては、コードフェンスの外側ではawaitを使用できず、awaitはコードフェンスの内側に記述しなければならないからだ。したがって、直接パスを参照したければ、けっきょくは変数化せざるを得ない。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Image } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;astro:assets&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; logoImage&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;../assets/logo.svg&apos;&lt;/span&gt;&lt;span&gt;)).default;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;logoImage.src&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Yuhei Yasuda&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このような場合では、あえてdynamic importを使う理由はない。&lt;/p&gt;
&lt;p&gt;一方、Astroコンポーネントではなく、MDXファイルの中ではawaitを使うことができる。そのため、dynamic importを活用した記述が可能である。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt; controls&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;./assets/video.webm&apos;&lt;/span&gt;&lt;span&gt;)).default&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;video/webm&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>日本語におけるtext-wrapプロパティの運用</title><link>https://yuheiy.com/2024-07-22-text-wrap-in-japanese</link><guid isPermaLink="true">https://yuheiy.com/2024-07-22-text-wrap-in-japanese</guid><description>CSSのtext-wrapプロパティを使うと、テキストの行の折り返し方法を変更できる。text-wrap: balanceを適用すると、適用しない場合と比べて次のように変化する。</description><pubDate>Sun, 21 Jul 2024 19:20:00 GMT</pubDate><content:encoded>&lt;p&gt;CSSの&lt;code&gt;text-wrap&lt;/code&gt;プロパティを使うと、テキストの行の折り返し方法を変更できる。&lt;code&gt;text-wrap: balance&lt;/code&gt;を適用すると、適用しない場合と比べて次のように変化する。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/balance.prqY6QZs_Z1i3MvQ.webp&quot; alt=&quot;2つの例があり、一方はunbalanced、もう一方はbalancedとラベリングされている。unbalancedの例では、文字は行の終端まで到達してから改行されており、balancedの例では、それぞれの行の長さが均等になる位置で改行されている&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;593&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://developer.chrome.com/docs/css-ui/css-text-wrap-balance&quot;&gt;CSS text-wrap: balance  |  CSS and UI  |  Chrome for
Developers&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;text-wrap: balance&lt;/code&gt;が適用された下の例では、すべての行の長さが均等になるように制御されている。&lt;/p&gt;
&lt;p&gt;しかし正確に言えば、すべての行の長さが必ずしもまったく同じになるわけではない。文字の適切な折り返し位置を考慮した上で、おおよそ同じくらいの長さになるように分配される、というのが正しい。その際、一つの英単語の途中で行が分割されるようなことは通常起こらない。これは、英語では単語の区切りに空白文字を挟んで記述されるが（&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E3%82%8F%E3%81%8B%E3%81%A1%E6%9B%B8%E3%81%8D&quot;&gt;わかち書き&lt;/a&gt;）、それが改行位置を決めるためのヒントとなるからである。&lt;/p&gt;
&lt;p&gt;だが日本語の場合は都合が違う。改行できる位置に空白文字が挟まることがないため、文節や単語の途中でも関係なく改行される仕様になっている。したがって、日本語にそのまま&lt;code&gt;text-wrap&lt;/code&gt;プロパティを適用しても、英語の例と同じように好ましい結果にはならない。&lt;/p&gt;
&lt;h2&gt;文節区切りでの改行の実現&lt;/h2&gt;
&lt;p&gt;日本語においても適切な位置で改行がなされるようにするための手法はいくつかあるが、最も有望なのは&lt;code&gt;word-break: auto-phrase&lt;/code&gt;を使うことであろう。これによって文節区切りの改行が実現できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ja&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      word-break&lt;/span&gt;&lt;span&gt;: auto-phrase;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;窓ぎわのトットちゃん&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/auto-phrase.BrEi0-c1_1QOqGM.webp&quot; alt=&quot;word-break:
auto-phaseを使うと文節に応じて行が折り返される&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;300&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://developer.chrome.com/blog/css-i18n-features&quot;&gt;Introducing four new international features in CSS  |  Blog  |  Chrome for
Developers&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;改行位置を手動で調整するやり方もあるが、この手法ではそれが自動的に行えるという点画期的だ。現状、&lt;a href=&quot;https://caniuse.com/mdn-css_properties_word-break_auto-phrase&quot;&gt;このプロパティのサポート状況は十分ではない&lt;/a&gt;ため、必要に応じて&lt;a href=&quot;https://developers-jp.googleblog.com/2023/09/budoux-adobe.html&quot;&gt;BudouX&lt;/a&gt;の使用なども検討すると良いだろう。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;balance&lt;/code&gt;と&lt;code&gt;pretty&lt;/code&gt;の使い分け&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;text-wrap&lt;/code&gt;プロパティの値としては&lt;code&gt;balance&lt;/code&gt;が取り立てて紹介されがちだが、それ以上に有用な&lt;code&gt;pretty&lt;/code&gt;という値もある。&lt;code&gt;balance&lt;/code&gt;ではすべての行が同じくらいの長さになるように調整されるのに対して、&lt;code&gt;pretty&lt;/code&gt;は最後の行が一つの単語だけで終わることを防ぐ。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/widows-orphans.BQ3yE3FY_Z96otW.svg&quot; alt=&quot;段落の冒頭にwidowが、末尾にorphanが配置されており、それと比較するようにwidowやorphanのない例が並べられている&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://fonts.google.com/knowledge/glossary/widows_orphans&quot;&gt;Widows &amp;amp; orphans – Fonts Knowledge - Google
Fonts&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;欧文組版においては、最後の行に一つだけ配置された単語を&lt;a href=&quot;https://fonts.google.com/knowledge/glossary/widows_orphans&quot;&gt;widows and orphans&lt;/a&gt;と呼び、これがあるとテキストが読みにくくなるとして避けられている。&lt;code&gt;pretty&lt;/code&gt;はこの問題を解決するためのものである。&lt;/p&gt;
&lt;p&gt;また、&lt;code&gt;pretty&lt;/code&gt;は欧文の本文に適用するためのものであるようにしばしば紹介されるが、見出しなどでも有効に活用できる。見出しのレイアウトにおいては、中央揃えなら&lt;code&gt;balance&lt;/code&gt;だが、行頭揃えなら&lt;code&gt;pretty&lt;/code&gt;の方が見栄えがよくなるという個人的な経験則がある。なぜなら行頭揃えの見出しに&lt;code&gt;balance&lt;/code&gt;を適用してしまうと、行の幅がその周辺のコンテンツよりもやけに狭く見えたり、行末方向にできた余白が不自然に大きく見えてしまったりしがちだ。これは日本語や英語に特有の問題ではなく、一般的な現象である。&lt;/p&gt;
&lt;figure&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://developer.chrome.com/docs/css-ui/css-text-wrap-balance&quot;&gt;CSS text-wrap: balance  |  CSS and UI  |  Chrome for
Developers&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;代わりに&lt;code&gt;pretty&lt;/code&gt;を適用することで、行の幅は自然なままにしつつ、折り返しが不自然になることは回避できる。&lt;/p&gt;
&lt;p&gt;ただし、&lt;a href=&quot;https://caniuse.com/mdn-css_properties_text-wrap_pretty&quot;&gt;&lt;code&gt;pretty&lt;/code&gt;のサポート状況も十分ではない&lt;/a&gt;ため、プログレッシブエンハンスメントとして採用するのがよいだろう。&lt;/p&gt;
&lt;h2&gt;日本語における適用方法&lt;/h2&gt;
&lt;p&gt;widows and orphansは欧文固有の問題であり、日本語では事情が異なる。日本語の本文は&lt;a href=&quot;https://github.com/fontplus/web-typography-glossary/blob/master/terms/betagumi.md&quot;&gt;ベタ組み&lt;/a&gt;にすることが原則であり、単語や文節に応じた折り返し位置の調整は行わないからである。したがって同じ理由で、本文に&lt;code&gt;balance&lt;/code&gt;を適用することもないはずだ。&lt;/p&gt;
&lt;p&gt;そのため&lt;code&gt;text-wrap&lt;/code&gt;プロパティの使いどころは、本文を除く見出し部分などが主となる。方針によっては、リードやキャプションなどを含むこともあるだろう。&lt;/p&gt;
&lt;p&gt;ここまでの説明を経てルールがやや複雑になってきたので、フローチャートを用いて整理してみる。英語の場合、行揃えの方向によって使い方が異なる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/flowchart-latin.sSiYb97H_ZSlKFq.webp&quot; alt=&quot;&quot; title=&quot;欧文のフローチャート&quot; loading=&quot;lazy&quot; width=&quot;916&quot; height=&quot;359&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;行頭揃えの見出しには&lt;code&gt;text-wrap: pretty&lt;/code&gt;、中央揃えの見出しには&lt;code&gt;text-wrap: balance&lt;/code&gt;。本文は常に&lt;code&gt;text-wrap: pretty&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;日本語の場合、見出しか本文かによってテキストの組み方が異なる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/flowchart-japanese.DqSvFSde_1WdyTq.webp&quot; alt=&quot;&quot; title=&quot;和文のフローチャート&quot; loading=&quot;lazy&quot; width=&quot;968&quot; height=&quot;300&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;行頭揃えの見出しには&lt;code&gt;text-wrap: pretty&lt;/code&gt;、中央揃えの見出しには&lt;code&gt;text-wrap: balance&lt;/code&gt;。本文には&lt;code&gt;text-wrap&lt;/code&gt;プロパティは使用しない。これに当てはまらないリードやキャプションなどについては、方針に応じていずれかの組み方に合わせる。&lt;/p&gt;
&lt;p&gt;また、見出しは文節改行かつ&lt;a href=&quot;https://github.com/fontplus/web-typography-glossary/blob/master/terms/proportional-metrics.md&quot;&gt;プロポーショナル&lt;/a&gt;詰め、本文はベタ組みにするのが一般的であるため、フローチャートでは次のように整理できる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/flowchart-overall.BVRARSJE_Z1famLJ.webp&quot; alt=&quot;&quot; title=&quot;欧文と和文のフローチャートに加えて、それぞれの方針を示したもの&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;724&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;欧文は常にプロポーショナル詰めになる。和文の場合は、見出しの場合は文節改行かつプロポーショナル詰めだが、本文ではベタ組みになる。&lt;/p&gt;
&lt;p&gt;ここまで述べてきた方針は、次のようなCSSとして表現できる。&lt;/p&gt;
&lt;p&gt;見出し（行頭揃え）:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-wrap&lt;/span&gt;&lt;span&gt;: pretty;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ja&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-feature-settings&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;palt&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-wrap&lt;/span&gt;&lt;span&gt;: pretty;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  word-break&lt;/span&gt;&lt;span&gt;: auto-phrase;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;見出し（中央揃え）:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;balance&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ja&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-feature-settings&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;palt&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-wrap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;balance&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  word-break&lt;/span&gt;&lt;span&gt;: auto-phrase;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;本文:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  text-wrap&lt;/span&gt;&lt;span&gt;: pretty;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:lang&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ja&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* デフォルト値を使用 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/css-ui/css-text-wrap-balance&quot;&gt;CSS text-wrap: balance  |  CSS and UI  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/css-text-wrap-pretty&quot;&gt;CSS text-wrap: pretty  |  Blog  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/css-i18n-features&quot;&gt;Introducing four new international features in CSS  |  Blog  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pot.co.jp/pagination&quot;&gt;ページネーションのための基本マニュアル&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/jlreq/&quot;&gt;Requirements for Japanese Text Layout - 日本語組版処理の要件（日本語版）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/fontplus/web-typography-glossary&quot;&gt;Webタイポグラフィの基礎知識と実践&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Storybookでcontrolledなコンポーネントを操作可能にしつつ引数をモックする</title><link>https://yuheiy.com/2024-07-09-controlled-components-in-storybook</link><guid isPermaLink="true">https://yuheiy.com/2024-07-09-controlled-components-in-storybook</guid><description>Storybookにおいて、Reactコンポーネントを使ったstoryを作成する際、単なるボタンのようなシンプルなコンポーネントであればcomponentプロパティに指定するだけで良い。</description><pubDate>Tue, 09 Jul 2024 05:10:00 GMT</pubDate><content:encoded>&lt;p&gt;Storybookにおいて、Reactコンポーネントを使ったstoryを作成する際、単なるボタンのようなシンプルなコンポーネントであれば&lt;code&gt;component&lt;/code&gt;プロパティに指定するだけで良い。&lt;/p&gt;
&lt;p&gt;なおこの記事での解説は、Storybookのバージョン8.1.1に基づいたものである。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Button.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { MouseEvent, ReactNode } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  onClick&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; MouseEvent&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HTMLButtonElement&lt;/span&gt;&lt;span&gt;&amp;gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Button.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Meta, StoryObj } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fn } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Button } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; meta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;Button&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  component: Button,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onClick: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Meta&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; meta;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; Story&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; StoryObj&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; meta&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; Primary&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    children: &lt;/span&gt;&lt;span&gt;&apos;Click me&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Story&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このように記述することで、コンポーネントにargsが渡されたうえで描画される。疑似コードで表現すれば次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Primary&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; args&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    children: &lt;/span&gt;&lt;span&gt;&apos;Click me&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onClick: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一方、&lt;a href=&quot;https://ja.react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components&quot;&gt;controlledなコンポーネント&lt;/a&gt;のstoryを作成する場合、そのコンポーネントの状態を外部から制御するための実装が必要になる。たとえば次のように。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ReactNode, useCallback } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; interface&lt;/span&gt;&lt;span&gt; ToggleButtonProps&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  isSelected&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  onChange&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; ToggleButton&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;onChange&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ToggleButtonProps&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; handleClick&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onChange&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;isSelected);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }, [isSelected, onChange]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;toggle-button&quot;&lt;/span&gt;&lt;span&gt; aria-pressed&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleClick&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ToggleButton } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./ToggleButton&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Primary&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt; isSelected&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      Toogle&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;&lt;code&gt;render&lt;/code&gt;メソッドの実装&lt;/h2&gt;
&lt;p&gt;これをstoryとして表現するには、&lt;code&gt;component&lt;/code&gt;プロパティにコンポーネントを指定するだけでは不十分である。そこでこの描画方法をカスタマイズするために、&lt;code&gt;render&lt;/code&gt;メソッドを併せて実装する必要がある。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Meta, StoryObj } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fn } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ToggleButton } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./ToggleButton&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; meta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;ToggleButton&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  component: ToggleButton,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onChange: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; isSelected&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Meta&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; ToggleButton&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; meta;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; Story&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; StoryObj&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; meta&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; Primary&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    children: &lt;/span&gt;&lt;span&gt;&apos;Toggle&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Story&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;metadata（&lt;code&gt;meta&lt;/code&gt;）として指定された値はstoryに継承されるため、自ずとPrimary storyでは同じ&lt;code&gt;render&lt;/code&gt;メソッドが再利用される。ちなみに&lt;code&gt;render&lt;/code&gt;メソッドに&lt;code&gt;Render&lt;/code&gt;という名前で関数宣言を記述しているのは、&lt;a href=&quot;https://github.com/storybookjs/storybook/issues/21115&quot;&gt;eslint-plugin-react-hooksの&lt;code&gt;react-hooks/rules-of-hooks&lt;/code&gt;ルールとの競合を回避するため&lt;/a&gt;だ。&lt;/p&gt;
&lt;p&gt;これによって、controlledなコンポーネントを操作可能なstoryをを作成できる。ただしこの実装では、Storybookのcontrolsからargsの値を制御できなくなってしまう。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;h2&gt;&lt;code&gt;useArgs&lt;/code&gt;フックの使用&lt;/h2&gt;
&lt;p&gt;そのようなケースに対応すべく、argsの値を操作する&lt;a href=&quot;https://storybook.js.org/docs/writing-stories/args#setting-args-from-within-a-story&quot;&gt;&lt;code&gt;useArgs&lt;/code&gt;フック&lt;/a&gt;がStorybookによって提供されている。これを利用することで次のような実装ができる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useArgs } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/preview-api&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Meta, StoryObj } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fn } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ToggleButton } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./ToggleButton&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; meta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;ToggleButton&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  component: ToggleButton,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onChange: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; [{ &lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;updateArgs&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useArgs&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; handleChange&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButtonProps&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;onChange&apos;&lt;/span&gt;&lt;span&gt;]&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      (&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        updateArgs&lt;/span&gt;&lt;span&gt;({ isSelected });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      [updateArgs],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; isSelected&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleChange&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Meta&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; ToggleButton&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;残る問題は、argsに設定されている&lt;code&gt;onChange&lt;/code&gt;が呼び出されていないことである。&lt;code&gt;onChange&lt;/code&gt;に渡されている&lt;a href=&quot;https://storybook.js.org/docs/essentials/actions#via-storybooktest-fn-spy-function&quot;&gt;&lt;code&gt;fn()&lt;/code&gt;はメソッドをモックするための機能&lt;/a&gt;であり、&lt;a href=&quot;https://jestjs.io/ja/docs/mock-functions&quot;&gt;Jestにあるモック関数&lt;/a&gt;に似たものだ。これを用いることで、呼び出しのたびにStorybookのActionsパネルにログを表示したり、interaction testsにて呼び出し回数を調べたりできる。storyをより有用なものにするには、これが機能している方が良いだろう。&lt;/p&gt;
&lt;p&gt;そんなわけで、このモックの呼び出しを有効にするには、次のように実装する。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useArgs } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/preview-api&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Meta, StoryObj } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fn } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useCallback } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ToggleButton, ToggleButtonProps } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./ToggleButton&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; meta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;ToggleButton&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  component: ToggleButton,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onChange: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; [{ &lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;updateArgs&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useArgs&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; handleChange&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButtonProps&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;onChange&apos;&lt;/span&gt;&lt;span&gt;]&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      (&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        args.&lt;/span&gt;&lt;span&gt;onChange&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;a);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; a;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        updateArgs&lt;/span&gt;&lt;span&gt;({ isSelected });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      [args, updateArgs],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; isSelected&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleChange&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Meta&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; ToggleButton&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;onChange&lt;/code&gt; propの呼び出しタイミングでargsの&lt;code&gt;onChange&lt;/code&gt;メソッドと&lt;code&gt;updateArgs&lt;/code&gt;フックが同時に呼び出されるようにする。それにより、argsの値を制御可能にしつつ、メソッドの呼び出しのたびにログが表示されるようにできる。&lt;/p&gt;

&lt;figure&gt;&lt;/figure&gt;
&lt;h2&gt;&lt;code&gt;useArgs&lt;/code&gt;フックの問題&lt;/h2&gt;
&lt;p&gt;しかし、&lt;code&gt;useArgs&lt;/code&gt;フックには、&lt;a href=&quot;https://github.com/storybookjs/storybook/issues/25380&quot;&gt;同一ページ内の複数箇所で呼び出されると正しく機能しないという不具合がある&lt;/a&gt;。したがって、&lt;a href=&quot;https://storybook.js.org/docs/writing-docs/autodocs&quot;&gt;Autodocs&lt;/a&gt;によって生成されるドキュメントページなどではコンポーネントが操作不能になってしまうため、都合が悪い。&lt;/p&gt;
&lt;p&gt;そのため、&lt;code&gt;useArgs&lt;/code&gt;フックを使わないパターンの実装も紹介しておく。次のようになる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.stories.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Meta, StoryObj } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fn } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@storybook/test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useCallback } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ToggleButton, ToggleButtonProps } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./ToggleButton&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; meta&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  title: &lt;/span&gt;&lt;span&gt;&apos;ToggleButton&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  component: ToggleButton,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  tags: [&lt;/span&gt;&lt;span&gt;&apos;autodocs&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  argTypes: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      control: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onChange: &lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(args.isSelected);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; handleChange&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButtonProps&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;onChange&apos;&lt;/span&gt;&lt;span&gt;]&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      (&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        args.&lt;/span&gt;&lt;span&gt;onChange&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;a);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        setSelected&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;a);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      [args],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;ToggleButton&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; isSelected&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleChange&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Meta&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; ToggleButton&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;まず、&lt;code&gt;useArgs&lt;/code&gt;フックの代わりに、前述と同様の&lt;code&gt;useState&lt;/code&gt;フックを使うようにする。次に、argsに設定した&lt;code&gt;isSelected&lt;/code&gt;を&lt;code&gt;useState&lt;/code&gt;フックに渡す。これによって、argsから初期値を制御できるようになる。別のstoryを作成する際に次のように活用できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; SelectedByDefault&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    isSelected: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; Story&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最後に、argTypesから&lt;code&gt;isSelected&lt;/code&gt;の&lt;code&gt;control&lt;/code&gt;を&lt;code&gt;false&lt;/code&gt;にする。&lt;code&gt;useArgs&lt;/code&gt;フックを使用しないとcontrolからの制御はできないので、control自体を非表示にしてしまう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/hide-control.BzeQ6txz_23Y5N0.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;225&quot; /&gt;&lt;/figure&gt;
&lt;h2&gt;controlledとuncontrolledの両方に対応したコンポーネントを作成する&lt;/h2&gt;
&lt;p&gt;とはいえ、そもそもコンポーネントがuncontrolledな状態にも対応できるように実装されていれば、通常利用する際にも便利だし、Storybookでの扱いも簡単になる。controlledとuncontrolledの両方に対応するコンポーネントを作成するには少しコツがいるが、既存のライブラリなどを利用すれば容易に実現できる。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://react-spectrum.adobe.com/&quot;&gt;React Spectrum&lt;/a&gt;で利用されている&lt;a href=&quot;https://github.com/adobe/react-spectrum/tree/%40react-stately/utils%403.10.1/packages/%40react-stately/utils&quot;&gt;@react-stately/utils&lt;/a&gt;では、そのための&lt;code&gt;useControlledState&lt;/code&gt;フックが提供されており、次のような実装ができる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ToggleButton.tsx&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useControlledState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@react-stately/utils&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ReactNode, useCallback } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; interface&lt;/span&gt;&lt;span&gt; ToggleButtonProps&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  isSelected&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  defaultSelected&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  onChange&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ((&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; ToggleButton&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ToggleButtonProps&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSelected&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useControlledState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    props.isSelected,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    props.defaultSelected &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    props.onChange,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; handleClick&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setSelected&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;isSelected);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }, [isSelected, setSelected]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;toggle-button&quot;&lt;/span&gt;&lt;span&gt; aria-pressed&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSelected&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleClick&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;[その他の使用例は同リポジトリから参照できる](&lt;a href=&quot;https://github.com/search?q=repo%3Aadobe%2Freact-spectrum%20useControlledState(&amp;amp;type=code)%E3%80%82&quot;&gt;https://github.com/search?q=repo%3Aadobe%2Freact-spectrum%20useControlledState(&amp;amp;type=code)。&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>不可視状態からフェードインする要素を即座にフォーカスする方法</title><link>https://yuheiy.com/2024-07-06-instantly-focus-on-a-fade-in-element</link><guid isPermaLink="true">https://yuheiy.com/2024-07-06-instantly-focus-on-a-fade-in-element</guid><description>CSSのdisplay: noneやvisibility: hiddenによって不可視状態になっている要素を、表示して即座にフォーカスしたいということがある。たとえば、初期状態では非表示になっている検索ボックスを、ユーザーのインタラクションに応じて表示するような場合。そうしたとき、通常では、スタイルを切り替えてすぐにfocusメソッドを呼び出すだけで良い。</description><pubDate>Sat, 06 Jul 2024 10:30:00 GMT</pubDate><content:encoded>&lt;p&gt;CSSの&lt;code&gt;display: none&lt;/code&gt;や&lt;code&gt;visibility: hidden&lt;/code&gt;によって不可視状態になっている要素を、表示して即座にフォーカスしたいということがある。たとえば、初期状態では非表示になっている検索ボックスを、ユーザーのインタラクションに応じて表示するような場合。そうしたとき、通常では、スタイルを切り替えてすぐに&lt;code&gt;focus&lt;/code&gt;メソッドを呼び出すだけで良い。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;search&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;.&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;revert&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; searchBoxElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;input[type=&quot;search&quot;]&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; open&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; close&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし場合によっては、表示する際にフェードインなどのアニメーションを伴わせたいこともある。これを簡単に実現するには、CSSの&lt;code&gt;transition&lt;/code&gt;プロパティを使いつつ、&lt;code&gt;opacity&lt;/code&gt;プロパティと&lt;code&gt;visibility&lt;/code&gt;プロパティを操作するとよい。&lt;code&gt;visibility&lt;/code&gt;プロパティはアニメーション可能（&lt;a href=&quot;https://www.w3.org/TR/web-animations/#discrete&quot;&gt;discrete&lt;/a&gt;）であるため、&lt;code&gt;transition&lt;/code&gt;プロパティで制御することができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;search&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  visibility&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  opacity&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    visibility &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    opacity &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;.&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    visibility&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;revert&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    opacity&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;revert&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; searchBoxElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;input[type=&quot;search&quot;]&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; open&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; close&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、このような実装ではフォーカスができずに失敗してしまう。なぜなら、スタイルを書き換えた直後にはまだ不可視状態（&lt;code&gt;visibility: hidden&lt;/code&gt;）のままだからだ。フォーカスさせるには、アニメーションが開始して&lt;code&gt;visibility: visible&lt;/code&gt;の状態になるまで待機しなければならない。そのために対処として、&lt;code&gt;requestAnimationFrame&lt;/code&gt;メソッドが使用されがちである。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; open&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;残念ながら、これは必ずしもうまくいくわけではない。&lt;code&gt;requestAnimationFrame&lt;/code&gt;メソッドが呼び出される時点ですでにアニメーションが開始しているかどうかは保証されていないからだ。そこで苦肉の策として、&lt;code&gt;requestAnimationFrame&lt;/code&gt;メソッドを二重に入れ子にすることで成功率を上げるというやり方もあるが、やはり確実ではない。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ここで重要なのは、アニメーションの開始タイミングに合わせて処理をすることである。そのためには、&lt;code&gt;transitionstart&lt;/code&gt;イベントを使用するのが適切だ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; open&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;transitionstart&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    { once: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;visibility&lt;/code&gt;プロパティを&lt;code&gt;visible&lt;/code&gt;に書き換えると、アニメーションの開始タイミングですぐに&lt;code&gt;visible&lt;/code&gt;に切り替わる（&lt;a href=&quot;https://drafts.csswg.org/web-animations-1/#animating-visibility&quot;&gt;Animation of visibility&lt;/a&gt;）。そのため、トランジションの開始をフックにすればうまくいく。&lt;/p&gt;
&lt;p&gt;さらには、&lt;code&gt;transition-behavior&lt;/code&gt;プロパティと&lt;code&gt;@starting-style&lt;/code&gt;を活用すればより簡単に実現できそうだ。&lt;code&gt;visibility&lt;/code&gt;プロパティをアニメーションさせるのと違って、&lt;code&gt;display&lt;/code&gt;プロパティに&lt;code&gt;allow-discrete&lt;/code&gt;を適用すると、表示の際にはトランジションの開始を待たずに切り替わるらしい。この性質を利用すれば、&lt;code&gt;focus&lt;/code&gt;メソッドは単に同期的に呼び出すだけでよい。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;search&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  opacity&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transition&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    opacity &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    display &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;ms&lt;/span&gt;&lt;span&gt; allow-discrete;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;amp;.&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    @&lt;/span&gt;&lt;span&gt;starting-style&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      opacity&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    display: revert;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    opacity: revert;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; searchBoxElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;input[type=&quot;search&quot;]&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; open&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; close&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  searchBoxElement.classList.&lt;/span&gt;&lt;span&gt;remove&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;open&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/entry-exit-animations?hl=ja&quot;&gt;スムーズな開始と終了のアニメーションを実現する 4 つの新しい CSS 機能  |  Blog  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nerdy.dev/using-starting-style-and-transition-behavior-for-enter-and-exit-stage-effects&quot;&gt;Using @starting-style and transition-behavior for enter and exit stage effects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Tailwind CSSにおけるHTMLのコンポーネント化とCSSのコンポーネント化</title><link>https://yuheiy.com/2024-03-18-html-components-and-css-components-in-tailwindcss</link><guid isPermaLink="true">https://yuheiy.com/2024-03-18-html-components-and-css-components-in-tailwindcss</guid><description>Tailwind CSSにおいて、スタイルの組み合わせを抽象化したいと考えたとき、対処としてはいくつかの方法が考えられる。</description><pubDate>Mon, 18 Mar 2024 01:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Tailwind CSSにおいて、スタイルの組み合わせを抽象化したいと考えたとき、対処としてはいくつかの方法が考えられる。&lt;/p&gt;
&lt;p&gt;もっとも一般的なのは、コンポーネントやパーシャルとして、クラス属性値とHTMLをセットで抽出する方法。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rounded&quot;&lt;/span&gt;&lt;span&gt; :src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;img&quot;&lt;/span&gt;&lt;span&gt; :alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;imgAlt&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mt-2&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-xs font-bold uppercase tracking-wider text-slate-600&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;{{ eyebrow }}&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;leading-snug font-bold text-slate-700&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; :href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;url&quot;&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;hover:underline&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;{{ title }}&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mt-2 text-sm text-slate-600&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;{{ pricing }}&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  props: [&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;imgAlt&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;eyebrow&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;title&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;pricing&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;url&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://tailwindcss.com/docs/reusing-styles#extracting-components-and-partials&quot;&gt;Reusing Styles - Tailwind
CSS&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これによってスタイルに関するコードが一元化されるため、新たにCSSを記述せずとも共通化の役割を果たせる。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;次に、クラスの文字列だけを変数化することで再利用可能にする方法。&lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;shadcn/ui&lt;/a&gt;では、コンポーネントに適用するクラスの文字列をexportして提供することで、ユーザーがスタイルだけを独立して利用できるようにしている。コンポーネント固有のマークアップやロジックに縛られないことが特徴である。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/shadcn-ui/ui/blob/shadcn-ui%400.8.0/templates/next-template/components/ui/button.tsx&quot;&gt;&lt;code&gt;shadcn-ui/ui/templates/next-template/components/ui/button.tsx&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; buttonVariants&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; cva&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    variants: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      variant: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        default: &lt;/span&gt;&lt;span&gt;&apos;bg-primary text-primary-foreground hover:bg-primary/90&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        destructive: &lt;/span&gt;&lt;span&gt;&apos;bg-destructive text-destructive-foreground hover:bg-destructive/90&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        outline: &lt;/span&gt;&lt;span&gt;&apos;border border-input hover:bg-accent hover:text-accent-foreground&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        secondary: &lt;/span&gt;&lt;span&gt;&apos;bg-secondary text-secondary-foreground hover:bg-secondary/80&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ghost: &lt;/span&gt;&lt;span&gt;&apos;hover:bg-accent hover:text-accent-foreground&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        link: &lt;/span&gt;&lt;span&gt;&apos;underline-offset-4 hover:underline text-primary&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      size: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        default: &lt;/span&gt;&lt;span&gt;&apos;h-10 py-2 px-4&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        sm: &lt;/span&gt;&lt;span&gt;&apos;h-9 px-3 rounded-md&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        lg: &lt;/span&gt;&lt;span&gt;&apos;h-11 px-8 rounded-md&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        icon: &lt;/span&gt;&lt;span&gt;&apos;h-10 w-10&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    defaultVariants: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      variant: &lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      size: &lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// （中略）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; { Button, buttonVariants };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この方法では、クラスを構成するためのライブラリとして&lt;a href=&quot;https://github.com/lukeed/clsx&quot;&gt;clsx&lt;/a&gt;や&lt;a href=&quot;https://cva.style/docs&quot;&gt;Class Variance Authority&lt;/a&gt;、&lt;a href=&quot;https://www.tailwind-variants.org/&quot;&gt;Tailwind Variants&lt;/a&gt;が用いられる。また、それらに応じた&lt;a href=&quot;https://github.com/paolotiu/tailwind-intellisense-regex-list&quot;&gt;専用の正規表現をエディタに設定する&lt;/a&gt;ことで、Tailwindのインテリセンスを機能させることができる。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;もう一つが、自分でCSSを書くという方法。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- Before extracting a custom class --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rounded-full bg-violet-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-400 focus:ring-opacity-75&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Save changes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;!-- After extracting a custom class --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;btn-primary&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Save changes&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; base;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; components;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; utilities;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@layer&lt;/span&gt;&lt;span&gt; components {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .btn-primary&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    @&lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt; focus&lt;/span&gt;&lt;span&gt;:ring-opacity-75 rounded-full bg-violet-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-violet-700 focus:ring focus:ring-violet-400 focus:outline-none;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply&quot;&gt;Reusing Styles - Tailwind
CSS&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;ある意味、これがもっともTailwindらしくないやり方ではあるが、有効になる場面もある。&lt;/p&gt;
&lt;p&gt;また似たアプローチとしては、普通にCSSを記述するのではなく、CSSを生成するためのプラグインを作成するという方法もある。これについては以前の僕のブログ「&lt;a href=&quot;/2024-03-16-mixins-that-take-arguments-in-tailwind-css&quot;&gt;Tailwind CSSで引数のあるMixinのような仕組みを作る方法（改）&lt;/a&gt;」で紹介した。&lt;/p&gt;
&lt;h2&gt;HTMLのコンポーネント化とCSSのコンポーネント化&lt;/h2&gt;
&lt;p&gt;それでは、これらのうちどれを採用すべきか。判断するには、&lt;strong&gt;そのスタイルはマークアップから独立して存在し得るか否か&lt;/strong&gt;という観点が重要になる。Tailwindにおいては、HTMLにおけるコンポーネントとCSSにおけるコンポーネントという概念は区別して考えられるからである。&lt;/p&gt;
&lt;p&gt;従来のBEM的なアプローチを思い返せば、CSSのためのクラスとHTMLは一対一の関係になっているのが基本だった。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- Even with custom CSS, you still need to duplicate this HTML structure --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification-logo-wrapper&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification-logo&quot;&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/img/logo.svg&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ChitChat Logo&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification-content&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification-title&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;ChitChat&amp;lt;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;chat-notification-message&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;You have a new message!&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://tailwindcss.com/docs/reusing-styles#compared-to-css-abstractions&quot;&gt;Reusing Styles - Tailwind
CSS&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;CSSはこうした決まったHTMLのために記述されるものであって、そこからは独立して存在できないように意図されている。その場合は1つ目の方法を採用するのが妥当だ。これはHTMLまでまとめてパターン化するものなので、便宜上「HTMLのコンポーネント化」と呼ぶこととする。一般に、ほとんどの場面ではこの選択になるだろう。&lt;/p&gt;
&lt;p&gt;一方「CSSのコンポーネント化」と言えるのが、スタイルの組み合わせを一つのクラスとして束ねてしまう3つ目の方法である。これはTailwindにおいては基本的に推奨されないが、しかしTailwind自身が提供しているクラスの中にはこれに該当するものがある。&lt;a href=&quot;https://tailwindcss.com/docs/container&quot;&gt;&lt;code&gt;container&lt;/code&gt;クラス&lt;/a&gt;である。&lt;/p&gt;
&lt;p&gt;Tailwindには、&lt;a href=&quot;https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer&quot;&gt;ITCSSから着想を得て取り入れられたレイヤーの概念がある&lt;/a&gt;。現代的な仕様で言うところの&lt;a href=&quot;https://drafts.csswg.org/css-cascade-5/#layering&quot;&gt;Cascade Layers&lt;/a&gt;に近いものである。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; base;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; components;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@tailwind&lt;/span&gt;&lt;span&gt; utilities;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Tailwindから提供されるすべてのクラスは、このbase / components / utilitiesのいずれかのレイヤーに分類されている。ほとんどのクラスはutilitiesレイヤーだが、唯一&lt;a href=&quot;https://tailwindcss.com/docs/container&quot;&gt;&lt;code&gt;container&lt;/code&gt;クラス&lt;/a&gt;だけがcomponentsレイヤーに属している（公式プラグインまで含めれば、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;&lt;code&gt;prose&lt;/code&gt;クラス&lt;/a&gt;、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-forms&quot;&gt;&lt;code&gt;form-*&lt;/code&gt;クラス&lt;/a&gt;、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-aspect-ratio&quot;&gt;&lt;code&gt;aspect-*&lt;/code&gt;クラス&lt;/a&gt;などもcomponentsである）。&lt;/p&gt;
&lt;p&gt;このcomponentsレイヤーは、スタイルの組み合わせをクラスとしてパターン化したものであり、後ろのutilitiesレイヤーのクラスから上書きされることが想定されている。このレイヤリングの考え方はITCSSから影響を受けたものであるが、構成をそのまま継承しているわけではなく、微妙なアレンジが施されている。ITCSSの各レイヤーをTailwindのそれと照合してみると、次のようになる。&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;ITCSS&lt;/th&gt;&lt;th&gt;Tailwind CSS&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;generic&lt;/td&gt;&lt;td&gt;base&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;elements&lt;/td&gt;&lt;td&gt;base&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;objects&lt;/td&gt;&lt;td&gt;&lt;strong&gt;components&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;components&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTMLテンプレート&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;utilities&lt;/td&gt;&lt;td&gt;utilities&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;ここで重要なのが、componentsレイヤーとして指し示される対象が変化していることである。&lt;/p&gt;
&lt;p&gt;ITCSSにおけるcomponentsレイヤーとは、特定の役割のために構築された、直接それとわかるようなはっきりとした形を持ったパーツ——普通に言われるところの「コンポーネント」——のことを指している。しかしTailwindにおいては、それはCSSのクラスではなく、HTMLのテンプレートとして表現されることがセオリーだ。つまり、「HTMLをコンポーネント化」したものがそれと対応する。&lt;/p&gt;
&lt;p&gt;問題は、Tailwindにおけるcomponentsレイヤーと対応する、ITCSSのobjectsレイヤーとは何かということ。これは、OOCSSの原則である「構造と表層の分離（&lt;a href=&quot;https://github.com/stubbornella/oocss/wiki#separate-structure-and-skin&quot;&gt;Separate structure and skin&lt;/a&gt;）」から影響を受けたものであり、ITCSSにおいてはそれは、装飾のない抽象的なパターンだとされている。具体例としては、OOCSSで言うところの&lt;a href=&quot;https://www.stubbornella.org/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/&quot;&gt;mediaオブジェクト&lt;/a&gt;や、グリッドシステムのカラム、そしてTailwindの&lt;code&gt;container&lt;/code&gt;クラスのようなもの。実装としては、&lt;a href=&quot;https://github.com/inuitcss/inuitcss/tree/develop/objects&quot;&gt;inuitcssのobjectsディレクトリ&lt;/a&gt;や、&lt;a href=&quot;https://every-layout.dev/layouts/&quot;&gt;Every Layoutのレイアウトプリミティブ&lt;/a&gt;なども参考にできるだろう。&lt;/p&gt;
&lt;p&gt;この両者の重要な差異は、前者はHTMLの構造を規定するが、後者は必ずしもそうではないという点である。たとえば、ボタンというUI要素は前者に含まれる。それは&lt;code&gt;button&lt;/code&gt;要素や&lt;code&gt;a&lt;/code&gt;要素として描画されることが意図されており、何にでもあてがえるわけではない。しかし後者は、自身がどのようなHTMLに対して適用されるかという関心を持たない。理論上、どのような場面で適用されても問題がないように構築されるものだ（もっとも、「objectsレイヤーに含まれるコンポーネント」が入れ子構造に依存する可能性はあるが、それはレイアウト上の理由でしかない）。&lt;/p&gt;
&lt;p&gt;後者のように、特定の用途やHTMLの構造に縛られないような、汎用的かつ意味のある繰り返しのパターンがあるのであれば、CSSとしてコンポーネント化する方が扱いやすくなることもある。僕がよく作るのは、&lt;a href=&quot;https://github.com/yuheiy/sdenv/blob/877567911afcf9209df8648f27a66034a17c0c16/src/styles/components/wrapper.css&quot;&gt;コンテンツの最大幅を制御する&lt;code&gt;wrapper&lt;/code&gt;クラス&lt;/a&gt;や、&lt;a href=&quot;https://github.com/yuheiy/sdenv/blob/877567911afcf9209df8648f27a66034a17c0c16/tailwind.config.cjs#L37-L71&quot;&gt;グリッドレイアウトのための&lt;code&gt;auto-grid&lt;/code&gt;クラス&lt;/a&gt;、&lt;code&gt;heading-2&lt;/code&gt;クラスのような標準的なテキストスタイル（例: &lt;a href=&quot;https://m2.material.io/design/typography/the-type-system.html#type-scale&quot;&gt;Material Design&lt;/a&gt;、&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/typography#macOS-built-in-text-styles&quot;&gt;Human Interface Guidelines&lt;/a&gt;）のためのものなど。いずれも、utilitiesのように単機能ではなく、複合的な一つのセットになることで意味のあるものだ。&lt;/p&gt;
&lt;p&gt;componentsレイヤーがutilitiesレイヤーと異なるのは、後ろのレイヤー、つまりutilitiesレイヤーからの上書きができるという点である。これによって、あくまでcomponentsレイヤーのスタイルは「デフォルト」として扱いつつ、utilitiesレイヤーを用いることで用途に応じた調整ができるようになっている。逆に、utilitiesである&lt;a href=&quot;https://tailwindcss.com/docs/screen-readers&quot;&gt;&lt;code&gt;sr-only&lt;/code&gt;クラス&lt;/a&gt;は、一見複数の機能の組み合わせのようにも見えるが、調整は想定されておらず常にこのまま適用されるべきであるためこのレイヤーに位置している。&lt;/p&gt;
&lt;p&gt;ただし実は、表に示したように、ITCSSとTailwind CSSのレイヤーが明確に一対一の関係にマッピングできるわけではない。ITCSSのcomponentsレイヤーとTailwind CSSのcomponentsレイヤーは、一部役割が被るところがある。たとえば、公式プラグインにある&lt;code&gt;prose&lt;/code&gt;クラスや&lt;code&gt;form-*&lt;/code&gt;クラスはITCSSで言えばcomponentsに近いが、HTMLテンプレートではなくTailwindのcomponentsレイヤーで実装されている。前述の表をより正確に表すとすれば次のようになるだろう。&lt;/p&gt;
&lt;figure&gt;&lt;div&gt;&lt;div&gt;ITCSS&lt;/div&gt;&lt;div&gt;Tailwind CSS&lt;/div&gt;&lt;div&gt;generic&lt;/div&gt;&lt;div&gt;base&lt;/div&gt;&lt;div&gt;elements&lt;/div&gt;&lt;div&gt;base&lt;/div&gt;&lt;div&gt;objects&lt;/div&gt;&lt;div&gt;&lt;strong&gt;components&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;strong&gt;components&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;HTMLテンプレート&lt;/div&gt;&lt;div&gt;utilities&lt;/div&gt;&lt;div&gt;utilities&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;最後に、前半で紹介した2つ目の方法は、HTMLのコンポーネント化とCSSのコンポーネント化のどちらでもないような、間に位置するアプローチである。しかし実際にそれを用いるのは、大抵の場合、特定の用途のためのスタイルを抽出するとき、つまりITCSSで言うcomponentsのようなものを作る場面だろう。実装の柔軟性のために、それをHTMLから独立した状態で扱えるようにしつつも、やはり実際のところどのような要素に適用されるかが決まっている。そのような場合に採用するのが適切である。&lt;/p&gt;</content:encoded></item><item><title>Tailwind CSSで引数のあるMixinのような仕組みを作る方法（改）</title><link>https://yuheiy.com/2024-03-16-mixins-that-take-arguments-in-tailwind-css</link><guid isPermaLink="true">https://yuheiy.com/2024-03-16-mixins-that-take-arguments-in-tailwind-css</guid><description>以前、「Tailwind CSSで引数のあるMixinのような仕組みを作る方法」というブログを書いた。しかしその後、調査を重ねていくうちに、以前書いたのとは別のアプローチの方が望ましいと考えるようになったため、改めて書き直すことにした。</description><pubDate>Sat, 16 Mar 2024 10:10:00 GMT</pubDate><content:encoded>&lt;p&gt;以前、「&lt;a href=&quot;2022-03-21-mixins-that-take-arguments-in-tailwind-css&quot;&gt;Tailwind CSSで引数のあるMixinのような仕組みを作る方法&lt;/a&gt;」というブログを書いた。しかしその後、調査を重ねていくうちに、以前書いたのとは別のアプローチの方が望ましいと考えるようになったため、改めて書き直すことにした。&lt;/p&gt;
&lt;p&gt;またこのブログの内容は、「&lt;a href=&quot;https://pixiv.connpass.com/event/310073/&quot;&gt;『Tailwind CSS実践入門』出版記念イベント&lt;/a&gt;」で行った僕のLTを基にしたものでもある。&lt;/p&gt;
&lt;p&gt;ちなみにここで紹介する手法はさておき、そもそもどういうときにこれをやるべきかという話については、「&lt;a href=&quot;/2024-03-18-html-components-and-css-components-in-tailwindcss&quot;&gt;Tailwind CSSにおけるHTMLのコンポーネント化とCSSのコンポーネント化&lt;/a&gt;」に書いた。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Tailwind CSSを使っていると、Sassで言うところのmixinのような仕組みが欲しくなることがたまにある。たとえば次のように、三角形を描画するための定型表現を形式化したいとき。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// https://qiita.com/degudegu2510/items/09f34d4b218c9df6bb57&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@mixin&lt;/span&gt;&lt;span&gt; triangle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$size&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  clip-path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;polygon&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;$size&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;#{$size}&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; tan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.triangle&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @include&lt;/span&gt;&lt;span&gt; triangle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rebeccapurple&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;似たようなことを実現するためのやり方はいくつかあるが、今回はこれをTailwind CSSプラグインとして実装してみる。&lt;/p&gt;
&lt;h2&gt;1値の引数を取る仕組み&lt;/h2&gt;
&lt;p&gt;まず引数を取る仕組みを作るには、プラグインの&lt;code&gt;matchUtilities&lt;/code&gt;関数を使う。ほかにも&lt;code&gt;matchComponents&lt;/code&gt;関数や&lt;code&gt;matchVariant&lt;/code&gt;関数というのもあるが、今回はユーティリティなのでこれが順当である。次のようにして使う。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tailwindcss/plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  theme: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    tabSize: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      1&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;1&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      2&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;2&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      4&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;4&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      8&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;8&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;matchUtilities&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      matchUtilities&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          tab&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            tabSize: value,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        { values: &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tabSize&apos;&lt;/span&gt;&lt;span&gt;) },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;例のように実装すると、次のようなクラスが使えるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!-- テーマを参照する例: --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;tab-4&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;!-- arbitrary valuesを使う例: --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;tab-[13]&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.tab-4&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  tab-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.tab-&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  tab-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;13&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これを見ればわかる通り、&lt;code&gt;matchUtilities&lt;/code&gt;関数のコールバック関数が返すオブジェクトがそのままCSSとして出力されるという仕組みになっている。なのでたとえば、&lt;code&gt;value&lt;/code&gt;を元にして動的に宣言を組み立てることもできる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;matchComponents&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;auto-grid&apos;&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    display: &lt;/span&gt;&lt;span&gt;&apos;grid&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;grid-template-columns&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;`repeat(auto-fill, minmax(min(${&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;}, 100%), 1fr))`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、このやり方では引数を一つしか受け取ることができない。そのため、複数の引数に応じた処理をしたい場合には使いづらい。&lt;/p&gt;
&lt;h2&gt;2値の引数を取る仕組み&lt;/h2&gt;
&lt;p&gt;そこで参考として、Tailwindのコアプラグインの実装を調べてみる。&lt;a href=&quot;https://tailwindcss.com/docs/font-size&quot;&gt;fontSizeプラグイン&lt;/a&gt;では、次のようにして2値を引数として指定できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-base/6 ...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;So I started to walk into the water...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-base/7 ...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;So I started to walk into the water...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-base/loose ...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;So I started to walk into the water...&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;base&lt;/code&gt;が&lt;code&gt;font-size&lt;/code&gt;、&lt;code&gt;6&lt;/code&gt;/&lt;code&gt;7&lt;/code&gt;/&lt;code&gt;loose&lt;/code&gt;が&lt;code&gt;line-height&lt;/code&gt;である。この実装は次のようになっている。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/master/src/corePlugins.js#L2100-L2120&quot;&gt;&lt;code&gt;tailwindcss/src/corePlugins.js#L2100-L2120&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;modifier&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; [fontSize, options] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;isArray&lt;/span&gt;&lt;span&gt;(value) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; value &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [value]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (modifier) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &apos;font-size&apos;&lt;/span&gt;&lt;span&gt;: fontSize,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &apos;line-height&apos;&lt;/span&gt;&lt;span&gt;: modifier,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; { lineHeight, letterSpacing, fontWeight } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; isPlainObject&lt;/span&gt;&lt;span&gt;(options)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ?&lt;/span&gt;&lt;span&gt; options&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    :&lt;/span&gt;&lt;span&gt; { lineHeight: options }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;font-size&apos;&lt;/span&gt;&lt;span&gt;: fontSize,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ...&lt;/span&gt;&lt;span&gt;(lineHeight &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt; {} &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&apos;line-height&apos;&lt;/span&gt;&lt;span&gt;: lineHeight }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ...&lt;/span&gt;&lt;span&gt;(letterSpacing &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt; {} &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&apos;letter-spacing&apos;&lt;/span&gt;&lt;span&gt;: letterSpacing }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ...&lt;/span&gt;&lt;span&gt;(fontWeight &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; undefined&lt;/span&gt;&lt;span&gt; ?&lt;/span&gt;&lt;span&gt; {} &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&apos;font-weight&apos;&lt;/span&gt;&lt;span&gt;: fontWeight }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;注目すべきは、関数の第二引数から&lt;code&gt;modifier&lt;/code&gt;という値を受け取っていること。クラス名のなかのスラッシュの後ろ側の値がこれに対応している。2値を取るプラグインはこれと同じ要領で実現できる。ただそれでも、3値以上の引数が必要になる場合に対応できない。&lt;/p&gt;
&lt;h2&gt;0値以上の引数を取る仕組み&lt;/h2&gt;
&lt;p&gt;そうした場合、カスタムプロパティを活用すると、3値以上（正確に言えば0値以上）の引数を取ることができる。たとえば、Tailwindでは&lt;code&gt;transform&lt;/code&gt;プロパティに関するクラスを次のように併せて記述できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;translate-x-4 scale-75 skew-y-3&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この場合、対応するCSSは次のように生成される。&lt;/p&gt;

&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.translate-x-4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.skew-y-3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.scale-75&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-translate-x&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-translate-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-rotate&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-skew-x&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-skew-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-scale-x&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-scale-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  transform&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;translate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-translate-x&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-translate-y&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    rotate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-rotate&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    skewX&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-skew-x&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;skewY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-skew-y&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    scaleX&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-scale-x&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;scaleY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-scale-y&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.translate-x-4&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-translate-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.skew-y-3&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-skew-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.scale-75&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-scale-x&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --tw-scale-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;通常、&lt;code&gt;transform&lt;/code&gt;プロパティに複数の座標変換関数を適用するには、マルチクラスの設計では実現しづらい（もっとも昨今では、それぞれに独立したプロパティが利用できるため、必ずしも&lt;code&gt;transform&lt;/code&gt;プロパティを使用する必要はないが、それは別の話）。しかしTailwindでは、このように値を部分的に挿入できるようにすることで、マルチクラスによって引数のような仕組みを実現している。&lt;/p&gt;
&lt;p&gt;同じような仕組みを実装するには、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v3.4.1/src/corePlugins.js#L910-L991&quot;&gt;&lt;code&gt;transform&lt;/code&gt;プロパティに関するプラグイン&lt;/a&gt;や、同様の手法を採用している&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/v3.4.1/src/corePlugins.js#L1835-L1933&quot;&gt;&lt;code&gt;gradientColorStops&lt;/code&gt;プラグイン&lt;/a&gt;のソースコードを参考にしていただければできるはずだ。&lt;/p&gt;
&lt;p&gt;しかし前述の2つの例と異なるのが、この手法では、複数の引数をJavaScript側で同時に受け取って加工するような処理ができないということ。「2値の引数を取る仕組み」では、&lt;code&gt;value&lt;/code&gt;と&lt;code&gt;modifier&lt;/code&gt;をJavaScript側で同時に受け取って処理することができたが、この手法では、JavaScript側で値を一つずつ受け取った上で、CSSの中で値の合成が行われることになる。そのため、複数の値の組み合わせに基づいた計算などを行うには、CSS組み込みの機能として表現できる範囲のことしかできない。&lt;/p&gt;
&lt;p&gt;たとえば、コミュニティによるプラグインの&lt;a href=&quot;https://github.com/matiasngf/tailwindcss-text-scale&quot;&gt;tailwindcss-text-scale&lt;/a&gt;では、カスタムプロパティとして2つの数値を受け取った上で、それをCSSの&lt;code&gt;calc&lt;/code&gt;関数を用いて計算するという実装方法が採用されている。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/matiasngf/tailwindcss-text-scale/blob/63254a98dba6bfe1c5247fbfabca4049bce028a0/src/index.ts#L58-L87&quot;&gt;&lt;code&gt;tailwindcss-text-scale/src/index.ts#L58-L87&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;addBase&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;:root&apos;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-max`&lt;/span&gt;&lt;span&gt;]: maxScreen.&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-min`&lt;/span&gt;&lt;span&gt;]: minScreen.&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  [screenMatcher]: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-offset`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(100vw - var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-min) * 1px)`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-difference`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-max) - var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-min)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /* *16 because clamp-percentage is in px and fontSize is in rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-percentage`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-offset) / var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-difference) * 16&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  [fontScaleMatcher]: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min-rem`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min) * 1rem)`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-max-rem`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-max) * 1rem)`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-current-rem`&lt;/span&gt;&lt;span&gt;]: &lt;/span&gt;&lt;span&gt;`calc(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-percentage) * (var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-max) -&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min)) +&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min-rem)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;font-size&apos;&lt;/span&gt;&lt;span&gt;: clamp&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ?&lt;/span&gt;&lt;span&gt; `clamp(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min-rem),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-current-rem),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-max-rem)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      :&lt;/span&gt;&lt;span&gt; `var(--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-current-rem)`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/matiasngf/tailwindcss-text-scale/blob/63254a98dba6bfe1c5247fbfabca4049bce028a0/src/index.ts#L92-L122&quot;&gt;&lt;code&gt;tailwindcss-text-scale/src/index.ts#L92-L122&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;matchUtilities&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`${&lt;/span&gt;&lt;span&gt;textScalePrefix&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;]: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;modifier&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (modifier &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; clampedUnitMin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; unitToRem&lt;/span&gt;&lt;span&gt;(value);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; clampedUnitMax&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; unitToRem&lt;/span&gt;&lt;span&gt;(modifier);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-min`&lt;/span&gt;&lt;span&gt;]: clampedUnitMin,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-max`&lt;/span&gt;&lt;span&gt;]: clampedUnitMax,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    values: fontSizes,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    modifiers: fontSizes,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;matchUtilities&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    [&lt;/span&gt;&lt;span&gt;`${&lt;/span&gt;&lt;span&gt;screenScalePrefix&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;]: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;modifier&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (modifier &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; clampedUnitMin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; parseScreenSize&lt;/span&gt;&lt;span&gt;(value);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; clampedUnitMax&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; parseScreenSize&lt;/span&gt;&lt;span&gt;(modifier);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-min`&lt;/span&gt;&lt;span&gt;]: clampedUnitMin,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [&lt;/span&gt;&lt;span&gt;`--${&lt;/span&gt;&lt;span&gt;varsPrefix&lt;/span&gt;&lt;span&gt;}-screen-max`&lt;/span&gt;&lt;span&gt;]: clampedUnitMax,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    values: screens,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    modifiers: screens,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>Object.fromEntriesを活用してArray#reduceを代替する</title><link>https://yuheiy.com/2024-03-06-object-from-entries</link><guid isPermaLink="true">https://yuheiy.com/2024-03-06-object-from-entries</guid><description>JavaScriptにおいて、ある配列をもとにして別のオブジェクトを作成する場合、Array#reduceを使用することが多い。</description><pubDate>Tue, 05 Mar 2024 21:05:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScriptにおいて、ある配列をもとにして別のオブジェクトを作成する場合、&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce&quot;&gt;&lt;code&gt;Array#reduce&lt;/code&gt;&lt;/a&gt;を使用することが多い。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; input&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;foo&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;bar&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;baz&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; input.&lt;/span&gt;&lt;span&gt;reduce&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;accumulator&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;currentValue&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  accumulator[currentValue] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; capitalize&lt;/span&gt;&lt;span&gt;(currentValue);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; accumulator;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}, {});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;assert.&lt;/span&gt;&lt;span&gt;deepStrictEqual&lt;/span&gt;&lt;span&gt;(result, { foo: &lt;/span&gt;&lt;span&gt;&apos;Foo&apos;&lt;/span&gt;&lt;span&gt;, bar: &lt;/span&gt;&lt;span&gt;&apos;Bar&apos;&lt;/span&gt;&lt;span&gt;, baz: &lt;/span&gt;&lt;span&gt;&apos;Baz&apos;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし例のように、単にキーと値の組み合わせにマッピングするだけなら、あえて&lt;code&gt;Array#reduce&lt;/code&gt;を使うまでもない。代わりに&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries&quot;&gt;&lt;code&gt;Object.fromEntries&lt;/code&gt;&lt;/a&gt;を使えば、記述するコードをもっとシンプルにできる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; input&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;foo&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;bar&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;baz&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Object.&lt;/span&gt;&lt;span&gt;fromEntries&lt;/span&gt;&lt;span&gt;(input.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [element, &lt;/span&gt;&lt;span&gt;capitalize&lt;/span&gt;&lt;span&gt;(element)]));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;assert.&lt;/span&gt;&lt;span&gt;deepStrictEqual&lt;/span&gt;&lt;span&gt;(result, { foo: &lt;/span&gt;&lt;span&gt;&apos;Foo&apos;&lt;/span&gt;&lt;span&gt;, bar: &lt;/span&gt;&lt;span&gt;&apos;Bar&apos;&lt;/span&gt;&lt;span&gt;, baz: &lt;/span&gt;&lt;span&gt;&apos;Baz&apos;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;まず配列を「キーと値のペアからなる配列」（&lt;code&gt;[key, value][]&lt;/code&gt;）に変換してから、それを&lt;code&gt;Object.fromEntries&lt;/code&gt;に渡すことでオブジェクト化できる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Object.fromEntries&lt;/code&gt;は&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/entries&quot;&gt;&lt;code&gt;Object.entries&lt;/code&gt;&lt;/a&gt;の対になるメソッドである。&lt;code&gt;Object.entries&lt;/code&gt;は、オブジェクトを「キーと値のペアからなる配列」に変換するものであるのに対して、&lt;code&gt;Object.fromEntries&lt;/code&gt;はその逆に、「キーと値のペアからなる配列」をオブジェクトに変換する。&lt;/p&gt;
&lt;p&gt;ついでに、TypeScriptでは&lt;code&gt;Array#reduce&lt;/code&gt;を使うと型の取り扱いがやや煩雑になるが、&lt;code&gt;Object.fromEntries&lt;/code&gt;を使うとそのような問題が起こらない。&lt;/p&gt;</content:encoded></item><item><title>タイプスケールがハマらないとはどういうことか</title><link>https://yuheiy.com/2024-02-16-type-scale-does-not-work</link><guid isPermaLink="true">https://yuheiy.com/2024-02-16-type-scale-does-not-work</guid><description>先日、頼まれて知人のサイトを作った。載せられるコンテンツはほとんどないものの、会社っぽい体裁にはしたいとのことだ。そんなわけで、要素はほとんどないなりに、多少なりとも見栄えがするようにデザインしてみた。要するに、このサイトみたいな極端な作りにはしなかったってこと。</description><pubDate>Thu, 15 Feb 2024 20:05:00 GMT</pubDate><content:encoded>&lt;p&gt;先日、頼まれて知人のサイトを作った。載せられるコンテンツはほとんどないものの、会社っぽい体裁にはしたいとのことだ。そんなわけで、要素はほとんどないなりに、多少なりとも見栄えがするようにデザインしてみた。要するに、このサイトみたいな極端な作りにはしなかったってこと。&lt;/p&gt;
&lt;p&gt;このサイトは、どんなビューポートのサイズで見ようがほとんどスタイルが変わらない。レイアウトも文字サイズもそのまま。ウェブページって別にそれでいいんだけど、困ることがあるとすれば、見栄えがしない。やはり、大きく表示したときにはそれ相応のスタイルが適用されていないと、手抜きに見える。あとまあ、小さいよりはデカい方が見やすいですね。&lt;/p&gt;
&lt;p&gt;そういう意味において合理的な手法のひとつとして、&lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot;&gt;fluid typography&lt;/a&gt;がある。これをTailwind CSSでも扱いやすいように僕は、&lt;a href=&quot;https://github.com/yuheiy/tailwindcss-fluid-sizing&quot;&gt;arbitrary valuesを引数として扱うことでon the flyでCSSを生成できるプラグインを作り&lt;/a&gt;、その場かぎりの値——つまりデザイントークンと言われるような秩序立てて整理された値ではないもの——をいくつもソースコードに埋め込みながら、ガシガシと見栄えを整えていった。&lt;/p&gt;
&lt;p&gt;急ぎの話だったので最初は手早く完成させたけど、一度公開してしまってからは、しばらくチマチマと余白や文字サイズを細かく調整したりして遊んでいた。そのなかで、揃ってないところを揃えようと思っていろいろ試してみていると、ふと、昔使った&lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia&lt;/a&gt;のことを思い出した。あの時はあまりうまくいかなかったけど、実際のところ何がダメだったんだろう。いちおう&lt;a href=&quot;/2022-01-24-retrospective-on-utopia&quot;&gt;振り返りは書いてみた&lt;/a&gt;ものの、あまり的を射た分析ができなかった気がする。当時は自分とは別にデザイナーがいたけれど、今は自分自身で作っているのだから、今度こそあの時掴めなかった何かに到達できるかもしれない。&lt;/p&gt;
&lt;p&gt;そうしてUtopiaを使い始めた。タイプスケールのパラメータをいじりながらソースコードにも適用し、またパラメータをいじるということを繰り返してみると、この辺がよさそうだなと落ち着くポイントが見つかった。ただ、ページを少しスクロールして、そのスケールを別のところにも適用した状態を見てみると、どうにもコレジャナイ感が出る。というのも、さっき見つけたポイントというのは、その時見ていた文字のサイズから逆算してそれに近い計算結果になるようにパラメータを調整しただけのものだったからだ。そこで続けて、ほかの部分がきれいになるように同じくパラメータの再調整をしてみると、次はまた別の部分がイマイチになってしまった。これでは埒が開かない。&lt;/p&gt;
&lt;p&gt;イマイチに見えるのは、文字とレイアウトの関係性がいい感じになってないからだろう。しかしこれは、一度完成したものの文字サイズだけを後から変えたからだという、制作の順番の問題なのか？　もしタイプスケールの方を先に決めてから作り始めればうまくハマったのか？　しかし本当に順番の問題なのであれば、今からレイアウトを作り直せばいいだけの話だ。でも、それはできなかった。思うにこれは多分、意識の問題だ。&lt;/p&gt;
&lt;p&gt;「先に外枠があってから、そのなかに文字がはめ込まれる」のか、それとも「先に文字があってから、それが主体となって周囲を形作る」のかという、主従関係の問題だ。僕が「見栄えがするように」と考えた結果立ち上がった意識が、文字を従属させたのである。外枠からの要求を満たすために文字があるのだとすれば、タイプスケールという文字主体のルール設定は成り立たない。&lt;/p&gt;
&lt;p&gt;一方、文字が主体であるという意識さえ確立されていれば、極端なことを言えば、ひとつひとつのスケールのサイズなんてなんであっても成り立つはずだ。各スケールに明確な役割（セマンティクス）を与えられれば、それに駆動されるようにしてデザインができる。この場合、タイプスケールの数は最小限に収まるだろう。&lt;/p&gt;
&lt;p&gt;逆に、文字が従属関係にありながらもタイプスケール的な発想が成り立つとすれば、アウトプットされるもののブレを間引いて丸めるための仕組みとしての機能になるだろう。ひとつひとつのステップの間隔を狭めたスケールを作って、出力値を刻むための目盛りとしてそれを位置付けることで、誤差が生じる確率を軽減するための機構となる。&lt;/p&gt;
&lt;p&gt;これらはそれぞれ逆方向からのアプローチと言える。インプット側に働きかけるか、アウトプット側に働きかけるかという違いである。&lt;/p&gt;
&lt;p&gt;タイプスケールと言うとき、そのどちらの働きを期待するかによって、意図するものや内容物は変わってくる。もし明確な問題意識があれば、そのとき必要なのはどちらか、自ずと判断できるだろう。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;本質的には、Utopia自体の問題ではなかったように思う。しかし、モジュラースケールという、ステップ間隔の広いタイプスケールを生成する仕組みがあることと、始点と終点という決まった2値の掛け合わせにデザインを委ねることのマインド的な難しさによって、問題を顕在化させる結果に繋がったようだ。&lt;/p&gt;
&lt;p&gt;こうして言葉にしてしまえば凡庸な話に思える。ただおそらく、これはけっこうクリティカルである気もするのである。&lt;/p&gt;
&lt;p&gt;「内から外へ」の発想は、&lt;a href=&quot;https://terkel.jp/archives/2021/11/every-layout/&quot;&gt;以前のタケルさんのブログ&lt;/a&gt;から着想を得た。&lt;/p&gt;</content:encoded></item><item><title>任意の要素に対してブラウザデフォルトのフォーカスリングを適用する方法</title><link>https://yuheiy.com/2023-12-31-apply-default-focus-ring-styles</link><guid isPermaLink="true">https://yuheiy.com/2023-12-31-apply-default-focus-ring-styles</guid><description>任意の要素に対して、ブラウザデフォルトのフォーカスリングが描画されるように明示的に設定したいことがある。たとえば、スタイリングの都合により一度取り除いたフォーカスリングを、ふたたび適用したいとき。</description><pubDate>Sun, 31 Dec 2023 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;任意の要素に対して、ブラウザデフォルトのフォーカスリングが描画されるように明示的に設定したいことがある。たとえば、スタイリングの都合により一度取り除いたフォーカスリングを、ふたたび適用したいとき。&lt;/p&gt;
&lt;p&gt;フォーカスリングのスタイルは、ブラウザの種別や状況によってまちまちであるため、CSSでそれらしいものをエミュレートするのが難しい。そのため、同じものを呼び出せるような特別なやり方を採用できると望ましい。&lt;/p&gt;
&lt;p&gt;一つの方法として、&lt;code&gt;outline: revert&lt;/code&gt;を適用すればデフォルトの挙動を復元できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;revert&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし場合によっては、通常はフォーカスリングが描画されない要素に対して、ブラウザのデフォルトと同じフォーカスリングが描画されるようにしたい、ということもあるだろう。この場合、&lt;code&gt;outline: revert&lt;/code&gt;ではフォーカスリングを適用できない。&lt;/p&gt;
&lt;p&gt;代わりに、次のように指定することでフォーカスリングを描画させられる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card:focus-within&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; -webkit-focus-ring-color&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;/* for Chrome */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;デフォルトのフォーカスリングを適用するための方法はブラウザによって異なるため、この方法では、それぞれに対応するために&lt;code&gt;outline&lt;/code&gt;プロパティを二重に宣言する。&lt;/p&gt;
&lt;p&gt;まずFirefox/Safariでは、次の条件に一致するときにデフォルトのフォーカスリングが描画される。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;outline-style&lt;/code&gt;として&lt;code&gt;auto&lt;/code&gt;が指定されている&lt;/li&gt;
&lt;li&gt;&lt;code&gt;outline-width&lt;/code&gt;として0より大きい&lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt;が指定されている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;outline-style&lt;/code&gt;の値が&lt;code&gt;auto&lt;/code&gt;の場合、&lt;code&gt;outline-color&lt;/code&gt;の値は無視される。&lt;/p&gt;
&lt;p&gt;Firefoxにおいては、&lt;code&gt;accent-color&lt;/code&gt;が指定されている場合、その値がフォーカスリングの色として適用される。&lt;/p&gt;
&lt;p&gt;一方Chromeでは、次の条件に一致するときにデフォルトのフォーカスリングが描画される。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;outline-style&lt;/code&gt;として&lt;code&gt;auto&lt;/code&gt;が指定されている&lt;/li&gt;
&lt;li&gt;&lt;code&gt;outline-width&lt;/code&gt;として0より大きい&lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt;が指定されている&lt;/li&gt;
&lt;li&gt;&lt;code&gt;outline-color&lt;/code&gt;として&lt;code&gt;-webkit-focus-ring-color&lt;/code&gt;が指定されている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;-webkit-focus-ring-color&lt;/code&gt;はブラウザの独自実装であり、標準化されていない。&lt;code&gt;outline-color&lt;/code&gt;の値を指定しなければ、&lt;code&gt;currentcolor&lt;/code&gt;がフォーカスリングの色として適用されることになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card:focus-within&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  outline&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;/* `outline-color`は暗黙的にgreenになる */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;green&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これらの二重の宣言はブラウザで次のように動作する。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;code&gt;outline: auto&lt;/code&gt;&lt;/th&gt;&lt;th&gt;&lt;code&gt;outline: auto -webkit-focus-ring-color&lt;/code&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Chrome&lt;/td&gt;&lt;td&gt;❌（&lt;code&gt;currentcolor&lt;/code&gt;で描画される）&lt;/td&gt;&lt;td&gt;✅（カスケードによって優先される）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;❌（パースエラーとなり無視される）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Safari&lt;/td&gt;&lt;td&gt;✅（カスケードによって打ち消される）&lt;/td&gt;&lt;td&gt;✅（カスケードによって優先される）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;デフォルトのスタイルを適用するためには、Chromeでは&lt;code&gt;outline: auto -webkit-focus-ring-color&lt;/code&gt;を使用する必要がある。このやり方の場合、カスケードによって前者の&lt;code&gt;outline: auto&lt;/code&gt;は打ち消されて、後者の宣言が優先される。&lt;/p&gt;
&lt;p&gt;Firefoxでは&lt;code&gt;outline: auto&lt;/code&gt;を使用する必要がある。後者の&lt;code&gt;outline: auto -webkit-focus-ring-color&lt;/code&gt;はパースエラーとなり無視されて、前者の宣言が適用される。&lt;/p&gt;
&lt;p&gt;Safariではそのどちらの宣言を使用してもデフォルトのスタイルが適用される。標準仕様を採用するという意味では、前者の&lt;code&gt;outline: auto&lt;/code&gt;を使用するのが好ましいが、このやり方では、カスケードによって後者の&lt;code&gt;outline: auto -webkit-focus-ring-color&lt;/code&gt;が優先されることになる。&lt;/p&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/7761&quot;&gt;[css-ui] Tweaking outline-style: auto colors. · Issue #7761 · w3c/csswg-drafts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/9199&quot;&gt;[css-ui] Should we drop ‘outline-color: invert’ · Issue #9199 · w3c/csswg-drafts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/9201&quot;&gt;[css-ui-4] influence of outline-width on auto style outlines · Issue #9201 · w3c/csswg-drafts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>次世代Webカンファレンス 2023: CSS Session</title><link>https://nextwebconf.connpass.com/event/300174/</link><guid isPermaLink="true">https://nextwebconf.connpass.com/event/300174/</guid><pubDate>Sat, 16 Dec 2023 00:00:00 GMT</pubDate></item><item><title>「リンクの中にあるリンク」「ボタンの中にあるボタン」を正しく実装する</title><link>https://cssnite.doorkeeper.jp/events/163736</link><guid isPermaLink="true">https://cssnite.doorkeeper.jp/events/163736</guid><pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate></item><item><title>CSSファイルに記述されたクラスをTailwind CSS IntelliSenseで検出できるようにする</title><link>https://yuheiy.com/2023-08-12-enabling-detection-of-classes-defined-in-css-files-with-tailwind-css-intellisense</link><guid isPermaLink="true">https://yuheiy.com/2023-08-12-enabling-detection-of-classes-defined-in-css-files-with-tailwind-css-intellisense</guid><description>VS Codeの拡張機能であるTailwind CSS IntelliSenseを使用する際には、通常、CSSファイルに記述されたクラスは補完されない。というのも、Tailwind CSS IntelliSenseでは、Tailwind CSSの設定ファイル（tailwind.config.js）を基にしてCSSを算出しており、設定ファイルを介さずに実装されたCSSは無視されるからだ。</description><pubDate>Sat, 12 Aug 2023 10:55:00 GMT</pubDate><content:encoded>&lt;p&gt;VS Codeの拡張機能である&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss&quot;&gt;Tailwind CSS IntelliSense&lt;/a&gt;を使用する際には、通常、CSSファイルに記述されたクラスは補完されない。というのも、Tailwind CSS IntelliSenseでは、Tailwind CSSの設定ファイル（&lt;code&gt;tailwind.config.js&lt;/code&gt;）を基にしてCSSを算出しており、設定ファイルを介さずに実装されたCSSは無視されるからだ。&lt;/p&gt;
&lt;p&gt;そのため、公式ドキュメントの&lt;a href=&quot;https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer&quot;&gt;Adding Custom Styles&lt;/a&gt;にあるようにCSSを記述するだけでは、IntelliSenseが有効にならない。&lt;/p&gt;
&lt;p&gt;これに対する解決策は、&lt;a href=&quot;https://tailwindcss.com/docs/plugins&quot;&gt;Tailwindプラグイン&lt;/a&gt;としてCSSを実装することだ。たとえば次のようにすることで、IntelliSenseから&lt;code&gt;card&lt;/code&gt;クラスを検出できるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tailwindcss/plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    plugin&lt;/span&gt;&lt;span&gt;(({ &lt;/span&gt;&lt;span&gt;addBase&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;addComponents&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;addUtilities&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      addComponents&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &apos;.card&apos;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          backgroundColor: &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;colors.white&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          borderRadius: &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;borderRadius.lg&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          padding: &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;spacing.6&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          boxShadow: &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;boxShadow.xl&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただしこの場合、JavaScriptとしてCSSを記述しなければならないという制約がある。普通のCSSと異なる書き方をしなければならないという点では、やや不便だろう。&lt;/p&gt;
&lt;p&gt;そこで、代わりに次のような実装をすることで、普通のCSSとして記述されたファイルをプラグインとして読み込むことができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;fs&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; postcss&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;postcss&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tailwindcss/plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    plugin&lt;/span&gt;&lt;span&gt;(({ &lt;/span&gt;&lt;span&gt;addComponents&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; fs.&lt;/span&gt;&lt;span&gt;readFileSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;src/styles/card.css&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;utf8&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; styles&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; postcss.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(content);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      addComponents&lt;/span&gt;&lt;span&gt;(styles.nodes);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;card.css&lt;/code&gt;は次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  box-shadow&lt;/span&gt;&lt;span&gt;: theme(boxShadow.xl);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  border-radius&lt;/span&gt;&lt;span&gt;: theme(borderRadius.lg);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: theme(colors.&lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding&lt;/span&gt;&lt;span&gt;: theme(spacing.&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;styles.nodes&lt;/code&gt;には、CSSをパースしてオブジェクト化された結果が格納されており、これをそのまま読み込むことでスタイルを登録できる。これは、&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/4c34b2ed34a162750df581af460121322132e88d/src/corePlugins.js#L499-L505&quot;&gt;Core pluginsのpreflightの実装方法&lt;/a&gt;を参考にしたものだ。&lt;/p&gt;
&lt;p&gt;これによってCSSファイルを読み込めるようにはなったが、ただ、さらに言うならば、CSSファイルが増えたり名前が変わったりした際には、その都度プラグインの実装に手を入れる必要があり、余計な手間は生じる。それを省力化すべく、CSSのファイル名に応じて自動的にスタイルが読み込まれる仕組み（convention over configuration）を作ってみよう。&lt;/p&gt;
&lt;p&gt;一つ前提として、Tailwindにはレイヤーという概念がある。Tailwindのすべてのスタイルは、base、components、utilitiesのいずれかに分類される必要がある。詳しくは「&lt;a href=&quot;https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer&quot;&gt;Why does Tailwind group styles into “layers”?&lt;/a&gt;」を参照のこと。&lt;/p&gt;
&lt;p&gt;プラグインの引数である&lt;code&gt;addBase&lt;/code&gt;、&lt;code&gt;addComponents&lt;/code&gt;、&lt;code&gt;addUtilities&lt;/code&gt;は、それらのレイヤーに基づいており、ユーザーは、適切なレイヤーに対応する関数を選択して使用することになる。CSSファイルがどのレイヤーに属するかの区分をするために、ファイル名のプレフィックスとしてレイヤー名を含めるという規約を設けるのがよいだろう。&lt;/p&gt;
&lt;p&gt;これを踏まえると、次のような実装になった。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * Loads CSS files through Tailwind’s plugin system to enable IntelliSense support.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * This plugin scans CSS files located in the `src/styles` directory and appends&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * them to their respective layers based on the file naming convention:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * - Files named `src/styles/base.{name}.css` are added to the base layer.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * - Files named `src/styles/components.{name}.css` are added to the components layer.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * - Files named `src/styles/utilities.{name}.css` are added to the utilities layer.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; cssFiles&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt;(({ &lt;/span&gt;&lt;span&gt;addBase&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;addComponents&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;addUtilities&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; dirname&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; path.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;src/styles&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; files&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; fs.&lt;/span&gt;&lt;span&gt;readdirSync&lt;/span&gt;&lt;span&gt;(dirname);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; file&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; files) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; matched&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;(base&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;components&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;utilities)&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;css&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(file);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (matched) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; layer&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; matched[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; addStyles&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        base: addBase,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        components: addComponents,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        utilities: addUtilities,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }[layer];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; fs.&lt;/span&gt;&lt;span&gt;readFileSync&lt;/span&gt;&lt;span&gt;(path.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(dirname, file), &lt;/span&gt;&lt;span&gt;&apos;utf8&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; styles&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; postcss.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(content);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      addStyles&lt;/span&gt;&lt;span&gt;(styles.nodes);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [cssFiles],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、まだ一つ問題が残る。それは、開発時にビルドツールなどを使ってソースコードの変更を監視する場合、CSSファイルを変更してもそれが検知されないため、再ビルドが自動的に実行されないということだ。&lt;/p&gt;
&lt;p&gt;これについての解決策としては、ビルドツールとは別に監視プロセスを設けるという手がある。CSSファイルが変更されるたびに&lt;code&gt;tailwind.config.js&lt;/code&gt;のタイムスタンプを更新することで、ビルドツールが再ビルドを実行するように促すことができる。npm scriptsとして、&lt;a href=&quot;https://github.com/open-cli-tools/chokidar-cli&quot;&gt;Chokidar CLI&lt;/a&gt;や&lt;a href=&quot;https://github.com/open-cli-tools/concurrently&quot;&gt;concurrently&lt;/a&gt;を使用することで、次のように実現できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;dev&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;concurrently --raw &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;npm:dev:*&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;dev:astro&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;astro dev&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;dev:css&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;chokidar &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;src/styles/{base,components,utilities}.*.css&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; -c &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;touch tailwind.config.cjs&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; -d 0 --silent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>ReactのuseStateをrender propとして抽象化して、コンポーネントを分割せずにコードのまとまりをよくする</title><link>https://yuheiy.com/2023-08-12-abstract-react-usestate-as-render-prop</link><guid isPermaLink="true">https://yuheiy.com/2023-08-12-abstract-react-usestate-as-render-prop</guid><description>ReactのuseStateを使用する際、stateのスコープが必要以上に大きくなってしまうことがある。たとえば次の例のように、コンポーネントの中のごく限られた部分でしか使わないstateが、コンポーネントのどこからでも参照できてしまう。</description><pubDate>Fri, 11 Aug 2023 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Reactの&lt;code&gt;useState&lt;/code&gt;を使用する際、stateのスコープが必要以上に大きくなってしまうことがある。たとえば次の例のように、コンポーネントの中のごく限られた部分でしか使わないstateが、コンポーネントのどこからでも参照できてしまう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isFirstOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setFirstOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isSecondOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setSecondOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isThirdOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setThirdOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setFirstOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;First&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isFirstOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setFirstOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setSecondOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Second&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isSecondOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setSecondOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setThirdOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Third&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isThirdOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setThirdOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;こうした場合の一般的な解決策としては、それぞれのstateに応じた粒度でコンポーネントを分割することだ。次のようにすると、stateのスコープが必要最小限になるように制限できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;First&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Second&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Third&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; First&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;First&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Second&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Second&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Third&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Third&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし場合によっては、stateの粒度に基づいてコンポーネントを分割していくと、却ってソースコードの見通しが悪くなることがある。&lt;/p&gt;
&lt;p&gt;そこで一つの代替案として、IIFEの中で&lt;code&gt;useState&lt;/code&gt;を使用するというやり方がある。次のようにすることで、&lt;code&gt;useState&lt;/code&gt;のスコープを制限できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;First&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Second&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Third&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかしこれでは、条件に応じてそのIIFEの表示を切り替えるようなケースに対応できなくなってしまう。たとえば次のような実装をすると、&lt;code&gt;isThirdVisible&lt;/code&gt;の値が切り替わった時にフックの整合性が保てなくなってしまう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;isThirdVisible&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;isThirdVisible&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;First&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Second&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;span&gt;isThirdVisible &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        (() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Third&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        })()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;加えて、&lt;q&gt;&lt;a href=&quot;https://ja.legacy.reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level&quot;&gt;最上位レベルのフックのみを呼び出す&lt;/a&gt;&lt;/q&gt;というルールに反しているため、ESLintプラグインの&lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-react-hooks&quot;&gt;eslint-plugin-react-hooks&lt;/a&gt;を使用すると警告が発生してしまう。あくまでコードスタイルに限っての問題ではあるが。&lt;/p&gt;
&lt;p&gt;したがって、IIFEの使用は得策ではなさそうだ。そのためやはり、&lt;code&gt;useState&lt;/code&gt;のスコープを制限するためにはコンポーネントを分割しなければならないという制約は免れない。&lt;/p&gt;
&lt;p&gt;そこで、&lt;code&gt;useState&lt;/code&gt;の使用を抽象化する汎用的なコンポーネントを作成するとする。これを用いることで、stateのスコープを制限しつつも、ノードの記述はインライン化できるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { State } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./State&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; App&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt; initial&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setState&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;First&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        )&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt; initial&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setState&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Second&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        )&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt; initial&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setState&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setOpen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Third&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt; isOpen&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; onOpenChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;setOpen&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        )&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;State&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;State&amp;gt;&lt;/code&gt;の実装は次の通り。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; { Dispatch, DispatchWithoutAction, ReactNode, SetStateAction } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useCallback, useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * The implement a hooks version of React PowerPlug’s `&amp;lt;State&amp;gt;`.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * https://renatorib.github.io/react-powerplug/#/docs-components-state&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * &lt;/span&gt;&lt;span&gt;@example&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * &amp;lt;State initial={false}&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *   {({ state: isOpen, setState: setOpen }) =&amp;gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *       &amp;lt;button type=&quot;button&quot; onClick={() =&amp;gt; setOpen(true)}&amp;gt;Open&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *       &amp;lt;Popover isOpen={isOpen} onOpenChange={setOpen}&amp;gt;...&amp;lt;/Popover&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *     &amp;lt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; *   )}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; * &amp;lt;/State&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&amp;gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;initialState&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  initial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; S&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; (() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; S&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    state&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; S&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setState&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Dispatch&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;SetStateAction&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    resetState&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; DispatchWithoutAction&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setState&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(initialState);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; resetState&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useCallback&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; setState&lt;/span&gt;&lt;span&gt;(initialState), [initialState]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; children&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    state,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setState,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    resetState,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;State&amp;gt;&lt;/code&gt;はその内部で&lt;code&gt;useState&lt;/code&gt;を保持しつつ、render propを介してその値や操作方法を提供する。これによって、スコープは&lt;code&gt;&amp;lt;State&amp;gt;&lt;/code&gt;の&lt;code&gt;children&lt;/code&gt;だけに制限できるようになる。最初の例と比較して、コードのまとまりはよくなったように思う。&lt;/p&gt;
&lt;p&gt;この&lt;code&gt;&amp;lt;State&amp;gt;&lt;/code&gt;は僕が考案したものではなく、&lt;a href=&quot;https://github.com/renatorib/react-powerplug&quot;&gt;React PowerPlug&lt;/a&gt;というライブラリからアイデアを借用したものだ。残念ながら、React PowerPlugは長らくメンテナンスされておらず、フックスやTypeScriptにも対応していないので、僕は前述のように自分で実装し直して使っている。&lt;/p&gt;
&lt;p&gt;もっとも、昨今の気の利いたUIライブラリであれば、こうした冗長なコードを省略できる仕組みもある。たとえば、&lt;a href=&quot;https://headlessui.com/&quot;&gt;Headless UI&lt;/a&gt;の系譜にある&lt;a href=&quot;https://react-spectrum.adobe.com/react-aria/react-aria-components.html&quot;&gt;React Aria Components&lt;/a&gt;では、次のようにコンポーネントを構成するだけで、状態管理やイベントの登録が暗黙的に行われるようになっている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;DialogTrigger&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;Open popover&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;OverlayArrow&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; height&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; d&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;M0 0,L6 6,L12 0&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;OverlayArrow&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Dialog&lt;/span&gt;&lt;span&gt;&amp;gt;This is a popover.&amp;lt;/&lt;/span&gt;&lt;span&gt;Dialog&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;Popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;DialogTrigger&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— 
&lt;cite&gt;&lt;a href=&quot;https://react-spectrum.adobe.com/react-aria/Popover.html&quot;&gt;Popover – React Aria&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;こうした仕組みによって、多くの場面には対応できるが、これでは解決できないようなケースではStateコンポーネントを採用するとよいだろう。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;ついでに言えば、&lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;を使う場合にも同じ問題がある。これに対して、&lt;a href=&quot;https://reactrouter.com/en/main&quot;&gt;React Router&lt;/a&gt;では、&lt;code&gt;&amp;lt;State&amp;gt;&lt;/code&gt;と同じくrender propインターフェースが実装された&lt;code&gt;&amp;lt;Await&amp;gt;&lt;/code&gt;という独自コンポーネントを提供することで、やはりコンポーネントを分割せずに記述できる仕組みを実現している。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://reactrouter.com/en/main/guides/deferred&quot;&gt;Deferred Data v6.15.0 | React Router&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Tailwind CSS実践入門——まず作ってから、あとで共通化する</title><link>https://gihyo.jp/list/group/Tailwind-CSS%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%EF%BD%9E%E3%81%BE%E3%81%9A%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%8B%E3%82%89-%E3%81%82%E3%81%A8%E3%81%A7%E5%85%B1%E9%80%9A%E5%8C%96%E3%81%99%E3%82%8B</link><guid isPermaLink="true">https://gihyo.jp/list/group/Tailwind-CSS%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%EF%BD%9E%E3%81%BE%E3%81%9A%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%8B%E3%82%89-%E3%81%82%E3%81%A8%E3%81%A7%E5%85%B1%E9%80%9A%E5%8C%96%E3%81%99%E3%82%8B</guid><pubDate>Mon, 24 Jul 2023 00:00:00 GMT</pubDate></item><item><title>ReactコンポーネントでレンダリングされるHTML要素の種類を変更可能にするためのパターン</title><link>https://yuheiy.com/2023-06-03-react-changeable-element-type-patterns</link><guid isPermaLink="true">https://yuheiy.com/2023-06-03-react-changeable-element-type-patterns</guid><description>ReactのUIコンポーネントライブラリを使っていると、あるコンポーネントによってレンダリングされるHTML要素の種類を変更したくなる場面がある。たとえば、通常はbutton要素としてレンダリングされるButtonコンポーネントを使うときに、代わりにa要素を使ってレンダリングしたいというケース。</description><pubDate>Sun, 04 Jun 2023 03:35:00 GMT</pubDate><content:encoded>&lt;p&gt;ReactのUIコンポーネントライブラリを使っていると、あるコンポーネントによってレンダリングされるHTML要素の種類を変更したくなる場面がある。たとえば、通常は&lt;code&gt;button&lt;/code&gt;要素としてレンダリングされる&lt;code&gt;Button&lt;/code&gt;コンポーネントを使うときに、代わりに&lt;code&gt;a&lt;/code&gt;要素を使ってレンダリングしたいというケース。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;as&lt;/code&gt; prop&lt;/h2&gt;
&lt;p&gt;そのような場合、一般的には、Buttonコンポーネントに&lt;code&gt;as&lt;/code&gt; propを実装して対処することが多い。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;button&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      I’m a &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;button&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt; element&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;a&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      I’m an &amp;lt;&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt;a&amp;lt;/&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;&amp;gt; element&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このパターンは、Polymorphicとも呼ばれる。&lt;a href=&quot;https://styled-components.com/docs/api#as-polymorphic-prop&quot;&gt;styled-components&lt;/a&gt;や&lt;a href=&quot;https://mui.com/material-ui/guides/composition/#component-prop&quot;&gt;Material UI&lt;/a&gt;、&lt;a href=&quot;https://chakra-ui.com/docs/styled-system/style-props#the-as-prop&quot;&gt;Chakra UI&lt;/a&gt;など、数多くのライブラリで同等の手法が採用されている。&lt;/p&gt;
&lt;p&gt;このコードだけを見ればごくごく簡単に実現できる機能のように思えるが、TypeScriptとして正しく型付けをしようしたり、&lt;a href=&quot;https://react.dev/reference/react/forwardRef&quot;&gt;&lt;code&gt;forwardRef&lt;/code&gt;&lt;/a&gt;を適用したりしていると、そこそこ複雑な実装になってしまう。&lt;/p&gt;
&lt;p&gt;具体的な実装方法については、&lt;a href=&quot;https://blog.logrocket.com/build-strongly-typed-polymorphic-components-react-typescript/&quot;&gt;詳しく解説している記事&lt;/a&gt;があったのでそちらに譲る。&lt;/p&gt;
&lt;p&gt;ただ、単にその仕組みの実装が複雑になるだけであれば、パッケージ化しておいたものを参照して使えばよい。実際にそれに当たるnpmモジュールもいくつかある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.radix-ui.com/docs/primitives/utilities/polymorphic&quot;&gt;@radix-ui/react-polymorphic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kripod/react-polymorphic-types&quot;&gt;react-polymorphic-types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;しかし、それ以外にもいくつかの問題がある。かつて&lt;code&gt;as&lt;/code&gt; propを採用していた&lt;a href=&quot;https://www.radix-ui.com/&quot;&gt;Radix UI&lt;/a&gt;では、これらの問題を解決するために、&lt;code&gt;as&lt;/code&gt; propが廃止されて新たに&lt;code&gt;asChild&lt;/code&gt; propが採用されている。これらの機能を実装した&lt;a href=&quot;https://github.com/jjenzz&quot;&gt;Jenna Smith&lt;/a&gt;氏によると、いくつかの観点から見て、&lt;code&gt;as&lt;/code&gt; propは過度な複雑性をもたらすと言う。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;I&apos;ve been thinking a lot about the `as` prop for polymorphism lately and wondering why we all went down the road of runtime TypeScript polymorphism. It introduces so many unnecessary complexities, bloats error messages and kills TS perf... 🧵&lt;/p&gt;— jenna (@jjenzz) &lt;a href=&quot;https://twitter.com/jjenzz/status/1423766700885954562?ref_src=twsrc%5Etfw&quot;&gt;August 6, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;一つは、TypeScriptのエラーメッセージが複雑になってしまうこと。たとえば、Reactの要素に間違ったpropが渡されたとき、通常の&lt;code&gt;a&lt;/code&gt;要素と、&lt;code&gt;as&lt;/code&gt; propとしてa要素が指定されたコンポーネントを比較すると、明らかに後者の方がユーザーにとって理解しづらい。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/as-prop-error.BkqL1KPJ_dV31G.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;341&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;次に、TypeScriptのパフォーマンスを悪化させること。&lt;code&gt;as&lt;/code&gt; propの型定義においては、高度な型推論や条件型、ジェネリクスを多用することになり、コンパイルするための負荷が高まってしまう。&lt;/p&gt;
&lt;p&gt;そして、&lt;code&gt;as&lt;/code&gt; propを使うと、元のコンポーネントと&lt;code&gt;as&lt;/code&gt;に当たるコンポーネントの双方に対するpropを同時に記述することになり、どのpropがどのコンポーネントのためのものかが理解しづらくなる。さらに、元のコンポーネントと&lt;code&gt;as&lt;/code&gt;に当たるコンポーネントに同じ名前のpropがあるとコンフリクトしてしまう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; ComponentOwnProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	onChange&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;thing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; void&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Component&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// thing would be `event` type from `input`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;input&quot;&lt;/span&gt;&lt;span&gt; onChange&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;thing&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(thing)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://github.com/radix-ui/primitives/pull/779#issuecomment-879257075&quot;&gt;[Polymorphic] Allow &lt;code&gt;ComponentType&lt;/code&gt; props to override by jjenzz · Pull Request #779 ·
radix-ui/primitives&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;&lt;code&gt;asChild&lt;/code&gt; prop&lt;/h2&gt;
&lt;p&gt;これらの問題を解決するために、新たに&lt;code&gt;asChild&lt;/code&gt; propが考案された。次のようにして使用する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Tooltip &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-tooltip&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Trigger&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://www.radix-ui.com/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Radix UI&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Trigger&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;…&amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— 
&lt;cite&gt;&lt;a href=&quot;https://www.radix-ui.com/docs/primitives/guides/composition&quot;&gt;Composition – Radix UI&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;レンダリングする要素を変更する対象のコンポーネントに対して、&lt;code&gt;as&lt;/code&gt; propの代わりに&lt;code&gt;asChild&lt;/code&gt; propを付与する。その&lt;code&gt;children&lt;/code&gt; propとして任意のノードを指定する。&lt;/p&gt;
&lt;p&gt;この&lt;code&gt;asChild&lt;/code&gt; propによって、前述の問題は一通り解決されることになる。&lt;/p&gt;
&lt;p&gt;おまけに、&lt;code&gt;asChild&lt;/code&gt; propの特性としてもたらされるのが、&lt;code&gt;asChild&lt;/code&gt; propを指定されたコンポーネントを複数に入れ子にできる機能である。次のように記述することで、親に当たるコンポーネントの機能を継承していくことができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Dialog &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-dialog&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Tooltip &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-tooltip&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; MyButton&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React.&lt;/span&gt;&lt;span&gt;forwardRef&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Dialog.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Trigger&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;Dialog.Trigger&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;MyButton&lt;/span&gt;&lt;span&gt;&amp;gt;Open dialog&amp;lt;/&lt;/span&gt;&lt;span&gt;MyButton&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/&lt;/span&gt;&lt;span&gt;Dialog.Trigger&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Trigger&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Tooltip.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;…&amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Tooltip.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Dialog.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;Dialog.Portal&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Dialog.Root&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.radix-ui.com/docs/primitives/guides/composition#composing-multiple-primitives&quot;&gt;Composition – Radix
UI&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;自前のコンポーネントに&lt;code&gt;asChild&lt;/code&gt; propを組み込む&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;asChild&lt;/code&gt; propはRadix UIのコンポーネントで提供されている機能だが、自前のコンポーネントにも容易に実装できる。&lt;a href=&quot;https://www.radix-ui.com/docs/primitives/utilities/slot&quot;&gt;&lt;code&gt;@radix-ui/react-slot&lt;/code&gt;&lt;/a&gt;パッケージを使用するのがよい。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Slot } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-slot&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;asChild&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; Comp&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; asChild &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Slot &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;Comp&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.radix-ui.com/docs/primitives/utilities/slot&quot;&gt;Slot – Radix UI&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;TypeScriptを使用しつつ、&lt;code&gt;forwardRef&lt;/code&gt;を適用する場合は、次のように記述する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { forwardRef, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ComponentPropsWithRef } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ComponentPropsWithRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;button&apos;&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;asChild&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; boolean&lt;/span&gt;&lt;span&gt; }&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ({ &lt;/span&gt;&lt;span&gt;asChild&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; Comp&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; asChild &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Slot &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;Comp&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Button.displayName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、こうなってくると定型コードとしてはやや煩雑である。そこで代わりに、Radix UIの内部で使用されている&lt;a href=&quot;https://github.com/radix-ui/primitives/tree/main/packages/react/primitive&quot;&gt;&lt;code&gt;@radix-ui/react-primitive&lt;/code&gt;&lt;/a&gt;パッケージを使うことで多少マシになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Primitive } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Radix &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { forwardRef } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ElementRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Radix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;((&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Button.displayName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このパッケージの使用方法はドキュメント化されていないので、&lt;a href=&quot;https://github.com/radix-ui/primitives/tree/1d57ea78007733facfa2ac7fc0cd919a545b39f6/packages/react&quot;&gt;Radix UIのソースコードの中で実際に使用されている様子&lt;/a&gt;を参考にして実装する必要がある。&lt;/p&gt;
&lt;h3&gt;コンポーネント同士のpropsのマージ&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Slot&lt;/code&gt;コンポーネント（および&lt;code&gt;Primitive.*&lt;/code&gt;コンポーネント）のpropsは、その子のpropsとマージされる仕組みになっている。たとえば、次のようにコンポーネントを使用するとする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Primitive } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Radix &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { forwardRef } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ElementRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Radix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;((&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ backgroundColor: &lt;/span&gt;&lt;span&gt;&apos;gray&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;original&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;original event&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Button.displayName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ border: &lt;/span&gt;&lt;span&gt;&apos;1px solid&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;child event&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      Hello&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この&lt;code&gt;Button&lt;/code&gt;コンポーネントのpropsは、擬似的に表現すれば、次のようにマージされることになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Button child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    backgroundColor: &lt;/span&gt;&lt;span&gt;&apos;gray&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    border: &lt;/span&gt;&lt;span&gt;&apos;1px solid&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    childProps.&lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    slotProps.&lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;args);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Hello&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このpropsのマージには次のルールがある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;on&lt;/code&gt;から始まる同名のpropが指定されていれば、それらをcomposeする関数が作成される&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style&lt;/code&gt; propの値はマージされる。同じプロパティが指定されていれば子が優先される&lt;/li&gt;
&lt;li&gt;&lt;code&gt;className&lt;/code&gt; propの値はマージされる&lt;/li&gt;
&lt;li&gt;それ以外のpropは子の値で上書きされる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;詳しくは、&lt;a href=&quot;https://github.com/radix-ui/primitives/blob/1d57ea78007733facfa2ac7fc0cd919a545b39f6/packages/react/slot/src/Slot.tsx#L91-L122&quot;&gt;&lt;code&gt;mergeProps&lt;/code&gt;&lt;/a&gt;の実装を参照のこと。&lt;/p&gt;
&lt;p&gt;以上のように、&lt;code&gt;Slot&lt;/code&gt;コンポーネントのpropsはその子のpropsと自動的にマージされる仕組みになっていることは把握しておきたい。&lt;/p&gt;
&lt;p&gt;注意すべきは、&lt;code&gt;Button&lt;/code&gt;コンポーネントのpropsについては、ユーザー側でマージする実装を行わなければならないということ。もし次のように、&lt;code&gt;Button&lt;/code&gt;コンポーネントのpropsのマージを考慮しない実装になっていると、&lt;code&gt;Button&lt;/code&gt;コンポーネントにpropsを指定した際に期待に反する挙動をする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Primitive } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Radix &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { forwardRef } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ElementRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Radix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;((&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ backgroundColor: &lt;/span&gt;&lt;span&gt;&apos;gray&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;original&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;original event&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Button.displayName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ border: &lt;/span&gt;&lt;span&gt;&apos;1px solid&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;child&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;child event&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Hello&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この&lt;code&gt;Button&lt;/code&gt;コンポーネントは、次のようにレンダリングされる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	data-test&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;original&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ backgroundColor: &lt;/span&gt;&lt;span&gt;&apos;gray&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;original event&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	Hello&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これでは、期待通り上書きできるものと、期待に反して上書きできないものが、propの種類によってまちまちになってしまう。期待通りのインターフェースを実現するには、&lt;code&gt;Slot&lt;/code&gt;コンポーネントにpropsが渡される前に、それらの値をマージしておかなければならない。たとえば、次のような実装になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Primitive } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Radix &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { forwardRef } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { mergeRefs } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react-merge-refs&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ElementRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Radix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;((&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; internalRef&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; useRef&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;mergeRefs&lt;/span&gt;&lt;span&gt;([internalRef, forwardedRef])&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      className&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;Button&apos;&lt;/span&gt;&lt;span&gt;, props.classname].&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean).&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; &apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      style&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        backgroundColor: &lt;/span&gt;&lt;span&gt;&apos;gray&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;span&gt;props.style,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      data-test&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;(props &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;span&gt;&apos;data-test&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &apos;original&apos;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        props.&lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;?.(event);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;original event&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Button.displayName &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;Button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3&gt;コンポーネントの中の子を規定する場合&lt;/h3&gt;
&lt;p&gt;場合によっては、&lt;code&gt;Button&lt;/code&gt;コンポーネントの実装として、ルートのコンポーネントの内側に別のコンポーネントを入れ子にしたいこともあるだろう。&lt;code&gt;as&lt;/code&gt; propを使えば、次のように実装することができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;button&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;Button&apos;&lt;/span&gt;&lt;span&gt;, props.className].&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean).&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; &apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ButtonLabel&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Component&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、&lt;code&gt;asChild&lt;/code&gt; propで同等のことを実現しようとすると、かなり複雑な実装が必要になる。たとえば次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Primitive } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; Radix &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@radix-ui/react-primitive&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Children,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  cloneElement,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ElementRef,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  forwardRef,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  isValidElement,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ReactElement,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ReactNode,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; ElementRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Radix&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ComponentPropsWithoutRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; Primitive.button&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; PrimitiveButtonProps&lt;/span&gt;&lt;span&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ({ &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; wrapChildren&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ButtonLabel&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ref&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;Button&apos;&lt;/span&gt;&lt;span&gt;, props.className].&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean).&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; &apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        {&lt;/span&gt;&lt;span&gt;props.asChild&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          ?&lt;/span&gt;&lt;span&gt; isValidElement&lt;/span&gt;&lt;span&gt;(children)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ?&lt;/span&gt;&lt;span&gt; cloneElement&lt;/span&gt;&lt;span&gt;(Children.&lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;(children &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; ReactElement&lt;/span&gt;&lt;span&gt;), {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ref: forwardedRef,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                children: &lt;/span&gt;&lt;span&gt;wrapChildren&lt;/span&gt;&lt;span&gt;(children.props.children),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            :&lt;/span&gt;&lt;span&gt; children&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          :&lt;/span&gt;&lt;span&gt; wrapChildren&lt;/span&gt;&lt;span&gt;(children)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;Primitive.button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;code&gt;Button&lt;/code&gt;コンポーネントのテストケース&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; renderer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react-test-renderer&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { describe, expect, it } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;vitest&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Button } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./button&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;empty&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;).&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className=&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          className=&quot;ButtonLabel&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;asChild&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;).&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;null&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;string&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;string&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;).&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className=&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          className=&quot;ButtonLabel&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          string&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;asChild&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;string&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;).&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;null&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;single element&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;single&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className=&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          className=&quot;ButtonLabel&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            href=&quot;/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            single&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;asChild&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;single&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className=&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        href=&quot;/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          className=&quot;ButtonLabel&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          single&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;mixed&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;default&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; tree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; renderer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;multi&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;-&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;ple&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      .&lt;/span&gt;&lt;span&gt;toJSON&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(tree).&lt;/span&gt;&lt;span&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;button&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className=&quot;Button&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          className=&quot;ButtonLabel&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            multi&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          -&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            href=&quot;/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ple&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/span&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/button&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  it&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;asChild&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    expect&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      renderer.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;multi&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;-&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;ple&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ).&lt;/span&gt;&lt;span&gt;toThrowError&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これはあまり現実的な実装方針ではないし、そもそもインターフェースとしてあまり直感的ではないと思う。&lt;/p&gt;
&lt;p&gt;もっともシンプルな解決策としては、内側に入れ子になったコンポーネントを別コンポーネントとして切り出してしまうことだろう。そして、利用時にはそれら二つのコンポーネントを組み合わせて使うという規約を設けておく。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonLabel&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;ButtonLabel&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; asChild&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;		&amp;lt;&lt;/span&gt;&lt;span&gt;ButtonLabel&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;ButtonLabel&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;実際のところ、Radix UIにおいてはこのような設計方針が採用されている。しかし、Radix UIのようなプリミティブなコンポーネントライブラリと違って、一般的なコンポーネントライブラリとしては、これでは使い勝手が悪い場合があるだろう。&lt;/p&gt;
&lt;h2&gt;Ariakitにおける&lt;code&gt;render&lt;/code&gt; prop&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ariakit.org/&quot;&gt;Ariakit&lt;/a&gt;では、レンダリングする要素を&lt;code&gt;render&lt;/code&gt; propを介して変更できるように設計されている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Combobox&lt;/span&gt;&lt;span&gt; render&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;span&gt; rows&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://ariakit.org/guide/composition#changing-the-html-element&quot;&gt;Composition - Ariakit&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;興味深いのは、その&lt;code&gt;render&lt;/code&gt; propに渡された要素に対して、内部的に管理されたpropsなどが暗黙的に渡される仕組みになっていること。たとえばあるコンポーネントに対して、&lt;code&gt;render&lt;/code&gt; propと&lt;code&gt;children&lt;/code&gt; propを同時に指定すると、&lt;code&gt;render&lt;/code&gt; propに指定した要素に対してその&lt;code&gt;children&lt;/code&gt; propが渡される仕組みになっている。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Tab&lt;/span&gt;&lt;span&gt; render&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/new&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;New&amp;lt;/&lt;/span&gt;&lt;span&gt;Tab&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://ariakit.org/guide/composition#composing-with-custom-components&quot;&gt;Composition - Ariakit&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これは次のような結果になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/new&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;New&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;先ほどの&lt;code&gt;asChild&lt;/code&gt; propにおいては、コンポーネントの内側に別の要素が入れ子になっている場合の表現の難しさが問題になった。しかし、このAriakitの&lt;code&gt;render&lt;/code&gt; propのアイデアを借用すれば、比較的直感的なインターフェースとシンプルな実装を実現することもできる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { cloneElement, ComponentProps, forwardRef, ReactElement, ReactNode } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; ButtonProps&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; ReactElement&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; ComponentProps&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;button&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Button&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; forwardRef&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ButtonProps&lt;/span&gt;&lt;span&gt;&amp;gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ({ &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;forwardedRef&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; createElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ReactNode&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        cloneElement&lt;/span&gt;&lt;span&gt;(render, props, children)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;span&gt;props,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ref: forwardedRef,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        className: [&lt;/span&gt;&lt;span&gt;&apos;Button&apos;&lt;/span&gt;&lt;span&gt;, props.className].&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean).&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos; &apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ButtonLabel&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;render?.props.children &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; children&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; render&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; render&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;必要に応じて、&lt;a href=&quot;https://github.com/ariakit/ariakit/blob/845c3b2f0ddba4bf40286743ec6c12feccb3c02f/packages/ariakit-react-core/src/utils/system.tsx#L83-L110&quot;&gt;Ariakitの&lt;code&gt;createElement&lt;/code&gt;の実装も参照のこと&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;Base UIにおける&lt;code&gt;slots&lt;/code&gt; propと&lt;code&gt;slotProps&lt;/code&gt; prop&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mui.com/base/getting-started/overview/&quot;&gt;Base UI&lt;/a&gt;では、&lt;a href=&quot;https://mui.com/base/guides/overriding-component-structure/&quot;&gt;コンポーネントの内部の構造まで上書きすることができる&lt;code&gt;slots&lt;/code&gt; propおよび&lt;code&gt;slotProps&lt;/code&gt; propが提供されている&lt;/a&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Select&lt;/span&gt;&lt;span&gt; slots&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ listbox: &lt;/span&gt;&lt;span&gt;&apos;ol&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;First option&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;First option&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;First option&amp;lt;/&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt; value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Second option&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Second option&amp;lt;/&lt;/span&gt;&lt;span&gt;Option&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Select&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://mui.com/base/guides/overriding-component-structure/#the-slots-prop&quot;&gt;Overriding component structure - Base
UI&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Badge&lt;/span&gt;&lt;span&gt; slotProps&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;{ badge: { className: &lt;/span&gt;&lt;span&gt;&apos;my-badge&apos;&lt;/span&gt;&lt;span&gt; } }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://mui.com/base/guides/overriding-component-structure/#the-slotprops-prop&quot;&gt;Overriding component structure - Base
UI&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;as&lt;/code&gt; propではルートのコンポーネントしか変更できなかったのに対して、Base UIの&lt;code&gt;slots&lt;/code&gt; propでは、そのコンポーネントの内部のサブコンポーネントまで変更することができる。&lt;code&gt;slotProps&lt;/code&gt; propを使えば、ルートおよびサブコンポーネントのpropsを変更できる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;asChild&lt;/code&gt; propやAriakitの&lt;code&gt;render&lt;/code&gt; propをベースとした設計では、提供するコンポーネントは細かく分割する必要があり、それらを組み合わせて使うことがユーザー側の責務になる。一方、Base UIの&lt;code&gt;slots&lt;/code&gt; propおよび&lt;code&gt;slotProps&lt;/code&gt; propを前提とすると、提供するコンポーネント自体を細分化する必要はなくなるため、ユーザーが組み合わせて使う負荷は軽減される。&lt;/p&gt;
&lt;p&gt;強いて言うならば、前者の方が柔軟性があってシンプル、後者の方が簡潔に使えてイージー、という違いだろうか。まだBase UIについての考察が十分にできていないので、総括としては物足りないけれど、ひとまず今回はこんなところで。&lt;/p&gt;</content:encoded></item><item><title>Tailwind CSS実践入門——まず作ってから、あとで共通化する</title><link>https://gihyo.jp/magazine/wdpress/archive/2023/vol133</link><guid isPermaLink="true">https://gihyo.jp/magazine/wdpress/archive/2023/vol133</guid><pubDate>Fri, 24 Feb 2023 00:00:00 GMT</pubDate></item><item><title>肩の力を抜いてラフに書く、思いつくままに書く</title><link>https://yuheiy.com/2023-02-11-loose</link><guid isPermaLink="true">https://yuheiy.com/2023-02-11-loose</guid><description>根を詰めて取り組んでいた原稿がやっと手離れして、肩の荷が下りた。僕がこれまで経験して来た中で、最も重い執筆だった。</description><pubDate>Sat, 11 Feb 2023 08:20:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2023-02-06-tailwind-css-in-wdpress&quot;&gt;根を詰めて取り組んでいた原稿&lt;/a&gt;がやっと手離れして、肩の荷が下りた。僕がこれまで経験して来た中で、最も重い執筆だった。&lt;/p&gt;
&lt;p&gt;僕が文章を書くスピードにはかなりのムラがある。1日で1万文字書けることもあれば、100文字とか200文字くらいしか書けないこともある。前者の場合はあまり悩まずにスラスラ書けている時で、後者の場合は悩みに悩んで絞り出すように書いている時だ。今回はかなり後者寄りだった。&lt;/p&gt;
&lt;p&gt;今回がいつもと違ったのは、かなりガチガチに構成を考えて書いたこと。規定のページ数ぴったりに収まるように、書くべきこととそうでないことを細かく選り分けて、それらが自然に理解できるように話の順序を何度も再検討して、重複したり無駄があるところ、抜け漏れが出ないようにも意識しながら、また説明の程度の濃淡も偏らないように調整しつつ書いていた。&lt;/p&gt;
&lt;p&gt;ブログと大きく違うのは、紙には強い「清書感」があることだと思う。ブログのように、あとから気軽に修正したり、別の記事として書き直したり、といったことが実質的にできない。やり直しが効かない中で、この一発にすべてを賭けるような意識で、完成されたものを作り上げなければならない。もっとも、経験を重ねればまた別の感覚を覚えるようになるかもしれないけれど。&lt;/p&gt;
&lt;p&gt;ここで書くときにはそのような緊張感はない。思いつくままに気楽に書けばいい。そういう思いでずっとやってきた。&lt;/p&gt;
&lt;p&gt;僕がブログを始めたのは、今から15年以上前の小学生のころだった。当初はなにか書きたいことがあったわけではないが、すでに下火になりつつあったブログブームに影響されて、とにかくなんでもいいのでブログを書くことをやってみたかった。また、簡単なホームページ的なものも作っていたけど、なにかコンテンツになるものがないとどうしようもないと悩みあぐねているときでもあった。&lt;/p&gt;
&lt;p&gt;ブログを立ち上げて以来、僕は書けるネタをずっと探し続けることになった。そして捻り出すようにしてなにかしらのブログを投稿した。日々の些細な出来事について書いたり、当時作っていたホームページの話をしたり、ある時は2ちゃんねるのまとめブログを作ったり、ニュース記事まとめサイトみたいなものを運営したりもしていた。&lt;/p&gt;
&lt;p&gt;そうこうしているうちに、僕は高校を卒業してウェブデザインの学校に進学した。そこでいろいろと専門的らしいことを勉強して、少しおもしろみのあることを書けるようになってきた。いろいろな人から読んでもらえるようにもなってきた。それくらいの時期から、文章を書くことが自分にとってのアイデンティティであるとも自覚し始めた。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://standard.shiftbrain.com/&quot;&gt;スタンダードデザインユニットのサイト&lt;/a&gt;を始めたことをきっかけに、これまでよりもきちんとした文章を書かなければならないと考え始めた。個人ではなく会社として発信するためには、それなりの責任が伴う。文章の書き方について真面目に勉強し始めたのがそのころだった。&lt;/p&gt;
&lt;p&gt;そうした経験を経て、よい文章を書くための技量は上がったように思う。その分、あえて書かないことも増えてきた。なにか書くにしても、筆の進みがどんどん慎重になってきた。そういう傾向を感じる。切り捨てられるものが増えて来たということ。&lt;/p&gt;
&lt;p&gt;硬直化している気がする。もっと気軽に書きたいし、そうすべきだとも思う。昔のように、あるいはそれよりもっと、軽やかに。僕はその楽しさに惹かれていたはずだ。&lt;/p&gt;</content:encoded></item><item><title>WEB+DB PRESS Vol.133に特集「Tailwind CSS実践入門——まず作ってから、あとで共通化する」を寄稿しました #wdpress</title><link>https://yuheiy.com/2023-02-06-tailwind-css-in-wdpress</link><guid isPermaLink="true">https://yuheiy.com/2023-02-06-tailwind-css-in-wdpress</guid><description>後日追記: 寄稿した特集がgihyo.jpにて全章無料公開されました。</description><pubDate>Mon, 06 Feb 2023 04:30:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;後日追記:&lt;/strong&gt; 寄稿した特集がgihyo.jpにて全章無料公開されました。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gihyo.jp/list/group/Tailwind-CSS%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%EF%BD%9E%E3%81%BE%E3%81%9A%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%8B%E3%82%89-%E3%81%82%E3%81%A8%E3%81%A7%E5%85%B1%E9%80%9A%E5%8C%96%E3%81%99%E3%82%8B&quot;&gt;Tailwind CSS実践入門 ～まず作ってから、あとで共通化する 記事一覧 | gihyo.jp&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;2023年2月24日発売の「&lt;a href=&quot;https://gihyo.jp/magazine/wdpress/archive/2023/vol133&quot;&gt;WEB+DB PRESS Vol.133&lt;/a&gt;」に特集「Tailwind CSS実践入門——まず作ってから、あとで共通化する」を寄稿しました。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;WEB+DB PRESS Vol.133、どこよりも早い表紙画像です！&lt;br /&gt;TypeScript最新活用、速習Ruby 3.2、Tailwind CSS実践入門を大特集！2月24日発売です！&lt;a href=&quot;https://twitter.com/hashtag/wdpress?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#wdpress&lt;/a&gt; &lt;a href=&quot;https://t.co/TaHmqI7eyq&quot;&gt;pic.twitter.com/TaHmqI7eyq&lt;/a&gt;&lt;/p&gt;— WEB+DB PRESS編集部 (@wdpress) &lt;a href=&quot;https://twitter.com/wdpress/status/1622416916987129857?ref_src=twsrc%5Etfw&quot;&gt;February 6, 2023&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;この特集では、すでにCSSの基本的な使い方は身につけているけどTailwind CSSについては知らない人、これから使ってみたい人、あるいはCSSの設計について改めて考えてみたいという人に向けて、その意義や使い方を解説しています。なぜTailwind CSSなのか、なぜTailwind CSSでなければならないかを解き明かすべく、ユーティリティファーストというTailwind CSSのコンセプトを中心に据えて、「まず作ってから、あとで共通化する」という考え方を掘り下げて書いています。&lt;/p&gt;
&lt;p&gt;構成は次のようになっています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第1章: ユーティリティファーストとは何か——従来のやり方の問題点を解決するためのアプローチ&lt;/li&gt;
&lt;li&gt;第2章: Tailwind CSSの導入——開発環境へのインストール、設定ファイルの記述、エディタの設定&lt;/li&gt;
&lt;li&gt;第3章: Tailwind CSSの使い方——ユーティリティクラス、修飾子、カスタムスタイル&lt;/li&gt;
&lt;li&gt;第4章: Tailwind CSSとデザイン——より良いデザインのためにCSSはどうあるべきか&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;まず第1章では基礎編として、Tailwind CSSを使うことのメリットや、ユーティリティファーストという基本的なコンセプト、従来のやり方の問題を解説します。第2章ではより具体的に、開発環境へのインストール方法、設定ファイルの記述方法、エディタの設定を紹介します。続いて第3章では、ユーティリティクラスやその修飾子の使い方、カスタムスタイルの実装方法を紹介します。最後に第4章では発展編として、デザインとCSSの関係性や、CSS設計の新しいあり方を考察します。&lt;/p&gt;
&lt;p&gt;少し前に、ブログ「&lt;a href=&quot;/2022-06-11-css-components&quot;&gt;CSS設計における、すべてがコンポーネントであるという誤謬&lt;/a&gt;」を書きましたが、それはこの特集のプロトタイピングのためでした。CSS設計について僕が抱いている問題意識をまとめたつもりでしたが、ブログに対するさまざまな反応を見るに、言いたいことはあまりよく伝わっていないようでした。&lt;/p&gt;
&lt;p&gt;そこでこの特集においては、その問題意識をもとにしつつ、必要となる前提知識からしっかりとていねいに説明したり、議論の幅を広げたりしながら、要点やその伝え方を徹底的に整理し直しました。&lt;/p&gt;
&lt;p&gt;その結果、Tailwind CSSを使う意味について、ほかにどこにもないような見方で解説する記事ができあがったと思います。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;企画の発端は、CSSの特集を書かないかという誘いをいただいたことです。ユーティリティファーストCSSについて書いてほしいという提案もあったので、当時いろいろと考えていたことを整理してまとめることにしました。しかしそれは、僕にとってはけっこうな挑戦でした。問題は、文章の量と、取り扱うテーマの複雑さです。&lt;/p&gt;
&lt;p&gt;この特集は全30ページで、最終原稿の文字数はおよそ4万文字です。長らくブログを書いてきてはいるものの、一本のブログがこれだけの分量になることは多くありません。&lt;/p&gt;
&lt;p&gt;文章の量や質が変わると、同じような書き方は通用しなくなります。短くて簡単な記事であれば、とりあえず本文を書き進めながら内容を考えていくということができます。書き終わってから気になるところがあっても、また頭に戻って書き直すことを繰り返していればなんとかなります。ところが、量が増えたり、話が複雑になってくると、それだけでは収拾がつかなくなってきます。要するに、本文を書きながら考えているだけでは、頭の中で話が整理しきれないのです。&lt;/p&gt;
&lt;p&gt;僕は基本的に、1テーマ思いつくごとに一本ブログを書くというやり方を取っています。単純に、それが簡単で書きやすいからです。しかしこの特集では、これまで蓄積してきたさまざまな考えを一挙に語る必要がありました。できるだけ網羅的で、抜け目のない議論にすることが重要だと考えたからです。加えて、入門者にも向けた特集である以上、基本的な使い方についてもカバーしなければいけません。そのため、それらをいかにして一つのつながりのある文章として成立させるかが難しいところでした。&lt;/p&gt;
&lt;p&gt;Tailwind CSSについて語るには、いくつもの論点があります。普段であれば、そういったものは箇条書きにしたりアウトライナーに書き出したりして、自分の頭の中を整理しています。執筆当初もそのように試みましたが、今回はそれがうまくいきませんでした。なぜなら、それぞれの論点がかなり複雑にリンクし合っていて、いきなりリニアな構造として形にするには無理があったからです。とりあえず書き出してみることはできても、それはあまり理解の助けになりませんでした。&lt;/p&gt;
&lt;p&gt;そこで、今回は&lt;a href=&quot;https://heptabase.com/&quot;&gt;Heptabase&lt;/a&gt;というツールを使いました。Heptabaseでは、トピックごとにカードを作って内容を記述し、それらを二次元の座標系に並べることができます。&lt;a href=&quot;https://miro.com/ja/&quot;&gt;Miro&lt;/a&gt;や&lt;a href=&quot;https://www.figma.com/ja/figjam/&quot;&gt;FigJam&lt;/a&gt;にも似ていますが、テキストベースの情報を整理するにはHeptabaseが向いています。&lt;/p&gt;
&lt;p&gt;これを使って、頭の中にある考えを一つ一つアウトプットしました。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/heptabase.C9QkgsOk_ueCRL.webp&quot; alt=&quot;小さく表示されたカードが情報の関連性ごとに近接して配置されている。カード同士が線で繋がれたりもしている&quot; title=&quot;Heptabaseのスクリーンショット&quot; loading=&quot;lazy&quot; width=&quot;2182&quot; height=&quot;1415&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;思考の断片をこのように可視化することで、自分の考えの全貌が少しずつ見えてきました。頭の中から一度に取り出せる情報はごく限られていますが、少しずつ外部化していくことによって、全体のつながりが明らかになってきました。&lt;/p&gt;
&lt;p&gt;そして、これらをもとにして記事の構成案を作成します。目次のようなものです。ここで初めてリニアな構造を意識し始めるわけですが、Heptabaseのおかげで話の種はできており、あとはその並べ方を考えるだけだったので、大きくは苦労せずに済みました。&lt;/p&gt;
&lt;p&gt;これは例えるならば、食材を用意することと料理を作ることに似ています。まずはHeptabaseで食材を用意して、用意が終わったあとから構成案という料理を作っているのです。もし食材の用意なしにいきなり料理を作り始めていれば、料理をする片手間で食材の用意もしなければならなくなるため、どちらの仕事も疎かになってしまうでしょう。&lt;/p&gt;
&lt;p&gt;とはいえ、実際の作業が手戻りなく進んだかというと、もちろんそういうわけでもありません。実際に本文を書き進めてみてから、当初の構成案ではうまくいかないということがわかり、引き返して手を入れることは何度か行いました。またそれに加えて、Heptabaseにだいたいのことは書き切ったと思っていても、文章の流れに促されて、想定していなかったことを新たに書く必要に迫られる場面も度々ありました。そもそも文章を書くという行為は、事前に予測不可能な発想を創発させるプロセスなのだと思います。それでも、下準備をするのとしないのでは大きな差がありました。&lt;/p&gt;
&lt;p&gt;全体としては非常に苦労しました。最初に想定していたよりもずっと長い時間がかかってしまいました。ほかにもこのような仕事をされている方々に対しては頭の下がる思いです。&lt;/p&gt;
&lt;p&gt;けれど最終的には、なんとかそれなりのものになりました。CSSについて考える人にとって、この特集がなにかしらのヒントなればうれしく思います。&lt;/p&gt;</content:encoded></item><item><title>ウルトラブースト日記</title><link>https://yuheiy.com/2022-12-03-ultraboost</link><guid isPermaLink="true">https://yuheiy.com/2022-12-03-ultraboost</guid><description>僕は右利きだが、スマホはいつも左手で使う。だから、スマホに入ったPASMOを改札機にタッチするときは毎回フラストレーションを覚える。</description><pubDate>Fri, 02 Dec 2022 15:30:00 GMT</pubDate><content:encoded>&lt;p&gt;僕は右利きだが、スマホはいつも左手で使う。だから、スマホに入ったPASMOを改札機にタッチするときは毎回フラストレーションを覚える。&lt;/p&gt;
&lt;p&gt;少し仕事の余裕ができたので、久しぶりに外に出かける。渋谷に向かうため電車に乗ると、気分が少し鬱々としてきた。そういえば僕は、電車に乗っている時間があまり好きではなかった。そんなことも忘れていた。&lt;/p&gt;
&lt;p&gt;adidasのスニーカーを買いに来た。ウルトラブーストというランニングシューズが狙い。ランニングを始めるわけではなく、普段履きとして使うためだ。&lt;/p&gt;
&lt;p&gt;僕は以前、ひどくスニーカーにハマっていて、今となってはちょっとどうかしていると思うほどの数を買っていた。しかしその大半はもう手放してしまった。気持ちの赴くままに手をつけていたけど、いつまでも履き続けたいと思えるものはほとんどなかったのだ。その時の気分に任せて靴を買っても、僕の気分の方はすぐに変わってしまう。でもその一方で、履き心地が好きだと思える靴は変わらなかった。当然と言えば当然かもしれない。&lt;/p&gt;
&lt;p&gt;最近になって、ウルトラブーストというやつはすこぶる履き心地がよいという評判を目にした。そのせいで、ふたたび気持ちが昂ってしまった。見た目としても割と好きな形だった。どうやら僕はランニング用っぽい形が好きらしく、これまでもそういうものばかり買ってきた。よくあるバッシュ系は一つも買ったことがない。&lt;/p&gt;
&lt;p&gt;店にいるスタッフは全員サッカーのユニフォームを着ていた。それがなぜなのかがしばらくわからなかった。&lt;/p&gt;
&lt;p&gt;ウルトラブーストを見つけたので、いざ履いてみる。たしかにいい感じがする。普通の靴と大きく違うのが、アッパー全体がニット生地になっていて、大袈裟に言えば靴下みたいに足にフィットするところ。ニット生地の靴はたまに見かけるけど、総じて独特のフィット感がある。快適なものも多いけど、このモデルはちょっと着圧が強すぎる。ランニング用だからだろうけど、一日ずっと履くにはつらいだろう。&lt;/p&gt;
&lt;p&gt;残念ながら今回は断念かと思いかけたところ、近くにあった、ウルトラブースト DNAという別シリーズが目に入った。ライフスタイル向けのウルトラブーストということらしい。手に取って調べてみると、アッパーが普通の靴とニット系の合いの子のような作りになっていた。このおかげで、強い着圧に頼らずに足をホールドできる。というのも、純粋なニットとして足をホールドしようとすると、立体的な構造として足型を包み込むことができなくて、着圧に頼らざるを得なくなるからだ。かれこれ一時間近く悩んでから、意を決してこれに決めた。&lt;/p&gt;
&lt;p&gt;ウルトラブースト以外のスニーカーも眺めてみて、「ああ、これ欲しいな」と思うこともあったけど、もちろん買わなかった。それは自分が買うべきものではないとわかっているので。今になって考えてみると、気になったスニーカーがあっても別に買わなくてもいい。店に飾られているものを眺めているだけで十分楽しめるから。というか、むしろその時間が気持ちの一番のピークなのだと思う。それに、自分の狭い家に飾って満足するよりも、暇があるときにいろんな店を巡ってたくさんのスニーカーを見た方が多幸感がある。&lt;/p&gt;
&lt;p&gt;その後、帰路にある「いきなり！ステーキ」に入った。この数年で、出かけた帰りには立ち寄るのが当たり前になった。普通に出社する生活になればこの習慣もなくなるかもしれない。&lt;/p&gt;
&lt;p&gt;家に着いてから、改めて履いて外を歩いてみた。家に帰ってから冷静になると、「なんか違う」となることがたまにあるけど、幸い今回はそうならなかった。足と一体化して、肌の一部になったと錯覚するほどよくフィットするし、足の運びが本当に軽くなった。これからも長く履いていけそうなので満足している。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/1.DX8jCOC-_19qB63.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1440&quot; /&gt;&lt;/figure&gt;</content:encoded></item><item><title>ブロックエディタ用のCSSを参考にした、よりよいコンテナの実装</title><link>https://yuheiy.com/2022-11-27-better-container</link><guid isPermaLink="true">https://yuheiy.com/2022-11-27-better-container</guid><description>以前、「本文エリア内の要素をpaddingのないコンテナとして実装する」という記事を書いた。本文エリア内の要素をすべて同じ幅でレイアウトするのではなく、ものによっては少し広めにしたりページの最大幅まで広げたりしたいという場合に、どのように実装すべきかという話。</description><pubDate>Sun, 27 Nov 2022 07:05:00 GMT</pubDate><content:encoded>&lt;p&gt;以前、「&lt;a href=&quot;/2022-07-31-container-with-no-padding&quot;&gt;本文エリア内の要素をpaddingのないコンテナとして実装する&lt;/a&gt;」という記事を書いた。本文エリア内の要素をすべて同じ幅でレイアウトするのではなく、ものによっては少し広めにしたりページの最大幅まで広げたりしたいという場合に、どのように実装すべきかという話。&lt;/p&gt;
&lt;p&gt;その後、「&lt;a href=&quot;https://qiita.com/TetsuakiHamano/items/4166dd093e2ba03c57d7&quot;&gt;【WordPress】 受託開発におけるハイブリッドテーマ開発&lt;/a&gt;」という記事を読んで、ここで紹介されているやり方の方がシンプルでよさげに見えたのでメモしておく。&lt;/p&gt;
&lt;p&gt;具体的には、次のような実装になる。前述の記事からの抜粋。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/* 幅広・全幅ブロック以外にデフォルトコンテンツ幅を指定 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.alignwide&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.alignfull&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--style--global--content-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* 幅広ブロックに幅広コンテンツ幅を指定 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;.alignwide&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--style--global--wide-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* 全幅ブロックを除いて左右にオートマージンを付与する */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.alignfull&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ここまでは以前の記事で紹介したやり方とあまり変わらない。しかし、コンテナの外側にできる余白の実装が少し違う。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--gutter&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--gutter&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;.alignfull&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--gutter&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--gutter&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最大幅の実装は個々の要素が担いつつ、外側の余白は親要素に持たせておくようになっている。そして、ページいっぱいまでに広がる要素（alignfull）は例外として、ネガティブマージンで処理されている。&lt;/p&gt;
&lt;p&gt;参考として&lt;a href=&quot;https://github.com/WordPress/wordpress-develop/tree/trunk/src/wp-content/themes/twentytwentytwo&quot;&gt;Twenty Twenty-Twoの実装&lt;/a&gt;を調べてみると、&lt;a href=&quot;https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-content/themes/twentytwentytwo/style.css&quot;&gt;style.css&lt;/a&gt;に同様の記述があった。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .is-root-container&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.edit-post-visual-editor__post-title-wrapper&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-block-group.alignfull&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-block-group.has-background&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-block-cover.alignfull&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.is-root-container&lt;/span&gt;&lt;span&gt; .wp-block&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-align&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;full&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-group&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.is-root-container&lt;/span&gt;&lt;span&gt; .wp-block&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-align&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;full&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-cover&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--spacing--outer&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--spacing--outer&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt; .alignfull&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-group.has-background&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-cover&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-template-part&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-group.has-background&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wp-site-blocks&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-template-part&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-cover&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .is-root-container&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-cover&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .is-root-container&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-template-part&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-group.has-background&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .is-root-container&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-template-part&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wp-block-cover&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.is-root-container&lt;/span&gt;&lt;span&gt; .wp-block&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-align&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;full&apos;&lt;/span&gt;&lt;span&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--spacing--outer&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wp--custom--spacing--outer&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;unset&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://qiita.com/TetsuakiHamano/items/4c1576131a71fc41ef41#settingsuserootpaddingawarealignments&quot;&gt;&lt;code&gt;settings.useRootPaddingAwareAlignments&lt;/code&gt;についての解説&lt;/a&gt;も参考になった。&lt;/p&gt;
&lt;p&gt;これらの例はWordPress向けの実装になっているので、少し手を加えて、単独で実装パターンとして成立するように整理した。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;entry-content&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- コンテンツ幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- コンテンツ幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;Lorem ipsum dolor sit amet.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ワイド幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alignwide&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- フル幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alignfull&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --spacing-outer&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;vw&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --content-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --wide-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing-outer&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing-outer&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.alignwide&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.alignfull&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--content-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .alignwide&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--wide-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.entry-content&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .alignfull&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing-outer&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing-outer&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;参考までに、Tailwind CSSっぽい疑似コードも示しておく。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;px-outer&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- コンテンツ幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;max-w-content mx-auto&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- コンテンツ幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;max-w-content mx-auto&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Lorem ipsum dolor sit amet.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- ワイド幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;max-w-wide mx-auto&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- フル幅 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;-mx-outer&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>マークアップのわかり方</title><link>https://yuheiy.com/2022-11-03-how-to-understand-the-markup</link><guid isPermaLink="true">https://yuheiy.com/2022-11-03-how-to-understand-the-markup</guid><description>この記事は、2022年10月28日に開催されたDIST.37「マークアップな夜」での発表「マークアップのわかり方」をもとにしたものです。当日は話せなかった内容も大幅に追加しています。</description><pubDate>Thu, 03 Nov 2022 04:50:00 GMT</pubDate><content:encoded>&lt;p&gt;この記事は、2022年10月28日に開催された&lt;a href=&quot;https://dist.connpass.com/event/261960/&quot;&gt;DIST.37「マークアップな夜」&lt;/a&gt;での発表「マークアップのわかり方」をもとにしたものです。当日は話せなかった内容も大幅に追加しています。&lt;/p&gt;
&lt;hr /&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/001.DZlSHxq-_biYhX.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;現代における「マークアップ」とはどのような行為なのか。いかにそれと向き合っていけばいいのか。そういったことについて考えてみます。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/002.DqsinsdU_Z1E6OR0.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;マークアップの議論においては、「マークアップには正解がない」という意見が決まって出ます。正解がないと言うならば、たいていなんであってもそうです。たとえばCSSやJavaScriptの書き方には「正解」があるのかと考えてみると、必ずしもそうではありません。&lt;/p&gt;
&lt;p&gt;しかし、ことさらマークアップにおいてこれがよく言われる理由としては、妥当性を判断する基準がわからない、ということでしょう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/003.6YfW5syA_FRelF.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;というのも、CSSなら望む通りの見た目になればとりあえずOKだし、JavaScriptでも意図した通りの振る舞いをすればまあいいだろう、という判断ができます。けれどHTMLでは、&lt;code&gt;div&lt;/code&gt;を使おうが&lt;code&gt;button&lt;/code&gt;を使おうが、何が変わるのかがその場でわからなくてどうしようもないのかもしれません。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/004.KsgOAEnV_2tT50y.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://validator.w3.org/nu/?doc=https%3A%2F%2Fdist.connpass.com%2Fevent%2F261960%2F&quot;&gt;Showing results for https://dist.connpass.com/event/261960/ - Nu Html
Checker&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;HTMLにはチェッカーがありますから、もし構文エラーをしていたり、変な属性を使っていたりすれば、チェッカーから発される警告によってそのことに気づけるようになっています。それでも、すべての間違いをチェッカーで指摘できるわけではありません。&lt;/p&gt;
&lt;p&gt;あくまでチェッカーでわかるのは、型通りに判断できるような、明確に間違いであると言える部分についてだけです。HTML仕様の定義と照らし合わせたときに、アリかナシかをはっきり言い切れる部分についてだけチェックしている、ということです。&lt;/p&gt;
&lt;p&gt;一方でマークアップにおいては、非常に文脈的な解釈が必要になる場面があります。HTMLの性質からして、定義の幅を狭く固定するということがなされていないので、状況に応じてその場その場の個別解を導き出すことが重要になります。したがって、個々人の考えをもって正解を決めるしかないのです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/005.CSjWKBn__1Ez9qq.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;一つ例を挙げます。私の家の近所にはインドカレー屋がありますが、その周りにはいくつか立て看板のようなものがあります。そこで、店自体をウェブサイトとして、看板はウェブページとして捉えてみると、マークアップにおいてはどのように考えて判断できるでしょうか。&lt;/p&gt;
&lt;p&gt;最初にマークアップすべきは、看板右上にあるこの店のロゴです。明らかにこれがサイトのシンボルだと思います。ウェブサイトで言うところのヘッダーなので&lt;code&gt;header&lt;/code&gt;としつつ、その中に入るロゴは画像なので&lt;code&gt;img&lt;/code&gt;です。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/006.Cz1iEu1D_Z1KyD2J.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;logo.svg&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;インド料理 ハティ&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一つポイントになるのが、その&lt;code&gt;alt&lt;/code&gt;の値です。ロゴとしては「HATI INDIAN RESTAURANT」と表記されていますが、&lt;code&gt;alt&lt;/code&gt;は店名として正式な表記であろう「インド料理 ハティ」とします。&lt;code&gt;alt&lt;/code&gt;の書き方についてはいくつかの考え方がありますが、基本としては、その画像をそのままテキストに置き換えてもおかしくないようにしておくのがセオリーです。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;alt&lt;/code&gt;とは代替を意味しますが、ロゴにあるのと同じ文字列に置き換えることが必ずしも代替なのではありません。そもそもは、店名の正式な表記が先にあってからロゴが後からついてくる、という主従関係になっているはずです。逆説的に言えば、むしろロゴの方が代替です。&lt;/p&gt;
&lt;p&gt;続いて看板の左上には、大きな文字で「Take Out Menu」「お弁当メニュー」とあります。ここからはページ固有の要素と言えるでしょう。外側は&lt;code&gt;main&lt;/code&gt;で囲いつつ、「Take Out Menu」は&lt;code&gt;h1&lt;/code&gt;にします。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/007.Cifut8mI_XRa3e.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;hgroup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Take Out Menu&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;お弁当メニュー&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;hgroup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;「Take Out Menu」の下には「お弁当メニュー」の文字があります。日本語訳を併記しているようです。これらの二行はまとめて&lt;code&gt;hgroup&lt;/code&gt;にするのがよいでしょう。場合によっては、両方のテキストを&lt;code&gt;h1&lt;/code&gt;の中に含めてしまうのもアリですが、レイアウトの都合を考えるとこちらがベターです。&lt;/p&gt;
&lt;p&gt;それらに続いて「インド料理のお弁当」という見出しがあり、お弁当の種類がカード状に並んでいます。カードが六つあるので、これらを&lt;code&gt;ul&lt;/code&gt;でまとめてしまうのがよさそうに見えます。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/008.OAeRRQ0U_gwc6Y.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;A カレーライス弁当&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;indian-lunchbox-a.jpg&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 480&amp;lt;/&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt; &amp;lt;&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 500&amp;lt;/&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;A 弁当 &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;A Lunch Box&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;カレー1種・ライス&amp;lt;&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;・ナン&amp;lt;/&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;1 Curry, Rice &amp;lt;&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;or Nan&amp;lt;/&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ところが最後の六つ目のカードを見ると、これだけ少し違った内容になっています。それ以外のカードは、「カレーライス弁当」「インド弁当」「ナン付きカレーライス弁当」という風になっているのに、六つ目のカードだけ、「ナンお持ち帰り」「ライス・麺の大盛り」とあります。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/009.Df7yzqMi_Z10sW5p.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;これらは情報の種類としては別物なので、別の項目に分けて扱いたいところです。同じ項目としてまとめられていると、弁当が六種類あるように勘違いしてしまうからです。&lt;/p&gt;
&lt;p&gt;そこでマークアップとしては、六つ目のカードだけ別のリストにできるとよさそうです。しかしそうすると、CSSでレイアウトするのが難しくなってしまうかもしれません。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;indian-other.jpg&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;ナンお持帰り&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Nan Take Out&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 280&amp;lt;/&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 300&amp;lt;/&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ならば、&lt;code&gt;display: contents&lt;/code&gt;を使うのもアリです。こうすれば、レイアウト的には&lt;code&gt;ul&lt;/code&gt;の存在が無視されて、その中の&lt;code&gt;li&lt;/code&gt;が直接の子要素と同等に扱われるようになります。これでCSSとしても問題ないでしょう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;grid&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;display: contents&quot;&lt;/span&gt;&lt;span&gt; role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;list&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;indian-other.jpg&quot;&lt;/span&gt;&lt;span&gt; alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;ナンお持帰り&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Nan Take Out&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 280&amp;lt;/&lt;/span&gt;&lt;span&gt;del&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;¥ 300&amp;lt;/&lt;/span&gt;&lt;span&gt;ins&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;注意点としては、&lt;code&gt;ul&lt;/code&gt;には&lt;code&gt;display: contents&lt;/code&gt;と同時に&lt;code&gt;role=list&lt;/code&gt;が指定されていることです。&lt;code&gt;role&lt;/code&gt;というのは、その要素がどのような役割を持つのかをスクリーンリーダーの支援技術などに対して明示するための属性です。これはHTMLとは別に、WAI-ARIAと呼ばれる仕様として定義されているものです。&lt;code&gt;ul&lt;/code&gt;の場合、&lt;code&gt;role&lt;/code&gt;属性を指定しなければ&lt;code&gt;list&lt;/code&gt;の役割を持つことに決まっています。&lt;/p&gt;
&lt;p&gt;では、なぜあえて&lt;code&gt;role=list&lt;/code&gt;と指定するのでしょうか。それは、現在の一部の環境においては、&lt;code&gt;display: contents&lt;/code&gt;を指定すると&lt;code&gt;list&lt;/code&gt;として扱われなくなってしまうという不具合があるからです。&lt;code&gt;ul&lt;/code&gt;が&lt;code&gt;list&lt;/code&gt;として扱われなくなると、スクリーンリーダーのユーザーなどにとって使い勝手が良くありません。そこで、明示的に&lt;code&gt;role=list&lt;/code&gt;と指定することでその問題を回避できるのです。&lt;/p&gt;
&lt;p&gt;というわけで、マークアップの際にはどういった風に考えているかという例を簡単に示してみました。それほど複雑なコンテンツではないものの、しかしながら、具体的な判断基準を言葉にしてみると、それなりにこみ入っていることがわかります。&lt;/p&gt;
&lt;p&gt;HTMLでは、それぞれの要素などをどう使うのかは一応定義されているものの、基本的には、それによってマークアップが強く規定されるというわけではありません。定義はあるものの、緩い定義になっていて、状況に応じて柔軟な使い方ができるような余白がある。そういうイメージです。&lt;/p&gt;
&lt;p&gt;だから、何か0か1かの判断を下せば自動的に答えが決まるわけではありません。考慮すべきさまざまな事情を踏まえて、時にはあちらを立てればこちらが立たずなジレンマを抱えながらも、それぞれの場面において最も理想的なバランスを見つけ出す。そうした姿勢が重要です。&lt;/p&gt;
&lt;p&gt;マークアップの際に考慮すべきことは、大きく捉えると次のような種類に分けられます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コンテンツの性質、制作者の狙い&lt;/li&gt;
&lt;li&gt;HTMLのセマンティクス&lt;/li&gt;
&lt;li&gt;WAI-ARIAのセマンティクス&lt;/li&gt;
&lt;li&gt;ブラウザ・OS・スクリーンリーダーでのサポート状況、ユーザーにとっての使い勝手&lt;/li&gt;
&lt;li&gt;CSSやJavaScriptなど周辺技術との兼ね合い、連携のしやすさ&lt;/li&gt;
&lt;li&gt;ソースコードの運用性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;まず、マークアップの対象である情報構造は人の手から生まれるものですから、そこには何かしらの意図や狙いがあるはずです。それを、HTMLにおいてどのように表現すべきかを考えてマークアップする必要があります。&lt;/p&gt;
&lt;p&gt;関連するところとして、以前、HTMLの語彙を使って情報構造を考えるという発表をしたことがあります。そちらもご覧ください。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/010.C9DT763D_Zq5Mqx.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://yuheiy.github.io/html-for-creators/&quot;&gt;制作者のためのHTML&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;次に、HTMLのセマンティクスです。つまり、HTML仕様をよく理解した上でマークアップできているか、ということです。非常に基本的ではあるものの、簡単ではありません。仕様を素直に読み解くことは誰にでもできることではありません。&lt;/p&gt;
&lt;p&gt;最近では、HTML仕様をわかりやすく正確に解説した書籍『HTML解体新書』がありますので、まずはこちらを読んでいただくのがおすすめです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/011.CzgKBhXH_21zbJq.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;—
&lt;cite&gt;&lt;a href=&quot;https://www.borndigital.co.jp/book/25999.html&quot;&gt;HTML解体新書 | ボーンデジタル&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;続いて、WAI-ARIAのセマンティクスです。WAI-ARIAはHTMLと連携した仕様であり、スクリーンリーダーなどの支援技術に対して適切な情報伝達ができるようにすることを目的としています。&lt;/p&gt;
&lt;p&gt;ブラウザでHTML文書を表示すると、それがDOMツリーに変換された上でページが描画されます。その際同時に、DOMツリーをもとにしてアクセシビリティツリーと呼ばれる構造が生成されています。スクリーンリーダーのユーザーは、OSを経由してアクセシビリティツリーにアクセスすることによってページを利用しています。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/012.CRhgOvG2_Z22BEWL.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://wicg.github.io/aom/explainer.html&quot;&gt;Accessibility Object Model | aom&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;アクセシビリティツリーは、基本的にはWAI-ARIA仕様に則って生成されます。そのため、WAI-ARIAについてもよく理解しておくことが大切です。『HTML解体新書』では、WAI-ARIAについても解説されています。&lt;/p&gt;
&lt;p&gt;WAI-ARIAの基本的なところを理解した後には、WAI-ARIAのデザインパターンに目を通してみるとよいでしょう。W3C Web Accessibility Initiativeというグループによって、WAI-ARIAを使って一般的なUIコンポーネントを実装するためのパターンが公開されています。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/013.CXPO8Al6_ZAK07L.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/&quot;&gt;Patterns | APG | WAI | W3C&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;そして、ブラウザ・OS・スクリーンリーダーでのサポート状況、ユーザーにとっての使い勝手について。環境によって、HTMLの機能が実装されていたりされていなかったりすることがありますが、これはWAI-ARIAにおいても同じです。仕様としては存在していても、ブラウザに実装されていないから実質使えない、ということがあります。そういったことを調べられるツールとして、Can I use…のWAI-ARIA版のようなものがあります。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/014.Djh9dp7I_1Tor6y.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://a11ysupport.io/&quot;&gt;Accessibility Support&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これによって実装状況を確認した後には、実際にその環境を使って検証するようにしてください。自分でもスクリーンリーダーを使ってみることが大切です。&lt;/p&gt;
&lt;p&gt;そして、ある機能が実装されていたとしても、それがユーザーにとって使いやすいものになっているかどうかはまた別の問題です。さらに、個々の機能だけにかぎらず全体として、ユーザーは実際にどのような環境で操作しているのかを知っておくことは重要です。&lt;/p&gt;
&lt;p&gt;そこで、実際に支援技術を利用する障害者がどのようにウェブページを利用しているかを紹介する動画が総務省から公開されています。まずはこれを見ていただくのがよいでしょう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/015.DtKOVCBo_ZjjWrK.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.soumu.go.jp/menu_news/s-news/2005/051215_1_wmv.html&quot;&gt;総務省｜情報バリアフリー環境の整備｜障害者のウェブページ利用方法の紹介ビデオ&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;加えて、実際のユーザーの利用状況についての調査結果なども公開されています。こうしたものにも目を通しておくと参考にできることがあります。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/016.Biw8TkKz_TAs9s.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://jbict.net/survey/at-survey-02&quot;&gt;第2回支援技術利用状況調査報告書 |
日本視覚障害者ICTネットワーク&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;さて次に、CSSやJavaScriptなど周辺技術との兼ね合い、連携のしやすさです。HTMLの構造によっては、CSSが書きにくくなったり、JavaScriptでの操作が難しくなったりすることがあります。あるいは、CMSの機能やWYSIWYGからの出力、テンプレートエンジンやコンポーネントシステムの性質を踏まえて、それに適した実装方法を検討することもあるはずです。そうした考慮もマークアップの一環です。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/017.xN30Grne_jMkbI.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— 
&lt;cite&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=oe452WcY7fA&quot;&gt;Introducing WordPress 6.0 - YouTube&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;最後に、ソースコードの運用性です。たとえば、よかれと思って複雑な仕様を実装してしまった結果、メンテナンスできないほど厄介な状態になってしまうことは珍しくありません。また似たようなところでは、無理のある運用ルールを設定して誰にも守られなくなるということもあるでしょう。そうした問題を防ぐためには、ちょうどいい塩梅を探りながら開発を進めていかなければなりません。&lt;/p&gt;
&lt;p&gt;マークアップのアプローチとして一つ頭に入れておいてほしいことがあります。それは、たとえば何か複雑に見えるものがあるとき、必ずしもそれを複雑なまま実装する必要はなく、もっとシンプルな形にしてもよいということです。&lt;/p&gt;
&lt;p&gt;『インクルーシブHTML+CSS &amp;amp; JavaScript』では、いわゆるボタンリンクの実装方法について紹介されています。いわく、ボタンリンクと本当の&lt;code&gt;button&lt;/code&gt;は、同じ見た目にせずにそれぞれ別々にしてしまおう、という話です。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/018.CweHJg-2_Z72Vs3.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://www.borndigital.co.jp/book/6610.html&quot;&gt;電子書籍版インクルーシブHTML+CSS &amp;amp; JavaScript |
ボーンデジタル&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;a&lt;/code&gt;と&lt;code&gt;button&lt;/code&gt;は、どちらも押せるという意味では同じですが、細かく見ていくと微妙に振る舞いが異なります。にもかかわらず、それらを同じ見た目にして同じコンポーネントであるように扱ってしまうのはやや乱暴です。機能的に異なるものを同じように扱うことで、制作者自身が混乱してしまうこともあります。&lt;/p&gt;
&lt;p&gt;その解決策はシンプルです。別物なのだから、見た目としても別物にしてしまう。ただそれだけです。もともとあるHTMLの区分を無視して、無理やり別のことをしているから難しくなってしまうのです。&lt;/p&gt;
&lt;p&gt;これまで話してきたのは、マークアップの複雑さについてです。考慮すべき要素がいくつもあるがゆえに、それらをうまく調整する手腕が問われるという話でした。一方、そのような「小難しいこと」を考えずに、ごく素朴な感覚をもってマークアップができるものの例として、利用規約のマークアップが挙げられます。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/019.Dy9KHaWc_ZKc1g0.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://connpass.com/term/&quot;&gt;利用規約 - connpass&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;利用規約は、極めてシンプルなウェブページの代表です。これは言ってしまえば、「誰も読まないページ」でもあります。だからこそ、HTMLという形式に対して素直で作りやすく、ごく普通の形を保ち続けられるのだとも言えます。&lt;/p&gt;
&lt;p&gt;利用規約のマークアップでは、ほとんど迷うことがありません。どのようにマークアップするかが明確で、簡単です。なぜならHTMLが、利用規約のような文書を表現するために適した形式になっているからです。&lt;/p&gt;
&lt;p&gt;HTMLでは、情報を表現するために使える語彙があらかじめ決められています。そのため、それに合ったものは作りやすく、合わないものは作りづらくなります。どのようなウェブページを作るにしても、そこから逃れることはできません。ではそもそも、どうしてこうなったのでしょうか。&lt;/p&gt;
&lt;p&gt;ティム・バーナーズ゠リー『Webの創成』によれば、HTMLの始まりは次のような考えで作られたと言います。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;私は、あらゆる種類のデータのフォーマット形式がWeb上に存在することになるだろうと予測していた。一方で、そこには誰でもが使える簡単な共通語が必要であるとも感じていた。（中略）すなわち、メニュー、ヘルプ・ファイルのような簡単な文書、会議録、メール、すなわちたいていの人の日常生活の用事の95%をカバーするような仕事である。これが、HTMLすなわちハイパー・テキスト・マークアップランゲージである。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;ウェブで扱われることになるであろうあらゆるタイプのドキュメント、それはたとえば、ビデオ、CAD、音声、アニメ、実行可能なコンピュータプログラムといったものを繋ぐための「糸」として、HTMLが機能することが期待されていました。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://info.cern.ch/hypertext/WWW/MarkUp/Tags.html&quot;&gt;1991年当時のHTMLで使われていたタグ&lt;/a&gt;は次のようなものです。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;TITLE&amp;gt; ... &amp;lt;/TITLE&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;NEXTID 27&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;A NAME=xxx HREF=XXX&amp;gt; ... &amp;lt;/A&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;ISINDEX&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;PLAINTEXT&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;LISTING&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/LISTING&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;P&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;H1&amp;gt;, &amp;lt;H2&amp;gt;, &amp;lt;H3&amp;gt;, &amp;lt;H4&amp;gt;, &amp;lt;H5&amp;gt;, &amp;lt;H6&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;H2&amp;gt;Second level heading&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;ADDRESS&amp;gt; text ... &amp;lt;/ADDRESS&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;HP1&amp;gt;...&amp;lt;/HP1&amp;gt;   &amp;lt;HP2&amp;gt;... &amp;lt;/HP2&amp;gt; etc.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;DL&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;DT&amp;gt;Term&amp;lt;DD&amp;gt;definition pagagraph&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;DT&amp;gt;Term2&amp;lt;DD&amp;gt;Definition of term2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/DL&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;UL&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;LI&amp;gt; list element&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;LI&amp;gt; another list element ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/UL&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;いくつか見慣れないタグはあるものの、現在でも当たり前に使われているものがいくつも含まれているのは驚くべきことです。見出しレベルが1から6までなのも最初から決まっていました。&lt;/p&gt;
&lt;p&gt;『Webの創成』には、続けて次のようにあります。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;世界中の人々に新しい地球規模の情報システムを使わせることが困難であることは知っていたので、私はできる限りすべてのグループを議論の場にひきずり出したかったのだ。マークアップ・ランゲージという分野がすでに存在していて、standard generlized markup language（SGML）がすでに世界の文書作成に関わる人々の間で好んで使われていた。また、当時はハイパーテキスト作成に関わる人々の間でも、これが唯一可能性のある文書作成の標準システムであると考えられていた。私はHTMLを開発するにあたり、SGMLの親戚のように見えるようにつくった。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;山型のついたタグという表現も、要素や属性としてデータの種類をマークアップする考え方も、SGMLを参考にしてできたものでした。しかし大きく違うのは、SGMLはその用途があらかじめ決められているわけではない、というところです。HTMLはウェブページの作成を目的とした言語ですが、SGMLはあらゆる情報をマークアップするための言語です。SGMLの作業は、個々の目的に応じた文書を分析するところから始まります。&lt;/p&gt;
&lt;p&gt;『SGML入門』では、教科書の前付けページの構成例が紹介されています。教科書の前付けページに含まれるデータを分析して、ツリーダイアグラムとして構造を定義したものです。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/020.SyDjCVOT_Z2ieB4Y.webp&quot; alt=&quot;典型的な教科書の前付けページの構成がツリーダイアグラムとして表現されている&quot; loading=&quot;lazy&quot; width=&quot;965&quot; height=&quot;709&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;Martin Bryan『SGML入門』（福島誠訳）&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;ul&gt;
&lt;li&gt;教科書
&lt;ul&gt;
&lt;li&gt;前付け
&lt;ul&gt;
&lt;li&gt;小扉のページ &lt;sup&gt;1&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;本の表題&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;小扉の裏
&lt;ul&gt;
&lt;li&gt;シリーズの表題&lt;/li&gt;
&lt;li&gt;シリーズの編集者&lt;/li&gt;
&lt;li&gt;他の表題&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;本扉
&lt;ul&gt;
&lt;li&gt;表題
&lt;ul&gt;
&lt;li&gt;表題の行&lt;/li&gt;
&lt;li&gt;副題 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;版番号 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;巻番号 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;著者
&lt;ul&gt;
&lt;li&gt;名前 &lt;sup&gt;2&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;役職 &lt;sup&gt;※2&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;編集者
&lt;ul&gt;
&lt;li&gt;名前&lt;/li&gt;
&lt;li&gt;役職&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;文書番号 &lt;sup&gt;※&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;ISBN &lt;sup&gt;4&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;その他&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;発行日&lt;/li&gt;
&lt;li&gt;出版社／印刷所の情報
&lt;ul&gt;
&lt;li&gt;名前 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;住所 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;抄録／その他のテキスト &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;本扉の裏
&lt;ul&gt;
&lt;li&gt;出版社／印刷所の詳細 &lt;sup&gt;3&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;名前&lt;/li&gt;
&lt;li&gt;住所&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;著作権の詳細
&lt;ul&gt;
&lt;li&gt;発行日&lt;/li&gt;
&lt;li&gt;著作権所有者&lt;/li&gt;
&lt;li&gt;著作権の宣言&lt;/li&gt;
&lt;li&gt;初版（翻訳）の詳細&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;印刷所のマーク
&lt;ul&gt;
&lt;li&gt;印刷の詳細
&lt;ul&gt;
&lt;li&gt;発行日（および前版） &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;再版日&lt;/li&gt;
&lt;li&gt;印刷所名&lt;/li&gt;
&lt;li&gt;印刷所の住所&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;他の製作者のクレジットライン &lt;sup&gt;※&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;植字工&lt;/li&gt;
&lt;li&gt;デザイナー&lt;/li&gt;
&lt;li&gt;イラストレーター&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;出版目録の詳細 &lt;sup&gt;※&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;英国図書館 &lt;sup&gt;4&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;米国国会図書館 &lt;sup&gt;4&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ISBN &lt;sup&gt;4&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;著者の献辞 &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;目次&lt;/li&gt;
&lt;li&gt;図のリスト &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;略語のリスト &lt;sup&gt;※&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;序文 &lt;sup&gt;※&lt;/sup&gt;
&lt;ul&gt;
&lt;li&gt;まえがき&lt;/li&gt;
&lt;li&gt;序文&lt;/li&gt;
&lt;li&gt;謝辞&lt;/li&gt;
&lt;li&gt;その他&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;sup&gt;※&lt;/sup&gt; オプション要素&lt;br /&gt;
&lt;sup&gt;1&lt;/sup&gt; 本扉から自動的に作成可能&lt;br /&gt;
&lt;sup&gt;2&lt;/sup&gt; 複数の著者／編集者および共作の場合もある&lt;br /&gt;
&lt;sup&gt;3&lt;/sup&gt; 本扉と裏のページに分けられる&lt;br /&gt;
&lt;sup&gt;4&lt;/sup&gt; 複数の箇所に現れる場合もある&lt;br /&gt;&lt;/p&gt;&lt;/figure&gt;
&lt;p&gt;もう少し親しみやすい例としては、『SGML実践ガイド』では、料理のレシピをマークアップするための要素の例も紹介されていました。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/021.1_uNsc4w_Z2kOpNy.webp&quot; alt=&quot;料理のレシピについて文書分析する例として、最終的に紙に印刷されるレシピをサンプルにしつつ、レシピで使う要素を例示している。&amp;lt;head1&amp;gt;、&amp;lt;p1&amp;gt;、&amp;lt;p2&amp;gt;、&amp;lt;p3&amp;gt;、&amp;lt;p4&amp;gt;、&amp;lt;pr&amp;gt;、&amp;lt;pl&amp;gt;、&amp;lt;it&amp;gt;&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;682&quot; /&gt;
&lt;figcaption&gt;— 
&lt;cite&gt;B.トラビス, D.ウォルト『SGML実践ガイド』（学研, スリーエーシステムズSGML事業室 訳）&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;『SGML実践ガイド』によれば、SGMLの文書を定義するに当たっては、次の3つの基本的な観点があります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;書式&lt;/li&gt;
&lt;li&gt;構造&lt;/li&gt;
&lt;li&gt;内容&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要素のスタイルまたは体裁にもとづいてタグをつけていくことを書式タグ付けと呼びます。対象となるのは、テキスト内の一部の言葉だけを太字にする強調部分などです。表も書式に大きく依存した要素です。書式タグに頼りすぎると、データを他の目的に転用するのが難しくなります。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;head1 q=&apos;left&apos; w=&apos;bold&apos;&amp;gt;This is a heading that is quad left and&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;p in=&apos;1em&apos; type=&apos;bold&apos;&amp;gt;This is an indented paragraph&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;The author wants to &amp;lt;emphasis/emphasize/ this word.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;次に、総称的な階層にもとづいてタグをつけていくことを構造タグ付けと呼びます。要素には、階層内の順列を表す名前をつけます。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;head1&amp;gt;This is a First Level Head&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;head2&amp;gt;This is a Second Level Head&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;para0&amp;gt;This is a high-level paragraph&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;list1&amp;gt;This is a first-level indented list item&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;list2&amp;gt;This is a second-level indented list item&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最後に、体裁や階層構造にとらわれず、要素の内容（目的）にもとづいてタグをつけるのが内容タグ付けです。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;lesson&amp;gt;How to peel an egg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;intro&amp;gt;In this lesson, we will present the best ways to peel&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;two kinds of eggs.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/intro&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;obj&amp;gt;Successfully peel a hard-boiled egg&amp;lt;/obj&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;obj&amp;gt;Successfully peel a raw egg&amp;lt;/obj&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;内容タグは使いすぎないように注意します。データとして別物であると意識しすぎて違うタグを使いたくなっても、実は、区別しなくても処理には差し支えないということがよくあるからです。&lt;/p&gt;
&lt;p&gt;内容タグの例としては次のようなものがあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Techinical Manual
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;part&amp;gt;&lt;/code&gt;: Part Number&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;mod&amp;gt;&lt;/code&gt;: Model&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;chkl&amp;gt;&lt;/code&gt;: Checklist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;cau&amp;gt;&lt;/code&gt;: Caution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Computer Manuals
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;command&amp;gt;&lt;/code&gt;: Command&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;opt&amp;gt;&lt;/code&gt;: Option&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;parm&amp;gt;&lt;/code&gt;: Parameter&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;key&amp;gt;&lt;/code&gt;: Keystroke&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Training Manual
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;les&amp;gt;&lt;/code&gt;: Lesson&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;obj&amp;gt;&lt;/code&gt;: Objectives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;ex&amp;gt;:&lt;/code&gt; Exercise&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;def&amp;gt;&lt;/code&gt;: Definition&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;General
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;fw&amp;gt;&lt;/code&gt;: Foreign Word&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;name&amp;gt;&lt;/code&gt;: Name&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;gl&amp;gt;&lt;/code&gt;: Glossary Word&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;xref&amp;gt;&lt;/code&gt;: Cross Reference&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Periodicals
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;au&amp;gt;&lt;/code&gt;: Author&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;hl&amp;gt;&lt;/code&gt;: Headline&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;en&amp;gt;&lt;/code&gt;: End Note&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;blurb&amp;gt;&lt;/code&gt;: Blurb&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Legal
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;case&amp;gt;&lt;/code&gt;: Case Cite&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;reg&amp;gt;&lt;/code&gt;: Reg Cite&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt;: Code Cite&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;squib&amp;gt;&lt;/code&gt;: Squib&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これら3つの観点をバランスよく組み合わせて定義するのが理想的であるそうです。&lt;/p&gt;
&lt;p&gt;現在あるHTML要素と照らし合わせてみると、まさにこの3つの観点が存在することがわかります。&lt;a href=&quot;https://www.w3.org/TR/html5-diff/#changed-elements&quot;&gt;HTML5では定義が改められた&lt;code&gt;b&lt;/code&gt;要素や&lt;code&gt;i&lt;/code&gt;要素など&lt;/a&gt;も、もとは明らかに書式タグ付けを意識しています。&lt;/p&gt;
&lt;p&gt;先述の通り、HTMLではこうして要素を定義するフェーズは通過済みで、ユーザーが使うときには定義を参照しながらマークアップするだけの状態になっています。これはつまり、「ウェブページとはこういうものだ」という思考の枠組みが決められているとも言えます。だからこそ、それに合わせて発想しさえすれば簡単にマークアップはできるわけですが、それができなければ難儀するかもしれません。&lt;/p&gt;
&lt;p&gt;はたまた、HTML仕様は変化し続けています。定義が決まっていると言っても不変ではありません。なぜなら、より多くのユースケースがウェブに求められているためです。時には、ユーザーが想定外の使い方をすることで新しい可能性が見い出されては、それに適応するように仕様が書き変わってきました。価値は変わりゆくものです。かつてはナシだったものが、今はアリになっている。そしてそれは、一時の逸脱によってもたらされます。&lt;/p&gt;
&lt;p&gt;HTMLのマークアップは保守的な営みです。問題が起こらないように、意味が損なわれないように、複数の文脈を繋ぎ止める役割をします。これは見方によっては、過去と現在を接続する言語だと捉えられます。&lt;/p&gt;
&lt;p&gt;変化は必然ですが、同時に、人は過去に依存して生きています。過去の選択が今を作っています。覆すことはできません。これを理解し、受け入れて、今を成り立たせていくことこそが、マークアップの意義であると思います。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/022.ChfISKhO_20NsCx.webp&quot; alt=&quot;&amp;lt;/html&amp;gt;&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;&lt;/figure&gt;</content:encoded></item><item><title>複数の要素を重ね合わせつつ、その中で最も大きい要素に合わせて親要素のサイズを設定するCSS</title><link>https://yuheiy.com/2022-08-25-sizing-based-on-multiple-overlapping-elements</link><guid isPermaLink="true">https://yuheiy.com/2022-08-25-sizing-based-on-multiple-overlapping-elements</guid><description>複数の要素を重ね合わせるためには、従来、position: absoluteを使うことが多かった。親要素にposition: relativeを、その子要素にposition: absoluteを設定するというもの。</description><pubDate>Thu, 25 Aug 2022 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;複数の要素を重ね合わせるためには、従来、&lt;code&gt;position: absolute&lt;/code&gt;を使うことが多かった。親要素に&lt;code&gt;position: relative&lt;/code&gt;を、その子要素に&lt;code&gt;position: absolute&lt;/code&gt;を設定するというもの。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: relative;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: absolute;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: absolute;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかしその場合、子要素自身のサイズに基づいて親要素のサイズを設定するということができない。CSSだけでは実現できないので、JavaScriptを使うことになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: relative; height: 421px;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: absolute;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;position: absolute;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;そんなとき、&lt;code&gt;position: absolute&lt;/code&gt;の代わりにGridを使うと、CSSだけでこれを実現できるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrapper&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;grid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.wrapper&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-row&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-column&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このようにすると、自ずと、最も大きい子要素のサイズに基づいて親要素のサイズが設定されるようになる。&lt;/p&gt;
&lt;p&gt;これを使うと、たとえば、ボタンのラベルの長さが変わってもボタン自体の幅は変化しないようにすることができる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;あるいは、複数の画像がクロスフェードするギャラリーのようなUIを作成するときに、画像自体に基づいてサイズを設定することもできる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;</content:encoded></item><item><title>横並びになったボタンを同じ幅で配置するCSS</title><link>https://yuheiy.com/2022-08-14-same-width-buttons</link><guid isPermaLink="true">https://yuheiy.com/2022-08-14-same-width-buttons</guid><description>複数のボタンを横並びにしつつ、それぞれのボタンが同じ幅になるように合わせたいということがある。ボタンに同じwidthを指定すれば合わせられるが、するとラベルの長さに応じた幅にできなくなる。</description><pubDate>Sat, 13 Aug 2022 23:20:00 GMT</pubDate><content:encoded>&lt;p&gt;複数のボタンを横並びにしつつ、それぞれのボタンが同じ幅になるように合わせたいということがある。ボタンに同じ&lt;code&gt;width&lt;/code&gt;を指定すれば合わせられるが、するとラベルの長さに応じた幅にできなくなる。&lt;/p&gt;
&lt;p&gt;縦方向に並んだボタンであれば、いずれか幅の広い方に合わせることは難しくない。次のようにすれば、最も幅の広いボタンに合わせて他のボタンも配置することができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrapper&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Hi&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Hello&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;button&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Lorem ipsum dolor&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-direction&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;column&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fit-content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;この例では、ボタンの幅は親要素いっぱいになりつつ、親要素の幅は最も幅の広いボタンにフィットするようになる。&lt;/p&gt;
&lt;p&gt;横方向に並んだボタンの幅を合わせる場合でも、何かしらの方法で、最も幅の広いボタンの大きさを知る必要がある。そこで、Gridの&lt;code&gt;fr&lt;/code&gt;が使える。グリッドアイテムの幅は等幅になるように指定しつつ、グリッド自体の幅はコンテンツに応じた幅にすることで、すべてのボタンの幅を最も幅の広いボタンに合わせることができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;grid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-auto-columns&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;fr&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-auto-flow&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;column&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fit-content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;grid-auto-columns&lt;/code&gt;と&lt;code&gt;grid-auto-flow&lt;/code&gt;を指定することで、どのように&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Grids#the_implicit_and_explicit_grid&quot;&gt;暗黙的なグリッド&lt;/a&gt;が生成されるかを定義できる。例のような場合、明示的なグリッドを使用すると、要素内に含めるボタンの個数に応じてグリッドの設定を変更する必要があるが、暗黙的なグリッドを使用すれば、ボタンの個数にかかわらず自ずと適切に処理されるようになる。&lt;/p&gt;</content:encoded></item><item><title>プロフィール画像を作り直した</title><link>https://yuheiy.com/2022-08-13-change-the-icon</link><guid isPermaLink="true">https://yuheiy.com/2022-08-13-change-the-icon</guid><description>僕のプロフィール画像、通称ギザギザを新しく作り直した。SVG化したいなと長らく思っていたけど、そのまま機械的に変換するのもつまらないし、せっかくなのでとマイナーチェンジを施した。</description><pubDate>Sat, 13 Aug 2022 13:40:00 GMT</pubDate><content:encoded>&lt;p&gt;僕のプロフィール画像、通称ギザギザを新しく作り直した。SVG化したいなと長らく思っていたけど、そのまま機械的に変換するのもつまらないし、せっかくなのでとマイナーチェンジを施した。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/before.DC-ORIwA_1gVio.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; /&gt;
&lt;figcaption&gt;これまでのプロフィール画像&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/after.Y057wQHY_10P0c1.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; /&gt;
&lt;figcaption&gt;新しいプロフィール画像&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;線が太くなって、角が丸くなり、パスも少し単純化した。その分、線が強く見えるようになったので、色を薄くした。これによって、縮小されても多少見やすくなった気がする。&lt;/p&gt;
&lt;p&gt;これまでのものも、新しいものも、いずれもプログラムによって線がランダムに生成されることでできている。たぶんこの元ネタになるのが、6年くらい前に実験で作っていたデモだった。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;僕の今のプロフィールアイコンをSVG化したいと思って元データを探していたら、元になった6年前のデモが出てきた &lt;a href=&quot;https://t.co/ziVumDyBAp&quot;&gt;pic.twitter.com/ziVumDyBAp&lt;/a&gt;&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1558387257186590720?ref_src=twsrc%5Etfw&quot;&gt;August 13, 2022&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;これは&lt;code&gt;canvas&lt;/code&gt;を使って実装したもので、後にプロフィール画像を作るタイミングになってからSVG版を作った。しかし、そこで生成されたSVGを保存していなかったので、以来、必要になるたびに自分のTwitterアカウントなどからアイコンをダウンロードしてくる羽目になった。&lt;/p&gt;
&lt;p&gt;ところで、今回どのようにしてこれを作ったかと言うと、とにかく大量のパターンを生成しては、それらを見比べて良さげなものを選ぶ、という感じ。というか、こんな単純な線に良いとか悪いとかあるのだろうか、わからない。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://t.co/96qUSzJEd9&quot;&gt;pic.twitter.com/96qUSzJEd9&lt;/a&gt;&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1558405235328286720?ref_src=twsrc%5Etfw&quot;&gt;August 13, 2022&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;そうこうしてアイコンをSVG化できたので、このサイトのヘッダー部分のアイコンとファビコンをSVGに変更した。あと、&lt;code&gt;favicon.ico&lt;/code&gt;と&lt;code&gt;apple-touch-icon&lt;/code&gt;、OG画像は、PNGのまま新しいものに差し替えた。ファビコンはフォールバック用に&lt;code&gt;favicon.ico&lt;/code&gt;も残しているというわけだ。SNS系のプロフィール画像も一通り更新したはず。&lt;/p&gt;</content:encoded></item><item><title>ボタンの最低幅を設定しつつ、かつコンテナが縮小してもはみ出さないようにするCSS</title><link>https://yuheiy.com/2022-08-12-shrinkable-min-width</link><guid isPermaLink="true">https://yuheiy.com/2022-08-12-shrinkable-min-width</guid><description>ボタンのラベルが短い場合でも、ボタン自体には最低限ある程度の幅を持たせたいという場合がある。そんなときには、ボタン自体にmin-widthを指定して最低幅を設定する。</description><pubDate>Fri, 12 Aug 2022 06:40:00 GMT</pubDate><content:encoded>&lt;p&gt;ボタンのラベルが短い場合でも、ボタン自体には最低限ある程度の幅を持たせたいという場合がある。そんなときには、ボタン自体に&lt;code&gt;min-width&lt;/code&gt;を指定して最低幅を設定する。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、そのボタンを配置するコンテナの幅が、ボタン自体の&lt;code&gt;min-width&lt;/code&gt;の値よりも狭くなってしまうこともある。すると、ボタンがコンテナをはみ出してしまう。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/1.7x-iWaC4_Z1COnAe.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;432&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;ボタンの幅をコンテナに収めるためには&lt;code&gt;max-width: 100%&lt;/code&gt;が使えそうにも思えるが、&lt;code&gt;min-width&lt;/code&gt;の方が優先されてしまうので機能しない。そこで、&lt;code&gt;min-width&lt;/code&gt;の値として&lt;code&gt;min()&lt;/code&gt;を使うようにする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このようにすると、&lt;code&gt;min-width&lt;/code&gt;の値が&lt;code&gt;100%&lt;/code&gt;を超えることを防げる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;後日追記:&lt;/strong&gt; &lt;code&gt;min()&lt;/code&gt;とすべきところを誤って&lt;code&gt;max()&lt;/code&gt;と表記してしまっていたことを&lt;a href=&quot;https://x.com/yuito_miki/status/1763156712079700377&quot;&gt;ご指摘いただいた&lt;/a&gt;ため、内容を修正した。&lt;/p&gt;</content:encoded></item><item><title>テキストの行の末尾に付随するアイコンをアイコンだけで改行させないようにするCSS</title><link>https://yuheiy.com/2022-08-11-text-with-accompanying-icon</link><guid isPermaLink="true">https://yuheiy.com/2022-08-11-text-with-accompanying-icon</guid><description>テキストの行の末尾にアイコンを付随させるというレイアウトがある。</description><pubDate>Thu, 11 Aug 2022 11:25:00 GMT</pubDate><content:encoded>&lt;p&gt;テキストの行の末尾にアイコンを付随させるというレイアウトがある。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/1.CNz87SI3_kdca4.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;272&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;これは、インライン整形コンテキストにおいてテキストとアイコンを並べて配置することで実現できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;gt;テキストの行の末尾に付随するアイコンを…&amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、その空間の大きさやテキストの分量によっては、末尾のアイコンだけが改行してしまって不恰好に見えることがある。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/2.krlZ9JHX_1BqgLw.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;272&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;そうした場合には、テキスト要素のパディングとアイコン要素のネガティブマージンを組み合わせることで対処できる。次のようにすると、アイコンだけが単独で改行されることがなく、常にテキストを伴って改行されるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;card&quot;&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;card__label&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;テキストの行の末尾に付随するアイコンを…&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!--&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;	--&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;card__icon&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --icon-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --icon-margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.25&lt;/span&gt;&lt;span&gt;em&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.card__label&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--icon-margin&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--icon-size&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.card__icon&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--icon-size&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--icon-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--icon-size&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;まずテキスト要素の&lt;code&gt;padding-right&lt;/code&gt;によって、アイコンとその間の余白を加えた分の空間が末尾に確保されるようになる。そしてその空間を埋めるように、アイコン要素の位置を&lt;code&gt;margin-left&lt;/code&gt;のネガティブ値で移動させる。それによって、アイコンが必ずテキストの末尾に隣り合って配置されるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;注意点としては、テキスト要素とアイコン要素の間にソースコード上でホワイトスペースが入らないようにすることだ。ホワイトスペースの分の幅ができると、アイコンのために確保した空間をはみ出してしまうため、改行方法が正しく制御されなくなる。&lt;/p&gt;
&lt;h2&gt;後日追記&lt;/h2&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;Firefox でアイコンが .card--improved の padding を突き抜けちゃうのなんでだろう。&lt;br /&gt;&lt;br /&gt;テキストの行の末尾に付随するアイコンをアイコンだけで改行させないようにするCSS &lt;a href=&quot;https://t.co/29Y1SkJSDI&quot;&gt;https://t.co/29Y1SkJSDI&lt;/a&gt; &lt;a href=&quot;https://t.co/CuRtPZLQ9t&quot;&gt;pic.twitter.com/CuRtPZLQ9t&lt;/a&gt;&lt;/p&gt;— トミー (@SaekiTominaga) &lt;a href=&quot;https://twitter.com/SaekiTominaga/status/1557902385376563202?ref_src=twsrc%5Etfw&quot;&gt;August 12, 2022&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;テキスト要素に&lt;code&gt;margin-right&lt;/code&gt;を使っていたら、Firefoxでこのような問題が起こってしまった。代わりに、&lt;code&gt;padding-right&lt;/code&gt;に差し替えると直った。理由はよくわからない……。&lt;/p&gt;
&lt;hr /&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;パディング領域よりアイコンの大きさが小さいと、まれに右下のようにアイコンが飛び出すっぽかった（ラベルの最後の文字に依存しそう） 
&lt;a href=&quot;https://t.co/eT03xm8Eq0&quot;&gt;pic.x.com/eT03xm8Eq0&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;— ながしまきょう (@hail2u_) &lt;/p&gt;&lt;a href=&quot;https://x.com/hail2u_/status/1557858922454974464?ref_src=twsrc%5Etfw&quot;&gt;&lt;p&gt;August 11, 2022&lt;/p&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/figure&gt;
&lt;p&gt;これは直し方がわからなかった……。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;アイコンのために&lt;code&gt;svg&lt;/code&gt;要素やあるいは擬似要素を使う代わりに、テキスト要素の&lt;code&gt;background-image&lt;/code&gt;を使うとより安全だけど、その場合はアイコンの高さを&lt;code&gt;1em&lt;/code&gt;より大きくすることができないという制約がある。&lt;/p&gt;
&lt;h2&gt;2025年1月7日追記&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;text-wrap&lt;/code&gt;プロパティを利用することで、アイコンだけで改行させないテクニックが紹介されていた。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://muffinman.io/blog/css-fix-to-prevent-orphan-icons/&quot;&gt;CSS fix to prevent orphan icons dropping to a new line · Muffin Man&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>宣言的デザインシステム（翻訳）</title><link>https://yuheiy.com/2022-08-01-declarative-design-system</link><guid isPermaLink="true">https://yuheiy.com/2022-08-01-declarative-design-system</guid><description>この記事は、Jeremy Keith氏による「Declarative design systems」の日本語訳です。掲載に当たって著者の許諾を得ています。</description><pubDate>Mon, 01 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;この記事は、&lt;a href=&quot;https://adactio.com/&quot;&gt;Jeremy Keith&lt;/a&gt;氏による「&lt;a href=&quot;https://adactio.com/journal/19131&quot;&gt;Declarative design systems&lt;/a&gt;」の日本語訳です。掲載に当たって著者の許諾を得ています。&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;「&lt;a href=&quot;https://adactio.com/journal/18982&quot;&gt;宣言的デザイン&lt;/a&gt;」（&lt;a href=&quot;/2022-08-01-declarative-design&quot;&gt;日本語訳&lt;/a&gt;）という考え方について書いたことで、多くの人々から確かな共感が得られました。&lt;/p&gt;

&lt;p&gt;一般的な感覚として、デザインや開発において命令的アプローチを取ると、つまり、あらゆることを精緻かつ直接的に指定しようとすると、歯痒い思いをするものなのだと思います。要するに、それはスケールしないのです。Jasonの言葉を借りれば、&lt;a href=&quot;https://cloudfour.com/thinks/traditional-web-design-process-is-fundamentally-broken/&quot;&gt;従来のウェブデザインのプロセスは根本的に破綻している&lt;/a&gt;ということです——&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;これは最悪の事態です。ウォーターフォール型のプロセスによって何十もの成果物が作成されますが、デザインがブラウザでどのように見えて、どのように動作するのかについては、そのうちのどれも正確に捉えることができていないのです。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;理論的には、この問題を打開するためにデザインシステムが役立つかもしれません。あらかじめ時間をかけてコンポーネントを正しいものにしておけば、あらゆる状況においてすばやく展開することができます。しかしこの場合、「正しい」という言葉には解釈の余地があります。&lt;/p&gt;

&lt;p&gt;命令型の考え方でデザインシステムに取り組むと、「正しい」は「精緻」という意味になります。このアプローチでは、精緻なスペーシングに精緻な数値、精緻なピクセルなど、精緻であることに価値が置かれます。&lt;/p&gt;

&lt;p&gt;宣言型の考え方でデザインシステムに取り組むと、「正しい」は「弾力的」であることを意味します。このアプローチでは、柔軟なスペーシングに柔軟なレンジ、柔軟な出力など、柔軟性に価値が置かれます。&lt;/p&gt;

&lt;p&gt;これら二つは根本的に異なるデザインアプローチですが、いずれの成果もデザインシステムだと言い表されるでしょう。実際のところ、「デザインシステム」は定義することが難しい用語です。ある人が「デザインシステム」と言うと、非常に精緻に統制されたコンポーネントのコレクションを意味し、別の人が「デザインシステム」と言うと、コンポーネントを生成するためにあらかじめ用意された境界条件の集合を意味するのです。&lt;/p&gt;

&lt;p&gt;個人的には、その「システム」という言葉こそがデザインシステムにとって重要な部分だと考えています。しかし多くの場合、デザインシステムはシステムというより&lt;em&gt;コレクション&lt;/em&gt;です。コンポーネントを生成するための&lt;em&gt;システム&lt;/em&gt;ではなく、あらかじめ生成されたコンポーネントのコレクションなのです。&lt;/p&gt;

&lt;p&gt;宣言的デザインの核心は、システム的なアプローチです。事前にルールと比率を設定し、最終的な実装の詳細はブラウザの実行時に委ねます。&lt;/p&gt;

&lt;p&gt;宣言的アプローチのコンポーネントとして、私が考える例を挙げてみましょう。デザインシステムのコンポーネントにおける「Hello World」とも言える、よくあるボタンを用いてみます。&lt;/p&gt;

&lt;p&gt;二年前、&lt;a href=&quot;https://adactio.com/journal/16960&quot;&gt;Sassの色関数を代替するためのCSSプログラミング&lt;/a&gt;について書きました。カスタムプロパティや&lt;code&gt;calc()&lt;/code&gt;といったCSSの機能を使って、&lt;code&gt;darken()&lt;/code&gt;や&lt;code&gt;lighten()&lt;/code&gt;のようなmixinを再現する方法について説明しました。&lt;/p&gt;

&lt;p&gt;カスタムプロパティとしてエンコードされた色相、彩度、明度を使用して、異なる色のボタンを宣言するCSSをいくつか紹介しました。これが、&lt;a href=&quot;https://codepen.io/adactio/pen/xxwovRZ&quot;&gt;さまざまなボタンの例を含むCodePen&lt;/a&gt;です。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;

&lt;p&gt;もし、これらのボタンが命令的デザインシステムの中にあるとすれば、出力が重要になるでしょう。そのデザインシステムは、これらのボタンを精緻に作成するためのコードを提供するでしょう。別のボタンを使いたければ、それがバリエーションとしてデザインシステムに追加されなければならないでしょう。&lt;/p&gt;

&lt;p&gt;しかし宣言的デザインシステムにおいては、出力は基礎となるルールセットほど重要ではありません。この場合には、次のようなルールがあります。&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;ボタンのホバー状態では、背景色の明度を5%下げる。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;これはCSSで次のようにエンコードされます。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hsl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--button-colour-hue&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--button-colour-saturation&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--button-colour-lightness&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;この種のデザインシステムでは、&lt;a href=&quot;https://codepen.io/adactio/pen/xxwovRZ&quot;&gt;いくつかの例&lt;/a&gt;を見ればルールの実行結果を確認することができます。しかしこれらの出力は、理解を助けるためのものです。これだけで終わりではありません。もし必要なボタンがなかったとしても、それを生成するために必要な情報は提供されていますし、そうすると、たとえばボタンのホバー状態などについても、あらかじめ定義されたルールの範囲内に収まります。&lt;/p&gt;

&lt;p&gt;この方がよりスケールするように思えるのです。また、より強力なアプローチだと思います。&lt;/p&gt;

&lt;p&gt;デザインシステムを組織に取り入れる上での困難の一つは、人々にそれを採用してもらうことです。私の経験では、上層部から提供される出来合いのコンポーネント群を採用したいと思う人はいません。「この既成のコンポーネントを使えば、車輪の再発明をする時間を節約できるので便利ですよ」ということなのですが、一方、過度に支配的に見えてしまうこともあります。「あなた方が適切な判断を下せるとは思えないので、あらかじめ用意されたコンポーネントを使いなさい」という風に。&lt;/p&gt;

&lt;p&gt;宣言的アプローチは、あまり支配的ではありません。「これはコンポーネントの作成に役立つルールとガイドラインです」ということです。しかし代償として、精緻さに欠けます。そしてこのデザインシステムを利用するためには、提供された体系的なルールから必要なコンポーネントを作成するための考え方と技能を習得しなければなりません。&lt;/p&gt;

&lt;p&gt;私の直観では、FigmaやSketchのようなグラフィックデザインツールには命令型の考え方がよく合うと思うのです。これらのツールは、レンジやルールではなく、精緻な数値を扱います。&lt;/p&gt;

&lt;p&gt;一方で宣言型の考え方は、ますますCSSにぴったりだと感じるようになってきました。この言語は、カスタムプロパティ、&lt;code&gt;calc()&lt;/code&gt;、&lt;code&gt;clamp()&lt;/code&gt;、&lt;code&gt;minmax()&lt;/code&gt;などを通してルールを設定できるように進化してきました。&lt;/p&gt;

&lt;p&gt;したがって言うまでもなく、このアプローチに正しいも間違いもないのです。けっきょくのところ、なにがあなたの組織にとって最も適しているかということになります。&lt;/p&gt;

&lt;p&gt;もしデザイナーや開発者が命令型の考え方を持っていて、FigmaファイルがSource of Truthと考えられているなら、命令的デザインシステムの方が適しているでしょう。&lt;/p&gt;

&lt;p&gt;しかし幸運にも、HTMLとCSSの観点から考える&lt;a href=&quot;https://adactio.com/journal/17838&quot;&gt;デザインエンジニア&lt;/a&gt;のチームがあるとすれば、宣言的デザインシステムは力を発揮するでしょう。デザインエンジニアリングマインドのための自転車になります。&lt;/p&gt;</content:encoded></item><item><title>宣言的デザイン（翻訳）</title><link>https://yuheiy.com/2022-08-01-declarative-design</link><guid isPermaLink="true">https://yuheiy.com/2022-08-01-declarative-design</guid><description>この記事は、Jeremy Keith氏による「Declarative design」の日本語訳です。掲載に当たって著者の許諾を得ています。</description><pubDate>Mon, 01 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;この記事は、&lt;a href=&quot;https://adactio.com/&quot;&gt;Jeremy Keith&lt;/a&gt;氏による「&lt;a href=&quot;https://adactio.com/journal/18982&quot;&gt;Declarative design&lt;/a&gt;」の日本語訳です。掲載に当たって著者の許諾を得ています。&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;ここ数年で、同じようなマインドセットを共有するウェブデザインのアプローチがいくつか現れたように思います。Jenの&lt;a href=&quot;https://www.youtube.com/watch?v=AMPKmh98XLY&quot;&gt;Intrinsic web design&lt;/a&gt;、AndyとHeydonの&lt;a href=&quot;https://every-layout.dev/&quot;&gt;Every Layout&lt;/a&gt;、TrysとJamesの&lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia&lt;/a&gt;です。&lt;/p&gt;

&lt;p&gt;FlexboxやGrid、calcのような、CSSという技術独自の機能を活用していることも特色ではあります。しかしそれ以上に重要なのは、アプローチを共有していることです。これらはすべて、適切な「入力」を生み出すことに焦点を当てています。ありとあらゆる「出力」を制御しようとするのではなく。出力のための最終的な計算はブラウザに任せてしまう。それがコンピュータの得意とするところです。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://set.studio/&quot;&gt;Andy&lt;/a&gt;はこう言います。&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;ブラウザのマイクロマネジャーではなく、メンターになること。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;Jim NielsenはUtopiaのアプローチを振り返って、次のように&lt;a href=&quot;https://blog.jim-nielsen.com/2022/exerting-control-with-media-queries/&quot;&gt;書いています&lt;/a&gt;。&lt;/p&gt;


&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;CSSは「宣言的」だとは言いますが、ビューポートの範囲全体に渡って、デザインをさまざまな方法で変更するためにブレイクポイントを追加すればするほど、命令的なコードを書いているように感じます。&lt;strong&gt;一連の宣言的なルールが命令的な操作に見えてくるのはいつからでしょうか？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;これに対してUtopiaの原則の一つは、宣言的であること、つまり「どのようにするかを指示するのではなく、なにをするかを記述すること」です。Utopiaのアプローチでは、任意のビューポート幅における文字サイズとスペーシングを数式で導き出す、というルールを宣言しています。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;宣言的！　これこそ、UtopiaとEvery Layout、Intrinsic web designが共有するものを表現できる言葉なのかもしれません。&lt;/p&gt;

&lt;p&gt;宣言的デザインがあるのなら、命令的デザインもあるのでしょうか？　それなら、命令的デザインのためのツールや技術はどのようなものでしょうか？&lt;/p&gt;

&lt;p&gt;私が思うに、&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;は命令的デザインのうまい例です。これは特定の出力をするため&lt;em&gt;だけ&lt;/em&gt;のものです。システム的な思考は積極的に排除され、代わりに、画面に最終的に表示されるピクセルをどうしたいかを精緻に示すことになります。&lt;/p&gt;

&lt;p&gt;Utopiaのような宣言型ツールが正しく、Tailwindのような命令型ツールが間違いだと言っているわけではありません。言うまでもなく、場合によります。それはあなたの考え方次第です。&lt;/p&gt;

&lt;p&gt;もしこの意見に賛同するなら、おそらく命令型デザインツールを使うべきでしょう——&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;CSSは破綻しているので、CSSの性質をツールによって回避したい。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;逆にこの意見に賛同するなら、おそらく宣言型デザインツールを使うべきでしょう——&lt;/p&gt;

&lt;figure&gt;&lt;blockquote&gt;
&lt;p&gt;CSSは素晴らしいので、CSSの性質をツールによって増幅したい。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/figure&gt;

&lt;p&gt;もしあなたが最初の意見に賛同するなら、UtopiaやEvery Layoutのような宣言型ツールを使おうとしたとき、おそらく嫌な思いをするでしょう。たぶん嫌いになるでしょう。そのツールは「悪いもの」だと断定してしまうかもしれません。&lt;/p&gt;

&lt;p&gt;同様に、もしあなたが二つ目の意見に賛同するなら、Tailwindのような命令型ツールを使おうとしたとき、おそらく嫌な思いをするでしょう。たぶん嫌いになるでしょう。そのツールは「悪いもの」だと断定してしまうかもしれません。&lt;/p&gt;

&lt;p&gt;そのツールの背後にある哲学が、自分の哲学と一致するかどうかにかかっているのです。哲学が一致すれば、そのツールは生産的になり、知性の自転車として機能するでしょう。しかし、ツールの哲学が自分の哲学と一致&lt;em&gt;しなければ&lt;/em&gt;、いちいちツールとけんかすることになり、足手まといになるでしょう。&lt;/p&gt;

&lt;p&gt;宣言型ツールと命令型ツールの間にこのようなスペクトルが存在することを知っておくと、&lt;a href=&quot;https://adactio.com/articles/12839&quot;&gt;技術を評価する&lt;/a&gt;ときに役立ちます。あるウェブデザインツールが売り込まれているときに、それはCSSが破綻していることを前提としているのか、それともCSSは素晴らしいということを前提としているのか、評価することができるのです。&lt;/p&gt;

&lt;p&gt;ウェブデザインや開発についての方針も、このスペクトルのどちらに共感するかに関わってくるのではないでしょうか。たとえば、HTMLやCSSのような宣言型言語を学んできた人なら、Intrisic web designが響くかもしれません。しかし、JavaScriptのような命令型言語が得意な人には、Tailwindの方がより理にかなっているかもしれません。&lt;/p&gt;

&lt;p&gt;ここでも、どちらが正しいということはありません。考え方とツールとの適正についての話です。&lt;/p&gt;

&lt;p&gt;個人的には、宣言的デザインのアプローチは、グローブのように私にフィットしています。Johnの&lt;a href=&quot;https://alistapart.com/article/dao/&quot;&gt;A Dao Of Web Design&lt;/a&gt;やEthanの&lt;a href=&quot;https://alistapart.com/article/responsive-web-design/&quot;&gt;Responsive Web Design&lt;/a&gt;のような、&lt;a href=&quot;https://frankchimero.com/blog/2015/the-webs-grain/&quot;&gt;ウェブの木目&lt;/a&gt;に沿って作業するという伝統的な流儀を受け継いでいるような気がするのです。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;「&lt;a href=&quot;/2022-08-01-declarative-design-system&quot;&gt;宣言的デザインシステム（翻訳）&lt;/a&gt;」に続きます。&lt;/p&gt;</content:encoded></item><item><title>本文エリア内の要素をpaddingのないコンテナとして実装する</title><link>https://yuheiy.com/2022-07-31-container-with-no-padding</link><guid isPermaLink="true">https://yuheiy.com/2022-07-31-container-with-no-padding</guid><description>後日追記: このやり方の改良版をブログ「ブロックエディタ用のCSSを参考にした、よりよいコンテナの実装」として書いた。</description><pubDate>Sat, 30 Jul 2022 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;後日追記&lt;/strong&gt;: このやり方の改良版をブログ「&lt;a href=&quot;/2022-11-27-better-container&quot;&gt;ブロックエディタ用のCSSを参考にした、よりよいコンテナの実装&lt;/a&gt;」として書いた。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;CSSにおいて、コンテナと呼ばれるパターンがある。たとえば次のような実装を指している。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  box-sizing&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;content-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;この実装の特徴は、ビューポートの幅が広いときには要素の内容物が中央揃えになり、狭いときには左右に最低限の余白を残しつつ縮むことだ。&lt;/p&gt;
&lt;p&gt;よくある使い方としては、このコンテナでページの外側の方を囲いつつ、その中に雑多に要素を配置していくことになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかし、このコンテナの幅よりも広い幅で何かを配置したいという場合には、コンテナの中に配置すると都合が悪い。そういう場合には、そのコンテンツをコンテナの外側に配置できると望ましい。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wider-than-container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ただし、いつでもこのように自由にHTMLを構成できるとは限らない。&lt;/p&gt;
&lt;p&gt;たとえば、CMSによってユーザーに入力された記事の本文部分を表示する場合。出力されるHTMLの形式はある程度決まっていて、カジュアルに変更するのは難しいときがある。&lt;/p&gt;
&lt;p&gt;前述の例の場合、HTMLとしては次のような形を取らざるを得ないかもしれない。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wider-than-container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;CMSから出力されるHTMLを、コンテナの幅ごとに分割することはできないので、このままの構造に従ってスタイルを適用しなければならない。&lt;/p&gt;
&lt;p&gt;その場合、こうした本文エリアの全体をコンテナで囲ってしまうと、幅の実装をするのが難しくなる。そこで、その代わりとして、出力されるHTMLに含まれる要素の一つ一つに直接幅を指定する、というやり方がある。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;prose&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wider-than-container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.prose&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  box-sizing&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;content-box&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  padding-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.prose&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .wider-than-container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;こうすると、幅に関する実装が簡単になる。&lt;/p&gt;
&lt;p&gt;しかし、要素に&lt;code&gt;padding&lt;/code&gt;が適用されていると、別のスタイルとの組み合わせに支障をきたすことがある。&lt;/p&gt;
&lt;p&gt;たとえば、見出しに下線が引かれているようなあしらいを実現するために&lt;code&gt;border-bottom&lt;/code&gt;を使うとすれば、下線の描画範囲が&lt;code&gt;padding&lt;/code&gt;のせいで左右に余計に広がってしまう。また、背景色がついている要素などの内側で&lt;code&gt;padding&lt;/code&gt;を使いたくても使えなくなってしまう。あるいは、&lt;code&gt;position: absolute&lt;/code&gt;を使って、要素の相対位置に何かを配置したいというときに、X軸の基準位置が外側にずれてしまう。&lt;/p&gt;
&lt;p&gt;こうした問題は、何かしらのテクニックによって回避できるかもしれないが、少なくとも&lt;code&gt;padding&lt;/code&gt;がないに越したことはないだろう。&lt;/p&gt;
&lt;p&gt;そういったわけで僕は、あえて&lt;code&gt;padding&lt;/code&gt;を使わずにコンテナを実装することがある。次のような感じだ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --_container-margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--_container-margin&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;これを応用すると、本文エリア内でも要素の幅が異なるような実装ができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;prose&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alignwide&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;alignfull&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;figure&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --_container-margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--_container-margin&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.prose&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.alignfull&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --_container-margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-right&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-left&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--_container-margin&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;45&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.prose&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; .alignwide&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;具体的なイメージが伝わりやすいように、もう少し複雑な、&lt;a href=&quot;/blog/2022/container-with-no-padding/demo.html&quot;&gt;よくある記事ページの実装例&lt;/a&gt;も用意した。&lt;/p&gt;</content:encoded></item><item><title>Re: #deisui_html_radio #52</title><link>https://yuheiy.com/2022-06-15-re-deisui52</link><guid isPermaLink="true">https://yuheiy.com/2022-06-15-re-deisui52</guid><description>これについて超絶走り書きで簡単に返すのでお許しください。</description><pubDate>Wed, 15 Jun 2022 04:24:00 GMT</pubDate><content:encoded>&lt;p&gt;これについて超絶走り書きで簡単に返すのでお許しください。&lt;/p&gt;
&lt;figure&gt;
	&lt;a href=&quot;https://youtube.com/watch?v=DxoANgf4Oto&quot;&gt;
		&lt;span&gt;Play&lt;/span&gt;
	&lt;/a&gt;


&lt;/figure&gt;
&lt;p&gt;僕が言いたかったのは、figmaで言うと、figma用のコンポーネント機能があるじゃないですか。デザインしていて普通コンポーネントだと考えるのはそれだと思うんですよ。たとえばあるページに、中央揃えになったボタンがあるとして、コンポーネントと言えるのはそのボタンそのものだけで、どうやって中央配置にするかということは蚊帳の外というか、コンポーネントとはまた別の観点になる。多くの場合、そこにはコンポーネントのように明文化されたルール、制約はなくて、なんでもありになる。遊びがあるという状態。けど、実質的に、セオリーからすると、だいたいこうレイアウトするよねっていう暗黙のルールがある。でもこれは必ずしも確かなものではなくて、ケースバイケースに判断を変えることが許されている。そういう意味で、偶然そうなっているだけでしかない、ということが言えると思います。そこで、css設計ではその遊びをどう解釈するかという問題がある。&lt;/p&gt;
&lt;p&gt;テキストコンポーネントの話について言うと、figmaのただのテキストレイヤーをコンポーネントと理解するのはcss脳というか、偏った捉え方だと思う。テキストスタイルとしてルールを定義することはあるにしても、これもまたすべてがそうではなくて、例外もある。そのとき、それがどうしてそうなっているかという意図を求めたとしても、この状況においてはこうなるべきだからという以上の答えが出てこない、というか必要ない。後で本当に必要になったらそのとき考えればいい。そういう場面があるはずなんです。そういうものをcssにおいてもそのまま表現できる手段が欲しいよねと。&lt;/p&gt;</content:encoded></item><item><title>CSS設計における、すべてがコンポーネントであるという誤謬</title><link>https://yuheiy.com/2022-06-11-css-components</link><guid isPermaLink="true">https://yuheiy.com/2022-06-11-css-components</guid><description>後日追記: WEB+DB PRESS Vol.133でさらに詳しく書いた。</description><pubDate>Sat, 11 Jun 2022 10:55:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;後日追記:&lt;/strong&gt; &lt;a href=&quot;/2023-02-06-tailwind-css-in-wdpress&quot;&gt;WEB+DB PRESS Vol.133でさらに詳しく書いた&lt;/a&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;BEMによってもたらされた、コンポーネントベースのアプローチでは、「ページはコンポーネントの集合によって表現されるべきであり、ページに含まれるのはすべてがコンポーネントである」と考える。しかしこれまでCSSを書いてきた経験から、これではデザイン意図をまともに表現することができないと感じ始めた。なぜなら、普通デザイナーはページのすべてがコンポーネントであるとは考えないからだ。&lt;/p&gt;
&lt;p&gt;もちろんページの構成要素のなかには、明らかにそれが「コンポーネント」であると意識して作られたものもある。ただしそれは一部であり、全部ではない。「コンポーネントもあれば、コンポーネントではないものもある」という感覚のほうが普通なのだ。&lt;/p&gt;
&lt;p&gt;典型的なUIライブラリにある、「ザ・コンポーネント」みたいなものだけではページは完成しない。例として、一貫してBEMに則って実装されている、&lt;a href=&quot;https://en.bem.info/methodology/&quot;&gt;BEM公式サイトにあるMethodologyのページ&lt;/a&gt;のコードを見てみれば、「これは本当に僕らが頭のなかに思い描くコンポーネントなのだろうか？」という疑問が浮かぶ。すべてがコンポーネントであるとはこういうことだ。本当に確信をもって、「これこそがコンポーネントである」と認識ができるのは一部分でしかない。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;僕は以前までは、あくまでCSS設計の作法に則って、すべてをコンポーネントとして表現しようと努めてきた。デザイナーが作ったデザインカンプからルールを見い出して自分なりに解釈し、ページに境界を引いて、名前をつけてコンポーネントとして分解する。これを隅から隅まで徹底した。その甲斐あってか、この設計はある程度は機能しているかに思えた。&lt;/p&gt;
&lt;p&gt;しかしある時、テンプレートの条件分岐が増えて、複雑な仕様になっていくにつれて、なんとなくうまくいかない感じが浮かび上がってきた。たとえばそれは——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ひとつのコンポーネントとして表現していたセクションが分かれて別々の箇所に配置されることになったので、コンポーネントをふたつに分解して作り直すことになったり&lt;/li&gt;
&lt;li&gt;だいたい同じような感じだけど、微妙に違う——それも偶然似ているだけなので共通化できないパターンが登場して、個別にコンポーネントを何種類も作ることになったり&lt;/li&gt;
&lt;li&gt;部分的に見るとこれまで繰り返し何度も登場しているように思えるスタイルなのに、コンポーネントとしては別物なので、都度新しくコンポーネントを書き足さないといけなかったり&lt;/li&gt;
&lt;li&gt;本当にちょっとした要素を追加するために、ちょうどいい既存のコンポーネントが存在しなければ、わざわざそれだけのために新しいコンポーネントを作ることになったり&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;というようなわけで、見た目にはたいしたことのない変化のために、結果として、割に合わない膨大な数のコンポーネントができあがってしまった。これはきっと、なにかが間違っている。&lt;/p&gt;
&lt;p&gt;問題は、すべてがきっちりとコンポーネント化されたデザインができていないこと、ではない。デザインとコードとで、ルール化の観点がズレていることだ。&lt;/p&gt;
&lt;p&gt;この場合、CSS設計上のコンポーネントの多くが、デザイナーの頭のなかにあるイメージと非対称的な構造になっている。エンジニアの頭のなかにしかない、元のデザインを独自に解釈した「別物」を作り出してしまっている。つまりこれでは、変更に対応できるかという以前に、現状に対応できていない。今のデザインが正しく表現されていない。&lt;/p&gt;
&lt;p&gt;たまたま近くにあっただけのものをひとつのコンポーネントとしてまとめたり、偶然そうなっているだけのものに意図を見い出して抽象化したりして、変更が発生してから、間違った構造化がなされていたことに気づく。あるいは、もっと細かなルールが見落とされてしまう。こうした取り違えが、コンポーネントありきの発想によって誘発される。コンポーネント化するということは、解釈を加えることだ。だから、変更に対応するたびに、間違った解釈を修正するという不必要な痛みが伴う。&lt;/p&gt;
&lt;p&gt;CSS設計は、コンポーネントというものに固執しすぎていた。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;つまりこれは、実装技術側の方法論的問題だ。すべてをコンポーネントにするアプローチ自体に無理がある。本当にそれがコンポーネントだと言い切ることができなかったとしても、コンポーネントとして表現する以外にやりようがない。これが設計を歪ませるのだ。ではどうするか。&lt;/p&gt;
&lt;p&gt;ページはもっと「一緒くた」になっていると考えていいと思う。不可分な要素もあって然るべきであり、必要を超えた構造化は害になる。その方が現実に即している。&lt;/p&gt;
&lt;p&gt;コンポーネントとは別に、より即物的な表現方法が必要だ。そういう意味でも、&lt;a href=&quot;https://yuheiy.hatenablog.com/entry/2020/05/25/021342&quot;&gt;ユーティリティファースト&lt;/a&gt;なのだ。今やBootstrapでさえ、ユーティリティファーストとまではいかなくても、&lt;a href=&quot;https://getbootstrap.com/docs/5.2/utilities/api/&quot;&gt;大量のユーティリティクラスを取り入れている&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;ビジュアル的な観点では、デザインルールはコンポーネントごとに「区切られている」わけではない。サイト全体など特定のコンテキストにおいて一貫した印象を与えるために、意図的に共有している要素がある。それはたとえば、タイポグラフィや余白、色やグラデーション、形状や影といったもの。ページ上のあらゆる要素は、コンポーネントであるかどうかという以前に、基本としてこれらのマナーに則っている。&lt;/p&gt;
&lt;p&gt;だとすれば、セレクタという観点においても、これらそのものを直接的に指し示すクラスがあってもいいはずだ。わざわざコンポーネントのクラスを経由する必要はない。必ずしも「セマンティック」なクラス名でなかったとしても、これもまたひとつのルールである。&lt;/p&gt;</content:encoded></item><item><title>本文のタイポグラフィとCSS</title><link>https://paper.dropbox.com/doc/CSS-wPD007Sd9dSeEDLP78jri</link><guid isPermaLink="true">https://paper.dropbox.com/doc/CSS-wPD007Sd9dSeEDLP78jri</guid><pubDate>Fri, 27 May 2022 00:00:00 GMT</pubDate></item><item><title>yuheiy.com-v6</title><link>https://yuheiy.com/2022-05-03-yuheiy.com-v6</link><guid isPermaLink="true">https://yuheiy.com/2022-05-03-yuheiy.com-v6</guid><description>このウェブサイトをリニューアルした。これまでも何度かリニューアルを重ねてきており、今回でバージョン6ということになる。サイトとしては今年で7年目。</description><pubDate>Tue, 03 May 2022 13:50:00 GMT</pubDate><content:encoded>&lt;p&gt;このウェブサイトをリニューアルした。これまでも何度かリニューアルを重ねてきており、今回でバージョン6ということになる。サイトとしては今年で7年目。&lt;/p&gt;
&lt;p&gt;ブログを組み込んだのはひとつ前のバージョン5からだ。それ以前は、1ページしかないただのリンク集のようななにかでしかなくて、ブログを書くときには外部のブログサービスを使っていた。その方が、考えないといけないことが少なくて済む。でも、ずっとやってるとそれに飽きてきた。やはりブログも自分で作った方が楽しいと思ったので、こうして丸ごとサイトに含めることにした。&lt;/p&gt;
&lt;p&gt;そして、それから2年弱くらいこのサイトを運用してきて、そろそろ作り直したい気持ちになってきた。前回リニューアルしたときから少し僕の考え方が変わっているし、技術的にもいろいろ試したいものが出てきたので、ちょうどまとまった時間が取れたこの連休でリニューアルした。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;まず、サイト全体としてかなり質素なスタイルにした。かつ、ページの構成要素も最小限に。ブログにとって重要なのは、その内容について参照するために、どこからかリンクされることであって、それに当たって余計な要素はできるだけ排除したかった。サイトとしてのナビゲーションとか、そのサイトらしさみたいな、サイトとしての都合、つまり単一リソースとして見たときに関係のない部分は不要だということだ。&lt;/p&gt;
&lt;p&gt;そう考えると、ブログにはサイトのタイトルすら不要に思えるが、これは残すことにした。ブログという情報を解釈する前提として、それを「誰が言っているのか」という点は欠かせないからだ。同じことを言っても、それを誰が言うかによって意味は違ってくる。どういった背景を踏まえた意見なのかが異なるから。このサイトでは筆者の名前がそのままサイト名になっているので、ページの上部にそれを配置した。&lt;/p&gt;
&lt;p&gt;次に、前のバージョンでは文字のジャンプ率がやや高めになっていたが、あまり差をつけすぎないように変更した。こういったコントラストは基本的に読み飛ばしを助長する方向に働く。読まずにわかった気にさせる。そもそもウェブはそういうものだと言われるかもしれないが、少なくともこの場所ではそれに抗っていたいと思う。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;技術的な部分での変更点はいろいろあるので、箇条書きで簡単に挙げてみる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EleventyからAstroに移行&lt;/li&gt;
&lt;li&gt;NetlifyからVercelに移行&lt;/li&gt;
&lt;li&gt;Value DomainからGoogle Domainsに移行&lt;/li&gt;
&lt;li&gt;Tailwind CSSを導入&lt;/li&gt;
&lt;li&gt;ダークモード対応
&lt;ul&gt;
&lt;li&gt;セマンティックな色指定ができるようにTailwind CSSを設定&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://terkel.jp/archives/2021/05/em-based-grid/&quot;&gt;カラム幅を文字サイズの整数倍&lt;/a&gt;にするようにした&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:visited&lt;/code&gt;のスタイルを追加&lt;/li&gt;
&lt;li&gt;&lt;code&gt;viewport-fit=cover&lt;/code&gt;を適用&lt;/li&gt;
&lt;li&gt;コードブロック内でのシンタックスハイライトを有効化&lt;/li&gt;
&lt;li&gt;マークダウンファイル内に記述した&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;要素に&lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt;と&lt;code&gt;decoding=async&lt;/code&gt;属性を自動で付与するようにした&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;要素にサイトタイトルを含めないようにした&lt;/li&gt;
&lt;li&gt;ホームに最新記事のリストを追加&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;詳しくは、&lt;a href=&quot;https://github.com/yuheiy/yuheiy.com-v6&quot;&gt;GitHubリポジトリ&lt;/a&gt;を参照してほしい。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;また言うまでもなく、至るところで&lt;a href=&quot;https://hail2u.net/&quot;&gt;Hail2u&lt;/a&gt;と&lt;a href=&quot;https://terkel.jp/&quot;&gt;terkel.jp&lt;/a&gt;の影響を受けている。&lt;/p&gt;</content:encoded></item><item><title>Tailwind CSSで引数のあるMixinのような仕組みを作る方法</title><link>https://yuheiy.com/2022-03-21-mixins-that-take-arguments-in-tailwind-css</link><guid isPermaLink="true">https://yuheiy.com/2022-03-21-mixins-that-take-arguments-in-tailwind-css</guid><description>後日追記: このブログの内容を再考して「Tailwind CSSで引数のあるMixinのような仕組みを作る方法（改）」として書き直した。</description><pubDate>Mon, 21 Mar 2022 08:35:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;後日追記:&lt;/strong&gt; このブログの内容を再考して「&lt;a href=&quot;/2024-03-16-mixins-that-take-arguments-in-tailwind-css&quot;&gt;Tailwind CSSで引数のあるMixinのような仕組みを作る方法（改）&lt;/a&gt;」として書き直した。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Sassでは、引数の値に応じて宣言をクラスに注入できるMixinの機能がある。たとえば次のようにすれば、フォントサイズがビューポートの幅に応じて流動的に変化するように実装できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@function&lt;/span&gt;&lt;span&gt; rem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$px&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@function&lt;/span&gt;&lt;span&gt; fluid-size&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$min-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$min-width:&lt;/span&gt;&lt;span&gt; 640&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-width:&lt;/span&gt;&lt;span&gt; 1280&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  $v&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$max-size&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; $min-size&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$max-width&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; $min-width&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  $r&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;$min-width&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; $max-size&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; $max-width&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; $min-size&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$min-width&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; $max-width&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @return&lt;/span&gt;&lt;span&gt; clamp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;($min-size)}&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#{$v &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;vw&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; #{&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;($r)}&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;($max-size)}&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@mixin&lt;/span&gt;&lt;span&gt; fluid-text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$min-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$min-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-width&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;fluid-size&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$min-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-size&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$min-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$max-width&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  @include&lt;/span&gt;&lt;span&gt; fluid-text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;64&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Tailwind CSSを使う場合でも、独自のプラグインを実装することで似たような仕組みが実現できる。&lt;/p&gt;
&lt;p&gt;Tailwind CSSによってデフォルトで提供されているクラス群は、内部的には、すべてプラグイン機構を使って実装されている。プラグインの設定によって、クラス名のプリフィックスを決めたり、受け取った値に応じてどのような宣言を適用するかの処理をしたりしている。&lt;/p&gt;
&lt;p&gt;プラグインはユーザー側でも簡単に作成できるようになっている。&lt;code&gt;tailwind.config.js&lt;/code&gt;に次のようなコードを書いていくだけだ。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tailwindcss/plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;addUtilities&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;addComponents&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;prefix&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      // Add your custom styles here&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;上記のコード例は公式ドキュメントからの引用。&lt;a href=&quot;https://tailwindcss.com/docs/plugins&quot;&gt;APIについても公式ドキュメントで解説されている&lt;/a&gt;ので、適宜参照されたし。&lt;/p&gt;
&lt;p&gt;これを使って、冒頭で例示したMixinのようなものを実装してみる。次のようなクラス名を記述することで意図した機能が実現できるようにする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;fluid-text-[24px,32px]&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;...&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;プラグインのAPIにある&lt;code&gt;matchUtilities&lt;/code&gt;関数を使うと、特定のクラス名の後ろ側に付与された値に応じてユーティリティクラスを生成する仕組みができる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fluidText&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;matchUtilities&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  matchUtilities&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;fluid-text&apos;&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;minSize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxSize&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;,&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; matched&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)px&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(v);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;matched) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&quot;${&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;}&quot; is not a valid value`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; Number&lt;/span&gt;&lt;span&gt;(matched[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &apos;font-size&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;getFluidSize&lt;/span&gt;&lt;span&gt;(minSize, maxSize),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;[]&lt;/code&gt;で囲われた文字列はそのままプラグインに渡される。&lt;code&gt;,&lt;/code&gt;で区切られた箇所も、配列に変換されたりするわけではなく、単純に&lt;code&gt;45rem,2rem&lt;/code&gt;という文字列を受け取ることになる。その文字列をプラグインの実装で分割する。便宜上&lt;code&gt;,&lt;/code&gt;で区切っているだけであって、このルールはどこかで決まっているものではない。&lt;/p&gt;
&lt;p&gt;そして、このプラグインを使えば、次のようなCSSが生成される。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.fluid-text-&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;24px&lt;/span&gt;&lt;span&gt;\2c&lt;/span&gt;&lt;span&gt; 32px&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  font-size&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1.25&lt;/span&gt;&lt;span&gt;vw&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt;の全体のコードは次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;tailwindcss/plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; rem&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; `${&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; 16&lt;/span&gt;&lt;span&gt;}rem`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; getFluidSize&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;minSize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxSize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;minWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 640&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxWidth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1280&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; (maxSize &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; minSize)) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (maxWidth &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; minWidth);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; r&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (minWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; maxSize &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; maxWidth &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; minSize) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (minWidth &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; maxWidth);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; `clamp(${&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;minSize&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}, ${&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;}vw + ${&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}, ${&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;maxSize&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;})`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; fluidText&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;matchUtilities&lt;/span&gt;&lt;span&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  matchUtilities&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;fluid-text&apos;&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;minSize&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxSize&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; value.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;,&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; matched&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)px&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(v);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;matched) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&quot;${&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;}&quot; is not a valid value`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; Number&lt;/span&gt;&lt;span&gt;(matched[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &apos;font-size&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;getFluidSize&lt;/span&gt;&lt;span&gt;(minSize, maxSize),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [fluidText],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;プラグインを使えば、ほかにもいろいろなことができる。少なくとも、Tailwind CSSがデフォルトで提供しているようなことはすべて実現できる。&lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/blob/master/src/corePlugins.js&quot;&gt;&lt;code&gt;corePlugins.js&lt;/code&gt;&lt;/a&gt;の実装を眺めてみてもらえればイメージしやすいと思う。&lt;a href=&quot;https://tailwindcss.com/docs/typography-plugin&quot;&gt;公式プラグイン&lt;/a&gt;の実装も参考になる。&lt;/p&gt;</content:encoded></item><item><title>ブックマーク代わりにツイートしていた7000件のURLを精査してEvernoteに移行した</title><link>https://yuheiy.com/2022-03-13-from-tweets-to-evernote</link><guid isPermaLink="true">https://yuheiy.com/2022-03-13-from-tweets-to-evernote</guid><description>なにか気になったウェブページを保存したいというとき、以前ははてなブックマークを使っていた。たぶん2017年くらいまで。</description><pubDate>Sat, 12 Mar 2022 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;なにか気になったウェブページを保存したいというとき、以前ははてなブックマークを使っていた。たぶん2017年くらいまで。&lt;/p&gt;
&lt;p&gt;しかし当時のそのインフラ的な事情か、ブックマークをしようとしてもエラーが発生してできない場面が頻発していた。いや、実際そこまでの頻度ではなかったのかもしれないが、僕がそこそこヘビーユーザーだったために出会す機会が多かった気がする。いずれにせよ、それが嫌になって徐々にはてブを使わないようになっていった。&lt;/p&gt;
&lt;p&gt;代わりに、気になったページはTwitterでシェアするようになった。自分用ブックマークを兼ねたシェア。ツイートはすぐに流れていってしまうので記録にはならないと考えるのが普通だと思うけど、僕の場合は、自分の過去のツイートを検索して見つけるという使い方をしてきた。&lt;/p&gt;
&lt;p&gt;たとえば、「&lt;a href=&quot;https://adamsilver.io/blog/buttons-shouldnt-have-a-hand-cursor/&quot;&gt;Buttons shouldn’t have a hand cursor&lt;/a&gt;」みたいな記事があったなと思えば、「&lt;a href=&quot;https://x.com/search?q=from%3A_yuheiy%20cursor&amp;amp;src=typed_query&quot;&gt;from:_yuheiy cursor&lt;/a&gt;」辺りのクエリで調べれば見つけられる。ただこれをやるには、記事タイトルにわかりやすいキーワードが含まれていたり、けっこう精度高くタイトルを覚えていたりしないといけない。&lt;/p&gt;
&lt;p&gt;僕はこういう情報の手がかりを人より多少覚えているほうなので、ある程度はこの運用でなんとなかってきた部分があったけど、とはいえ見つけられなかった場面も多い。だからこのやり方をこの先も続けていくには無理があるなと思いつつも、別のやり方に乗り換えるのが面倒くさくて先延ばしにし続けていた。けれど、さすがにそろそろなんとかしようという気になってきて重い腰を上げることにした。&lt;/p&gt;
&lt;p&gt;まずは移行先のサービスを選定する。過去の記録を掘り返すために検索性能を重視しつつ、オープンに記録するには微妙なものもあるのでこれからはプライベートなスペースを選びたい。そこで候補になるサービスとして、記録したページの全文検索ができる「Pocket」「Evernote」「Notion」という3つを挙げてみた。&lt;/p&gt;
&lt;p&gt;Pocketははてブと同じくブックマーク専用ツール。EvernoteとNotionはざっくり言うと文書全般を扱う汎用ツールという感じで、ページを記録するためにはそれぞれにあるWebクリッパーという機能を使う。Webクリッパーでは、ページのコンテンツをスクレイピングしてアーカイブすることになるので、もし記録したページが消滅しても参照し続けられる。Pocketの場合でも、有料会員なら永久保存ができる。どのサービスでも、ページを丸ごと保存するというわけではなくて、本文らしき領域を自動的に判別して、簡易的な形式で取り込んでくれる。&lt;/p&gt;
&lt;p&gt;また、EvernoteとNotionではWebクリッパーでスクレイピングのアプローチが違う。&lt;/p&gt;
&lt;p&gt;いずれもブラウザ拡張をインストールした上で使うのだが、Evernoteの場合はそのブラウザでそのまま見えている状態を記録する。たとえば、開発者ツールでページの内容を書き換えてからWebクリッパーを実行すると、書き換えた後の内容が記録されることになる。ログインが必要なページなどを開いている場合でも同様で、ログインしないと表示できないようなコンテンツも記録できる。また保存形式を選ぶこともできて、ページを丸ごと保存するか、記事として簡易的な見た目で保存するか、たんにブックマークにするか、などを選べる。&lt;/p&gt;
&lt;p&gt;一方Notionの場合、Webクリッパーを実行すると、ユーザーの手元のブラウザ経由でスクレイピングするのではなく、表示中のページのURLをNotionのWeb APIに送信して、URLだけを手がかりにしてスクレイピングする。したがって、ログインが必要なページのコンテンツを記録することができない。&lt;/p&gt;
&lt;p&gt;検索機能についても比較してみる。いずれも全文検索が強みに思えるが、Notionの場合、キーワードがマッチする箇所ごとに1件の検索結果として表示されるようになっている。たとえばひとつのページに「cursor」というキーワードが18回登場する場合、検索結果としては同じページが18件分表示されることになる。これではかなり探しづらい。マッチする箇所をひとつひとつ確認したいわけではなくて、先に目当てのページを見つけたいがために検索するわけなので、フォーカスする粒度が合っていない。PocketとEvernoteでは、同じページが検索結果に複数回登場するということはない。&lt;/p&gt;
&lt;p&gt;こういった尺度で検討してみて、最終的にはEvernoteに移行することに決めた。Notionは検索機能の面で無しとして、Pocketと比べたときに、Evernoteではログインが必要なページでも保存できるようになっていることと、それにかぎらずあらゆるものをストックするのに向いた設計になっていることが決め手になった。&lt;/p&gt;
&lt;p&gt;そういうわけで、これからのブックマークはEvernoteで記録していく、ということでもいいのだが、僕としてはこれまで長きにわたってツイートしてきた大量のページたちをなかったことにするのが惜しいので、どうにかして移行したい。検索機能が強化されればもっと有用に役立てられるリソースも増えるはずなので。しかし正当なやり方はたぶんないので、泥臭くやっていく。&lt;/p&gt;
&lt;p&gt;まず最初に、これまでの全ツイートのアーカイブをダウンロードする。Twitterの設定画面から「データのアーカイブをダウンロード」をリクエストすると、24時間くらい経ってから、アカウントのあらゆるデータが含まれたファイルをダウンロードできるようになる。&lt;/p&gt;
&lt;p&gt;アーカイブはHTMLでできたSPAになっていて、ブラウザで開くとビューアーとして機能する。ツイートのXMLやJSONが含まれているわけではないけど、いちおうツイートのデータだけを含んで切り出された次のようなJavaScriptのファイル（&lt;code&gt;data/tweet.js&lt;/code&gt;）があるので、それをもとにしていろいろ処理していく。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;window.&lt;/span&gt;&lt;span&gt;YTD&lt;/span&gt;&lt;span&gt;.tweet.part0 &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  { &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;まずはこの中から、ツイートしたURLのみを抽出したい。Twitterではツイートの本文データの中にURLもいっしょに含まれているので、URLのみを抽出するためにはTwitter公式のパーサーである&lt;a href=&quot;https://github.com/twitter/twitter-text&quot;&gt;twitter-text&lt;/a&gt;を使う。&lt;/p&gt;
&lt;p&gt;すると、これまでの全67000件のツイートのうち、25000件にURLが含まれていた。体感的にこれは多すぎるなと思って確認してみたところ、リツイートや画像ツイートの分のURLも含まれてしまっているようだ。これらは不要なので除去する。&lt;/p&gt;
&lt;p&gt;ツイート本文内に保存されるURLはすべて「&lt;a href=&quot;https://t.co/%E3%80%8D%E3%81%8B%E3%82%89%E5%A7%8B%E3%81%BE%E3%82%8BTwitter%E7%94%A8%E7%9F%AD%E7%B8%AEURL%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%A7%E3%80%81%E7%9B%AE%E7%9A%84%E3%81%AEURL%E3%82%92%E5%88%A4%E5%88%A5%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AB%E3%81%B2%E3%81%A8%E9%80%9A%E3%82%8A%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88%E5%85%88%E3%81%AEURL%E3%81%AB%E5%8A%A0%E5%B7%A5%E3%81%99%E3%82%8B%E3%80%82%E3%81%9D%E3%81%AE%E5%BE%8C%E3%80%81%E3%80%8Chttps://x.com/%E3%80%8D%E3%81%8B%E3%82%89%E5%A7%8B%E3%81%BE%E3%82%8BURL%E3%82%92%E9%99%A4%E5%8E%BB%E3%81%99%E3%82%8B%E3%81%A8%E3%81%84%E3%81%86%E6%B5%81%E3%82%8C%E3%80%82%E3%81%9D%E3%82%8C%E3%81%AB%E5%8A%A0%E3%81%88%E3%81%A6%E3%80%81%E3%81%93%E3%82%8C%E3%81%BE%E3%81%A7%E3%81%AB%E3%83%84%E3%82%A4%E3%83%BC%E3%83%88%E3%81%97%E3%81%9FURL%E3%81%AE%E4%B8%AD%E3%81%A7%E9%87%8D%E8%A4%87%E3%81%99%E3%82%8B%E3%82%82%E3%81%AE%E3%82%82%E9%99%A4%E5%8E%BB%E3%81%97%E3%81%9F%E3%80%82&quot;&gt;https://t.co/」から始まるTwitter用短縮URLになっているので、目的のURLを判別するためにひと通りリダイレクト先のURLに加工する。その後、「https://x.com/」から始まるURLを除去するという流れ。それに加えて、これまでにツイートしたURLの中で重複するものも除去した。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;この時点で15000件になった。ページの内容を時系列で飛ばし飛ばしに確認してみて、古いものにはあまり価値がなさそうだなと判断したので、ざっくり半分くらいを捨ててしまって、残りが7000件くらいになった。&lt;/p&gt;
&lt;p&gt;これで残ったものはひと通りEvernoteに登録していきたいところだが、どうやらEvernoteのWebクリッパーには公開Web APIのようなものがないらしい（ちなみにNotionのほうにもなかった）。したがってここから先は手動でやるしかない。Puppeteerとかを使ってブラウザ拡張を自動操作したら無理ではないかもしれないけど、それも簡単ではなさそうなので、今回はこれまでツイートしたURLを振り返る意味もあって自分で登録していくことに決めた。&lt;/p&gt;
&lt;p&gt;残った7000件のURLはすべて&lt;a href=&quot;https://sociomedia.com/textwell/&quot;&gt;Textwell&lt;/a&gt;に放り込む。Textwellにはテキストを処理するためのアクションという機能が用意されていて、ユーザーがJavaScriptをスクリプトを記述することで新たなアクションを登録できるようになっている。これを使って、「上から20行分のURLをブラウザで開いて、その後それらのURLは削除する」というアクションを作成する。後はそれを実行して、開いたページを処理しては、その後はまたアクションを実行する。これを最後までやる。&lt;/p&gt;
&lt;p&gt;これが一番時間がかかって精神的苦痛も伴う作業で、数日では到底終わらない。ひとつひとつのページにざっと目を通して、いらなさそうなものはスルーして、後で見返したい気がするものは保存する。懐かしい気持ちになったりしてちょっと楽しくはあるが、いかんせん量が多すぎるので、まあ大変だった。こういう作業に向いてないからプログラマーをやっているのだ。&lt;/p&gt;
&lt;p&gt;ちなみに、Textwellの代わりにシェルスクリプトでも同じようなことはできるのだけど、あえてTextwellを使っていることには理由がある。僕はいつもメールとかSlackとかのために作文するときにはTextwellを使っているのだが、そのTextwellを大量のURLで占拠してしまうとそうした普段の作業がやりにくくなるので、嫌でも気になって早く終わらせようという気持ちになる。これをもし差し障りのないようなところに置かれたテキストファイルで管理していたとすれば、半端なところで投げ出してしまっていたに違いない。&lt;/p&gt;
&lt;p&gt;しかしその間いろいろ忙しかったりもしたので、けっきょく1ヶ月半くらいかかってしまった。それでも、すでに何度もEvernoteの検索機能と大量の記録に助けられたので、結果オーライだと思う。保存件数は1000件くらいになった。&lt;/p&gt;
&lt;p&gt;加えて、ブラウザの余計なブックマークも減らすことができた。ツイートで済ませてしまうと後から探せなくなる不安が大きいので、いちおう保険としてブラウザのブックマークにも保存しておくかという気になっていたが、Evernoteだと検索性能がかなり信頼できるので、余計な不安を払拭できた。&lt;/p&gt;
&lt;p&gt;ところで、僕がなぜこれまで執拗にブックマークにこだわるのかというと、偶然に任せることでしか摂取できない種類の情報というのがあるからだ。Googleで検索できる情報というのは所詮自分が多少なりとも知っていることだけで、関知しないことについてはキーワードがわからないので調べようがない。だいぶ検索精度が上がっているとはいえ、ゴミも増えている。だから、外部から供給される情報を受動的に浴び続けられるようなチャンネルに接続して、自分が意図しない情報に偶然出会う機会が重要になる。これがなければ能動的な探究もうまくできない。&lt;/p&gt;
&lt;p&gt;そうした場で、少しでも気になる情報に巡り合ったときには、後から振り返ることができるように記録しておかなければならない。今すぐには十分理解できないなと思うような時こそなおさら。後から必要になる時が来るからだ。その時に、「無知の知」として機能する。昔偶然見つけたブログが、自分にとって見ず知らずの領域に立ち入るための入り口になるからだ。&lt;/p&gt;</content:encoded></item><item><title>Utopiaを実際の案件で使ってみて、うまくいったことといかなかったこと</title><link>https://yuheiy.com/2022-01-24-retrospective-on-utopia</link><guid isPermaLink="true">https://yuheiy.com/2022-01-24-retrospective-on-utopia</guid><description>少し前に担当したウェブサイト制作の案件で、Utopiaというツールを使ってみた。Utopiaを使うと、ビューポートの幅に応じて流動的に変化するフォントサイズとスペーシングの値のセットを生成できる。</description><pubDate>Sun, 23 Jan 2022 16:35:00 GMT</pubDate><content:encoded>&lt;p&gt;少し前に担当したウェブサイト制作の案件で、&lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia&lt;/a&gt;というツールを使ってみた。Utopiaを使うと、ビューポートの幅に応じて流動的に変化するフォントサイズとスペーシングの値のセットを生成できる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/utopia-type_scale_graph.DRYUtAbb_Z15slNB.webp&quot; alt=&quot;H1、H2、H3、H4、Pという要素の値が、320pxから1500pxというビューポート幅において流動的に増大する&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/blog/designing-with-fluid-type-scales&quot;&gt;Designing with fluid type scales |
Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;レスポンシブデザインのパターンとして、ビューポートの幅が狭ければテキストのフォントサイズは小さめで、広ければ大きめにしたい、という場面がよくある。そうしたとき、フォントサイズを変更するために、ブレイクポイントごとに値を上書きする設定をしていくと、組み合わせ地獄になってデザインルールが非常に複雑化してしまう。フォントサイズの数とブレイクポイントの数がかけ合わさった分のルールが生まれて、後から整理しようとしても手に負えない状況になる。&lt;/p&gt;
&lt;p&gt;この組み合わせ地獄を回避するために、Utopiaのアプローチでは、まずフォントサイズを実際のピクセル数ではなく、ステップとして捉える。そのステップと対応する実際のフォントサイズは、ビューポートの幅に応じて自動的に導出される、という考え方になる。&lt;/p&gt;
&lt;p&gt;CSSにおいては、特定のステップ数をフォントサイズの値として設定しさえすれば、それだけで自動的にちょうどいいサイズが適用されるようになる。そういう実装にする。結果、ブレイクポイントごとにフォントサイズを設定し直す必要がなくなる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/the-result.DhdDLQq0_Z1ALIBl.webp&quot; alt=&quot;H1、H2、H3、H4、Pという要素の値が、320pxから1500pxというビューポート幅において流動的に増大する&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/blog/designing-with-fluid-type-scales&quot;&gt;Designing with fluid type scales |
Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;フォントサイズの値が変化する始点と終点に当たる数値は、モジュラースケールによって導出される。&lt;/p&gt;
&lt;p&gt;幅が375pxの場合——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基準となるフォントサイズは14px&lt;/li&gt;
&lt;li&gt;モジュラースケールの係数は1.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;幅が1440pxの場合——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基準となるフォントサイズは18px&lt;/li&gt;
&lt;li&gt;モジュラースケールの係数は1.25&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;こういう具合でUtopiaに入力すると、次のように値が出力される。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/calculated-font-sizes.DtP_Yc9Z_Z1V78wR.webp&quot; alt=&quot;スケールのステップが5、4、3、2、1、0、-1、-2と用意されており、ビューポート幅が320pxのときと1140pxのときのフォントサイズが並んでいる&quot; loading=&quot;lazy&quot; width=&quot;1904&quot; height=&quot;1086&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/type/calculator/&quot;&gt;Fluid type scale calculator | Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;フォントサイズだけでなく、似たロジックを使ってスペーシングの値のセットを生成する仕組みも備わっている。これらの値は、マージンや要素のサイズ設定などのために使用できる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/individual-space-values.DZVa3UZS_1LFE89.webp&quot; alt=&quot;3XS、2XS、XS、S、M、L、XL、2XL、3XLというステップごとに、最小値と最大値が並んでいる&quot; loading=&quot;lazy&quot; width=&quot;1904&quot; height=&quot;2082&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://utopia.fyi/space/calculator/&quot;&gt;Fluid Space Calculator | Utopia&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;案件を担当するデザイナーとはUtopiaの考え方について話して、これを使ってフォントサイズとスペーシングの値を設定してもらうことになった。Figmaで作るデザインカンプでももちろんこのルールに基づいてデザインする。&lt;/p&gt;
&lt;p&gt;こうして案件をひとつ完遂したけれど、結論としては、次からは使わないと思う。しかしこれは必ずしもUtopiaのアプローチが合わなかったというわけではなくて、メリデメがいろいろあった上での総合的な評価結果にすぎないので、詳しい経緯を記録しておく。&lt;/p&gt;
&lt;h2&gt;うまくいったこと&lt;/h2&gt;
&lt;p&gt;スタイルとして設定する値について、あらかじめ正しく共通認識を持てるようになったことは普通によかった。いわゆるデザイントークンというやつ。これによって、デザインカンプがより精緻なものになって、「なにが正解なのかわからない状態」をかなり減らすことができた。&lt;/p&gt;
&lt;p&gt;というのも僕の経験では、デザインカンプ上で各要素に適用されているフォントサイズやマージンの値に規則性を見い出せなくて、開発担当者がCSSとしてうまく表現できないような場面がよくある。すると、開発担当者がデザイン仕様をよしなに「調整」した上で実装し、それをデザイン担当者に確認してみて、フィードバックを受けて場合によってはまたそれをやり直すという余計な仕事が発生する。その際には細かい指示が増えがちで、「この箇所だけフォントサイズを2px大きくしたい」とか、「こことここはマージンを揃えて、あっちは少し広げたい」みたいな、人間同士でやるにはストレスフルになるようなやり取りをすることになる。しかしその実は、前工程での検討が不十分だったためとしか言えないような指示も少なくない。&lt;/p&gt;
&lt;p&gt;問題は、設計段階においてその意図が明瞭になっていないこと。判断のブレを是正するための基準が設けられていないという場合が多い。そこで、Utopiaが生成したフォントサイズとスペーシングを導入することによって、判断の枠が設定されると、確然たる判断をするように半ば強制されることになる。そのおかげで、後でやり直したくなるような生煮えの判断を減らせるのだと思う。&lt;/p&gt;
&lt;p&gt;レスポンシブデザインにまつわる事情が絡んでくると、この辺りの判断は余計に曖昧になる傾向があった。前述の組み合わせ地獄によって、ルールはより複雑に、より微妙になっていく。しかしこれについてもUtopiaのおかげで、「値はブレイクポイントにかかわらずステップとして一意に決定される」というシンプルなルールが打ち立てられて、曖昧さを解消できた。&lt;/p&gt;
&lt;p&gt;またレスポンシブデザインにおいては、モバイル向けのデザインとデスクトップ向けのデザインで、スタイルはどの程度個別化されたり共通化されたりするのが妥当かという問題がある。どうにも場合によるところは大きいけれど、それぞれで視覚的な階層構造が共通している限りは、フォントサイズについてはステップ数さえ決まっていれば再設定の必要はほとんどないと言えそうだ。&lt;/p&gt;
&lt;p&gt;開発は複数人で行った。Utopiaの採用によってルールの単純化が実現された結果、担当者ごとの実装のブレはかなり抑えられたように思う。特にレスポンシブデザインを採用する場合の開発では、通常、デザインカンプでは表現されない仕様が大量にあり、開発担当者によってまったく違った実装になってしまうことが珍しくない。こうした個人の解釈に依ってしまう部分がUtopiaによって補完され、いたずらな判断のばらつきを平坦化できた。&lt;/p&gt;
&lt;h2&gt;うまくいかなかったこと&lt;/h2&gt;
&lt;p&gt;すべてのフォントサイズの値が流動的に変化するのはやり過ぎだった。Utopiaを素直に使うと、ビューポートの幅が広がるにつれて、どのフォントサイズも漏れなく拡大されていくのだが、本文よりも小さいフォントサイズまでもいっしょに大きくなっていくのは不都合だった。全部の要素を大きくしたいのではなくて、メリハリをつけるためにジャンプ率を高めたいという場面が多いので、小さいテキストはそのままでいいことになる。&lt;/p&gt;
&lt;p&gt;加えて、モジュラースケールはやはり使い勝手がよくなかった。&lt;a href=&quot;https://standard.shiftbrain.com/blog/music-math-typography&quot;&gt;「音楽、数学、タイポグラフィ」で指摘された&lt;/a&gt;ように、モジュラースケールでは導出される値の間隔はまばらになるが、より使用頻度が高いのは本文のフォントサイズ周辺の値だ。本文を中心としたデザインであればあるほど、そこからの差異を表現するために必要なギャップは少しでよくなる。それなのに、ステップ同士の間隔が空きすぎているとその辺りの調整がやりづらいことになる。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/scales.B6k1PnKu_ZFb85f.webp&quot; alt=&quot;調和数列にもとづくスケールでは、導出されるフォントサイズは16px近辺に集中しているが、等比数列にもとづくスケールでは、比較的均等にばらけている&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;
&lt;figcaption&gt;—&lt;cite&gt;&lt;a href=&quot;https://standard.shiftbrain.com/blog/music-math-typography&quot;&gt;音楽、数学、タイポグラフィ -
シフトブレイン／スタンダードデザインユニット&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;スペーシングについては、Utopiaの値を使いつつも、けっきょくブレイクポイントで上書きする場面が多かった。フォントサイズと違って、マージンなどについては、一般化したルールに則ってやるのが難しかったということだ。&lt;/p&gt;
&lt;h2&gt;次に試していること&lt;/h2&gt;
&lt;p&gt;フォントサイズにまつわるルールの複雑化を回避するという意味では、Utopiaのように値を流動的に変化させるアプローチは別に必須ではない。ブレイクポイントごとにフォントサイズを上書きするにしても、その組み合わせをパターン化することさえできていれば問題にはならない。&lt;/p&gt;
&lt;p&gt;たとえば「ヘッドライン」というパターンを作って、モバイルの幅では40px、デスクトップの幅では48pxというルールを定めておく。同様に、「ボディ」や「キャプション」なども追加した上で、テキストのスタイルは必ずこれらを参照して決定されるということにすればよい。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/adaptive-type-scale.BL06OY0E_ZlxgXD.webp&quot; alt=&quot;タイプスケールの値がデバイス間で自動的に変化するようになっている&quot; loading=&quot;lazy&quot; width=&quot;4096&quot; height=&quot;2270&quot; /&gt;
&lt;figcaption&gt;— 
&lt;cite&gt;&lt;a href=&quot;https://m3.material.io/styles/typography/overview&quot;&gt;Typography – Material Design 3&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;これらのパターンのなかに、行の高さやトラッキングなどもいっしょに含めておくと、さらにルールの一貫性を高めることができるし、開発も楽になる。いま担当している案件では、デザイナーがページの制作を進める傍らで、こうしたタイプスケールも定義してもらっている。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/type-scale.BRmP-O4Y_26goGk.webp&quot; alt=&quot;H1、H2、Subtitle、Body、Captionのようなカテゴリごとに、Typeface、Weight、Size、Case、Letter
spacingの値が設定されている&quot; loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;1025&quot; /&gt;
&lt;figcaption&gt;— &lt;cite&gt;&lt;a href=&quot;https://material.io/design/typography/the-type-system.html#type-scale&quot;&gt;The type system - Material
Design&lt;/a&gt;&lt;/cite&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;さらに、タイプスケールやスペーシングだけでなく、そのほかのいろいろな値（デザイントークン）を一元管理できればなおよい。そこで、&lt;a href=&quot;https://jansix.at/resources/figma-tokens&quot;&gt;Figma Tokens&lt;/a&gt;というプラグインを導入することにした。これを使えば、テキストやスペーシングに限らず、カラーパレットやボーダー、不透明度などもトークンとして管理できるようになる。Figma Tokensにトークンを設定すると、それらに対応するボタンが出現して、クリックするとレイヤーに適用できるという仕組みになっている。トークンの設定はJSONで入出力できるので、&lt;a href=&quot;https://gist.github.com/yuheiy/e1fc01fc0a4816924d1959221fdba46c&quot;&gt;Tailwind CSSのデフォルト値をJSON化&lt;/a&gt;し、デザイナーと共有している。&lt;/p&gt;</content:encoded></item><item><title>『Every Layout』に到った背景と思想についての私の解釈</title><link>https://note.com/shiftbrain/n/n0a01726673e0</link><guid isPermaLink="true">https://note.com/shiftbrain/n/n0a01726673e0</guid><pubDate>Mon, 27 Dec 2021 00:00:00 GMT</pubDate></item><item><title>「Every Layout」をめぐる座談会</title><link>https://www.codegrid.net/series/2021-talk-about-every-layout</link><guid isPermaLink="true">https://www.codegrid.net/series/2021-talk-about-every-layout</guid><pubDate>Thu, 16 Dec 2021 00:00:00 GMT</pubDate></item><item><title>僕と梅、男梅シート</title><link>https://yuheiy.com/20211206-ume</link><guid isPermaLink="true">https://yuheiy.com/20211206-ume</guid><description>昔から梅が好きでよく食べる。実家にいたころは、口寂しくなるたびに冷蔵庫を開け、おやつ感覚で梅干しを食べては、塩分の摂りすぎだからやめろと母親に言われていた。ご飯を食べるときにもよく梅干しをおかずにしていた。</description><pubDate>Sun, 05 Dec 2021 19:55:00 GMT</pubDate><content:encoded>&lt;p&gt;昔から梅が好きでよく食べる。実家にいたころは、口寂しくなるたびに冷蔵庫を開け、おやつ感覚で梅干しを食べては、塩分の摂りすぎだからやめろと母親に言われていた。ご飯を食べるときにもよく梅干しをおかずにしていた。&lt;/p&gt;
&lt;p&gt;梅のジュースもよく飲む。最近はどこでも見かけるようになったけど、昔はけっこう珍しかった気がする。見つけたら買っておいてもらって、家ではそれがなくなるまで毎日飲んでいた。今でも見かけるたびに買って飲む。&lt;/p&gt;
&lt;p&gt;テレビで毎日のように流れていた梅酒のCMを見るたびに、いったいこれはどういうものなんだろうとずっと気になっていた。大人になってからは、飲みに行くたびにほぼ毎回梅酒を頼んでいる。梅干しサワーを頼むこともあるけど、梅酒のほうが好き。&lt;/p&gt;
&lt;p&gt;駄菓子屋ではいつもカリカリ梅を買って食べていた。母親といっしょに遠くのスーパーに行ったときは、そこでしか売ってない「スッパイマン」を買ってもらっていた。家からは少し遠いコンビニが行動圏内に入ってきたころには、干し梅をよく買うようになった。&lt;/p&gt;
&lt;p&gt;男梅が発売開始したときはちょっとした衝撃だった。当時、はちみつ梅至上主義とも言える風潮があった商品棚の中で、あえてその酸っぱさを前面に出したback to basicsな感じはむしろ新鮮だった。&lt;/p&gt;
&lt;p&gt;一方で、ねりうめ的なジャンルにはいくどか挑戦してきたけどなかなか馴染めなかった。グミとか、梅干しシートとかも同じで。あの手の製品って、梅酒くらいにほぼ別物として振り切っているというほどでもないんだけど、梅そのものともちょっと距離がある感じがして、どないやねんと思ってしまっていた。あの形式を目指す限り、たぶんなにか加工の技術的問題とかで、どうにもいい感じにはならないものなんだろう、と長年思い込んでいた。&lt;/p&gt;
&lt;p&gt;しかし4年くらい前に登場した男梅シートを試してみて、これはまだ捨てたものじゃないと直観した。たんにコンセプト的問題だったのだと。けれど当時はリピートしたいと思えるほどの出来ではなかった。それからしばらく時間が経って、最近になってからまた男梅シートが目に入ったので試してみたところ、シート的形状でありながら、ほぼ男梅そのものというレベルに達していた。これなのだ、求めていたものは。そして、男梅の本質が凝縮されている。男梅よりも男梅なのだ。モノマネで言うところの本物より似ているというやつ。このエントリーを書き切るまでに3枚食べた。&lt;/p&gt;
&lt;p&gt;追伸: 男梅シートのパッケージは4年前から変わっていないようなので、そのころからおいしさが変わったのかどうかは実際のところわからない。製法が変わったのか、僕の味覚が変わったのか。また、男梅シートの製法はほかの梅干しシート系の製品とはだいぶ違っていそうな感じもするので、単純比較はできないかもしれない。&lt;/p&gt;</content:encoded></item><item><title>文の途中でも流れを読み誤られないように作文することを心がけている</title><link>https://yuheiy.com/20211204-sentence</link><guid isPermaLink="true">https://yuheiy.com/20211204-sentence</guid><description>日本語の文では、文末に到達するまで話の方向性が読み切れないことがある。文を読むときに、途中まで読んだところで「あー、はいはい」となっていても、最後まで読み進めると「あれ、そういう話？」という感じになり、流れを読み誤っていたことに後で気づく。人がしゃべるのを聞いているときにはより顕著になる。しゃべる速度は読む速度よりも遅いので、どうしても焦ったくなってしまうものだ。</description><pubDate>Sat, 04 Dec 2021 15:05:00 GMT</pubDate><content:encoded>&lt;p&gt;日本語の文では、文末に到達するまで話の方向性が読み切れないことがある。文を読むときに、途中まで読んだところで「あー、はいはい」となっていても、最後まで読み進めると「あれ、そういう話？」という感じになり、流れを読み誤っていたことに後で気づく。人がしゃべるのを聞いているときにはより顕著になる。しゃべる速度は読む速度よりも遅いので、どうしても焦ったくなってしまうものだ。&lt;/p&gt;
&lt;p&gt;一方で英語の文では、最初にまずシンプルな表現で言い切ってから、補足的な言葉は後ろに修飾されていくので、大筋から順に話の方向性が確定していく傾向にある。これは便利だと思う。というのもこの形式であれば、言葉のひとつひとつを見聞きしたそばからすぐに解釈を開始できる。つまり、受け手にとっての思考のロスを削減することに加えて、より速く伝えられるようになるはずだ。&lt;/p&gt;
&lt;p&gt;僕は日本語の作文においてもそれを実現すべく、言葉が並んでいる順に解釈できる文体を目指している。受け手が思う流れを裏切らないために、まず端的に言い切ったり、倒置法を活用したり、語順や言葉のまとまりを調整したり、などなど。&lt;/p&gt;
&lt;p&gt;翻訳作業を経てから意識するようになったことのひとつ。&lt;/p&gt;</content:encoded></item><item><title>これはこれで逆に広げられない話は書けないみたいな縛りになっている</title><link>https://yuheiy.com/20211126-nothing</link><guid isPermaLink="true">https://yuheiy.com/20211126-nothing</guid><description>いつからかそういう癖がつき始めた。なんでもかんでもを「そのまま」書くわけにはいかないからという意識からなんだけど。散歩してるとかシャワーを浴びてるときみたいに、頭が暇な時間がありすぎると、考えがそっちの方向に進みすぎて、ざっと記事化するには手に負えない感じになってしまうことがある。ちょうどいい抽象度で書きたいことをドンピシャで書けるようなタイミングもあるんだけど、そういうときって今みたいな「まだそのときじゃない」と見送ってきたピースがちょうど繋がったときでもあるかもしれないな、ということに、今まで書いていて気づいた。しかしそれを感じるタイミングは割と頻繁にあって、「このタイミングは本当にマジのやつか？」があまりわからない。いやあるいは、あるテーマを考え終えるとき、別のテーマに頭が移っていく前のタイミングというのが、自分の中で一番満足するまで考え切ったタイミングだ、と言えるだろうか。それがマジのやつだろうか。</description><pubDate>Fri, 26 Nov 2021 13:45:00 GMT</pubDate><content:encoded>&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;「言葉は違いから生まれる」というテーマで今日の分を書こうとしたけどあんまり的を射たことを言えそうにないのでやめておいた&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1464216216240025600?ref_src=twsrc%5Etfw&quot;&gt;November 26, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;「アイデアはメモリが解放されたときに生まれる」というテーマで一昨日の分を書こうとしたけどあんまり的を射たことを言えそうにないのでやめておいた&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1464216323383504896?ref_src=twsrc%5Etfw&quot;&gt;November 26, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;「一般化しすぎると言うことがなくなる」というテーマで今日の分を書こうとしたけどあんまり的を射たことを言えそうにないのでやめておいた&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1464217138357743616?ref_src=twsrc%5Etfw&quot;&gt;November 26, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;いつからかそういう癖がつき始めた。なんでもかんでもを「そのまま」書くわけにはいかないからという意識からなんだけど。散歩してるとかシャワーを浴びてるときみたいに、頭が暇な時間がありすぎると、考えがそっちの方向に進みすぎて、ざっと記事化するには手に負えない感じになってしまうことがある。ちょうどいい抽象度で書きたいことをドンピシャで書けるようなタイミングもあるんだけど、そういうときって今みたいな「まだそのときじゃない」と見送ってきたピースがちょうど繋がったときでもあるかもしれないな、ということに、今まで書いていて気づいた。しかしそれを感じるタイミングは割と頻繁にあって、「このタイミングは本当にマジのやつか？」があまりわからない。いやあるいは、あるテーマを考え終えるとき、別のテーマに頭が移っていく前のタイミングというのが、自分の中で一番満足するまで考え切ったタイミングだ、と言えるだろうか。それがマジのやつだろうか。&lt;/p&gt;
&lt;p&gt;こうやって思いついた順に書き連ねるようなスタイルは、人に見せるには野蛮な感じがして通常憚られるが、日記としては役に立つと思う。なにも書かないとなにも残らないので、こうやって捻り出してる感じをわざと残すのもひとつの記録の仕方だ。&lt;/p&gt;</content:encoded></item><item><title>こと非同期コミュニケーションにおいては少しくどいと思うくらいに説明するのでちょうどいい</title><link>https://yuheiy.com/20211123-explain-too-much</link><guid isPermaLink="true">https://yuheiy.com/20211123-explain-too-much</guid><description>Slackのようなインターフェースを見ると、さも人と人が対面してしゃべるような言葉のやり取りがそのままできて、コミュニケーションとして成り立つと思ってしまうことがある。非常に限定的な状況においては可能かもしれないが、たいていはそうならない。</description><pubDate>Tue, 23 Nov 2021 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Slackのようなインターフェースを見ると、さも人と人が対面してしゃべるような言葉のやり取りがそのままできて、コミュニケーションとして成り立つと思ってしまうことがある。非常に限定的な状況においては可能かもしれないが、たいていはそうならない。&lt;/p&gt;
&lt;p&gt;非同期コミュニケーションにおいては、人がひとまとまりの言葉を発する1ターンに重みがある。&lt;/p&gt;
&lt;p&gt;対面してしゃべるときには、相手がしゃべっている途中でなにか引っかかりを感じれば、そこで割り込みをして質問したりすることが簡単にできる。&lt;/p&gt;
&lt;p&gt;「いま言ってるAって、もしかしてBのこと？」&lt;br /&gt;
「あ、それでした」&lt;/p&gt;
&lt;p&gt;この場合の割り込みはたいしたコストにならない。しかし、たとえばZoomとかになってくると難しくなる。通信による遅延があるからだ。しゃべってる途中で割り込もうとすると、間が悪くなる。&lt;/p&gt;
&lt;p&gt;「さっき言ってたAって、もしかしてBのこと？」&lt;br /&gt;
「えっ、ごめんなんのこと？」&lt;br /&gt;
「あのさっきの」&lt;br /&gt;
「ああ、そんなこと言ってた？」&lt;/p&gt;
&lt;p&gt;みたいになる。つまり、指摘や質問のコストが上がってしまう。これが一度切りだとすれば気にならないが、1時間とか話してるとそういう場面は何度もある。したがって、ちょっとしたことでいちいち流れを止めてしまうと申し訳ないとか、恥ずかしいという気持ちになるので、&lt;/p&gt;
&lt;p&gt;「あの人が言ってるこれって、たぶんあれの言い間違えな気がするけど、わかってるよな」&lt;br /&gt;
「ちょっと違和感があるけど、これくらいのことならスルーしておいて大丈夫か」&lt;/p&gt;
&lt;p&gt;という判断になる。あるいは、しゃべってる人が一息ついたタイミングでまとめて確認しようと思ってホールドしておく。すると、その人が間髪入れずにしばらくしゃべり続けるので、前提条件の確認がすごい後手に回ってしまったり、みたいなこともある。&lt;/p&gt;
&lt;p&gt;この話がわかってないのって自分だけだろうかと思うときって、実はほかの人も同じようにわかってない場面も多くて、そういうときはズレをこまめに確認しておかないと、的外れな発言が頻発して時間の無駄になる。&lt;/p&gt;
&lt;p&gt;非同期性によって、この「結局余計めんどくさい問題」が発生しやすくなる。解決策としては、できるだけいい回線を使うとか、ゆっくり目に間を置いてしゃべるとか、その辺だろうか。&lt;/p&gt;
&lt;p&gt;テキストでのやり取りも同じような問題を孕んでいる。非同期性が高いのでじっくりやり取りできると思いきや、結局みんな時間はないので急いでしまう。そして、1ターンの重みはさらに増す。ひとつ反応をもらうのに半日かかるかもしれないし、もしお互いがそうであれば、言動はますます慎重にならざるを得ない。&lt;/p&gt;
&lt;p&gt;そしてテキストである以上、対面してしゃべることとは性質が違うが、それにあまり配慮せずにいるとまた不健全性な方向に進んでしまう。&lt;/p&gt;
&lt;p&gt;しゃべるときは相手と言葉だけでコミュニケーションしているわけではない。声の感じとか、間の置き方とか、表情とか、そういうところでつねに言葉以外のやり取りをして、相手の反応も伺いつつ話を進めている。&lt;/p&gt;
&lt;p&gt;しかししゃべりの場と同じノリのままで、しゃべりの場をイメージして出てきた言葉だけを切り取ってテキスト化してしまうと、なにを言っているのかわからない人になってしまう。相手と共有されていない、自分のイメージの中だけで発生したしゃべりの断片を、相手に送りつけることになる。&lt;/p&gt;
&lt;p&gt;すると相手としては、テキストからは読み取れない情報を補完するために、発信者はどういう思考の流れでこれを書いたのかというところまでさかのぼり、意図を探り当ててやらないといけない。そうして探り当てた意図が本当に正確なものであるかに不安があれば、「あなたが言いたいのってこういうこと？」と確認する必要がある。しかしその察されている状況までもが理解できない人にとっては、「いや、だからそう言ってるでしょ。早く次の話をしてよ」という反応になってしまう。&lt;/p&gt;
&lt;p&gt;「人の立場になって考えましょう」というのは、簡単な精神論で終わる話ではなく、訓練を要する技術的な問題だと思う。人が自分の話について来れてないと認識できなかったり、そういう状況を軽視してしまう人は、話を伝えるということの実感に欠けている。&lt;/p&gt;
&lt;p&gt;伝えるためには、その相手の知識レベルや考え方、立場の違いなどについてよく理解していないと、どこに向かって説明すればいいのかがわからなくなるはずだ。そして、相手を理解しようと努めてきた経験であったり、その経験から得られる想像力によって、相手への理解は育まれるものだ。&lt;/p&gt;
&lt;p&gt;そしてこの違いは、1ターンの重みによって如実に現れることになる。指摘や質問をするコストが増大する状況であるからこそ、相手に対する配慮のなさから生まれるコストはただならないものになってしまう。だから発信側は相手にうまく伝えるために、よく考えるべきだ。&lt;/p&gt;
&lt;p&gt;非常に端的で簡単なテキストでうまく伝えられるならば、それ以上のことはない。しかしどうしても、言葉に解釈の余地が生まれてしまうことは回避できないし、相手を完全に理解することもできない。だから大事をとって、できるだけ周到な形で伝えるようにしたいと僕は考えている。&lt;/p&gt;
&lt;p&gt;相手がつまづいてしまいそうな要素は排除する。自分があたりをつけたところと相手が少し離れたところにいても、なんとか機能できるように、多少の読み違えをされてしまっても、続きを読んでいるうちに相手の理解が軌道修正されるように、少しくらいのズレは念頭に置いておく。受け取る側が察するよりも、発信側のほうが配慮する余地があるのだから、相手より先回りして書いておくくらいのことはやるべきだ。&lt;/p&gt;
&lt;p&gt;しかしそういう意識で書いていると、自分の伝え方がなんだかくどくどしく見えてきて、うっとうしくて恥ずかしかったりすることもある。でも、相手にとってはだいたいそれくらいでちょうどいいものだ。相手は自分とは違う人間なので。それでうまくいく経験がほとんどだし、逆に、頓珍漢な方向にハッスルするような失敗をしてしまうこともあるけど、伝えるための努力をしていない人よりは全然マシだと思う。&lt;/p&gt;</content:encoded></item><item><title>Every Layout——モジュラーなレスポンシブデザインを実現するCSS設計論</title><link>https://www.borndigital.co.jp/book/24204/</link><guid isPermaLink="true">https://www.borndigital.co.jp/book/24204/</guid><pubDate>Tue, 23 Nov 2021 00:00:00 GMT</pubDate></item><item><title>田舎に回帰したい</title><link>https://yuheiy.com/20211122-to-inaka</link><guid isPermaLink="true">https://yuheiy.com/20211122-to-inaka</guid><description>いま住んでる部屋が結構ボロいので、いろいろ不満があって、引っ越したいなあと思いつつも、めんどくさくてずっとほったらかしていたけど、そろそろ本当にどうにかしたいと思って引っ越し先を考え始めた。</description><pubDate>Sun, 21 Nov 2021 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;いま住んでる部屋が結構ボロいので、いろいろ不満があって、引っ越したいなあと思いつつも、めんどくさくてずっとほったらかしていたけど、そろそろ本当にどうにかしたいと思って引っ越し先を考え始めた。&lt;/p&gt;
&lt;p&gt;仕事は今はフルリモート環境になって、今後もこれが続く予定なので、どこに住んでも別に問題ない。しかし自由度がありすぎて決められないので、とりあえず今と同じ最寄り駅で探してみたけど、それだと家賃があんまり安くなくて、改めて考えてみるとやっぱり損だなと思い直したので、安いところで探すことに。&lt;/p&gt;
&lt;p&gt;いちおう東京にはいたほうが安心かなと最初は思ったものの、試しにもう少し可能性を広げてみる。所沢辺りはいいかもしれないとか。でもそれはちょっと遠すぎか。とりあえず買い物とかは便利なところがいいけど、まあそれはだいたいどこでも便利だよねとも思う。そういう感じで、いまいちポイントが見つからないなと思っていると、ふと目についた田舎っぽい駅の写真にすごい惹かれてしまった。&lt;/p&gt;
&lt;p&gt;僕は地元がそこそこな田舎で、あらゆる一帯が田畑とか山とかしかないみたいなところで育ってきたので、そういう風景に懐かしさを感じてしまう。地元ほどの田舎に住むと不便すぎるので、将来的に戻るとかはまったく考えられず、今後ずっと都会にいる気でいたけど、東京の利便性があるなかでそういう景色も拝めるのは悪くない。むしろこれこそが最適解な気がする。&lt;/p&gt;
&lt;p&gt;その方向性で探してみて、いい感じに田舎っぽいところをリストアップできてきた。野菜の無人販売所とかある雰囲気がいい。似たところで、自然が多い街という感じの場所もイメージできてきたけど、そういうところが自分が求めている感じの場所かはまだわからない。とりあえず今週や来週にでも、それぞれいくつか候補地を回りたい気分になってきた。地元にいるときは外では自転車で走り回ってるのが当たり前だったので、またそういう生活をするのもよさそう。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;ブログを書くのがなぜ難しかったのかを思い出してきた。ひとつは、タイトルが必要だから。つまりテーマをはっきりさせないといけない。思いついた順にただなんとなく書いているだけのツイートでは、書いてる側から次々テーマが変わっている。書きながら考えて、そのまま散らかしっぱなしになっている。これがいわゆる0から1のフェーズだとすると、記事化するにはその後の1から10のフェーズも経ないとダメだと思ってしまう。そして、それがないとちゃんとしたタイトルをつけられない。&lt;/p&gt;
&lt;p&gt;あるいは、タイトルを先に書くか本文を先に書くかという問題もあるかもしれない。僕はだいたいタイトルが先に出るけど、本文を書き進めているうちに次々とタイトルも変わっていくことが多い。だいたいこの辺かなという目星を決めて始めてしまってから、後で方向修正してるというか。逆になにもタイトルらしきものなしに始めてしまうのが無理かもしれない。でも最初にタイトルが出るときって、このテーマで記事化できるなっていう直観が先にあるときでもあるな。そしてそれを形成する前段にはツイートのような小さなピースがある。だから、ブログを書くための大きな流れとしては「ツイート→タイトル⇄本文」みたいな感じかも。今やっているのは、その「ツイート→タイトル」の部分だけ切り出してやることで省エネにして、習慣化することで一回ごとの負荷を減らして、最終的にはまた「ツイート→タイトル⇄本文」を楽にやれるようにするためだという気がしてきた。&lt;/p&gt;
&lt;p&gt;ここ数日の記事の内容がどこか混沌としているのはそういう事情だ。考えをクリアにして整理する過程を省いているのでこうなる。ちゃんとやってるつもりでもできてないときももちろんあるけど。細かい表現もそんなに気にしないようにしている。これくらいのものでも別にいいと自分が許容できるようになるための練習。&lt;/p&gt;</content:encoded></item><item><title>かわいいぬいぐるみとか見つけるとうわほしいぃって思うけどたぶん2ヶ月後くらいにはいらねえって思っちゃう</title><link>https://yuheiy.com/20211121-mono</link><guid isPermaLink="true">https://yuheiy.com/20211121-mono</guid><description>とんかつ屋。隣には、神経質そうな思春期の息子と、大雑把そうな年配の父親。</description><pubDate>Sat, 20 Nov 2021 05:50:00 GMT</pubDate><content:encoded>&lt;p&gt;とんかつ屋。隣には、神経質そうな思春期の息子と、大雑把そうな年配の父親。&lt;/p&gt;
&lt;p&gt;「ご飯のお代わりのためだけに呼ぶの悪くない？」と息子が言うと、「それも織り込み済みだからいいんだよ」と父親が答える。&lt;/p&gt;
&lt;p&gt;わかるよ。それでも、個人としては微妙に嫌だろうと思うんだよ。そう考えちゃうし、考えたいよね。2人が呼んだ店員を僕は帰り際に呼び止めて、ご飯のお代わりを頼んだ。僕は昔から父親と全然しゃべらなかったので、父親と息子が淀みなく会話している光景を見ると非現実的に感じて、物珍しげに見てしまった。&lt;/p&gt;
&lt;p&gt;「そんなに食べたら、さっき散歩した分が無駄になっちゃうよ」。一理ある。&lt;/p&gt;
&lt;p&gt;帰りにセブンイレブンの前を通った。今月の27日から始まる、&lt;a href=&quot;https://www.h-kuji.com/goods/tomandjerry/&quot;&gt;トムとジェリーのHappyくじ&lt;/a&gt;というのがここで引けると同僚に聞いた。とくに「チーズを丸飲みしたジェリー」のフォルムがすごくいいので欲しい。&lt;/p&gt;
&lt;p&gt;割とよくこういうのを見つけて欲しくなることがある。ぬいぐるみとかも欲しくなる。しかしそれらが増えてくると部屋が煩雑になるので、強い気持ちを持って、できるだけ買わないようにしている。&lt;/p&gt;
&lt;p&gt;例外的に、&lt;a href=&quot;https://scrapbox.io/yuhei-print-tshirt/&quot;&gt;プリントTシャツだけは50枚くらい持っている&lt;/a&gt;。なんとなく自分ルールで、これだけはOKということにしているのだ。ただ最近、衣装ケースに入り切らなくなってきたので、さすがに減らそうかなと思い始めた。本棚から溢れる本類はサマリーポケットに預けたりもしているが、Tシャツまでそれをやると運用が複雑になりすぎてしまう。&lt;/p&gt;
&lt;p&gt;けっきょく捨てるようなら最初から買わないほうがいい。捨てるのは、どうもやり切れない気持ちになる。これがもし、Tシャツを何回も繰り返し着続けて生地がダメになったから捨てるというのなら、うまい付き合いができたと思って納得できるが、はたまたぬいぐるみだったり、「チーズを丸飲みしたジェリー」だったりした場合の、適切な捨てどきはいつなのだろう。&lt;/p&gt;
&lt;p&gt;当初の目的を果たさなくなるという意味では、飽きたときや好きじゃなくなったときがその時なんだろうけど、これはモノの側の劣化とはたいてい関係がなく、持ち主の気持ちの変化によるところでしかないので、モノの側の問題——つまりやむを得ないものではない。持ち主を変えればいい。ただ、現実的にはうまくいかない。なんでもかんでもメルカリで売れるわけではないし。いやこれは半分嘘で、僕はまだメルカリを使ったことがない。&lt;/p&gt;
&lt;p&gt;しかし譲渡の選択肢がないとして、モノに対する持ち主の気持ちの消失だけをわけにゴミになってしまうのがどうしても解せない。そのモヤモヤに蓋をして、今までたくさんのモノをゴミにしてきたわけなのだけど。その行為を割り切って考えてもいいのだろうか。ペットを飼うときには、生き物だからモノのように扱うなと言うが、同じような意味で、モノもただのモノのように扱うなとでも言いたくなるような感覚がある。そういったモノを捨てることには、うっすらとだが、生き物を殺処分するような罪の意識があるかもしれない。&lt;/p&gt;</content:encoded></item><item><title>3ツイするくらいの軽さでブログ書くタイプの人類になりたい</title><link>https://yuheiy.com/20211119-like-3-tweets</link><guid isPermaLink="true">https://yuheiy.com/20211119-like-3-tweets</guid><description>3は極端かもしれない。6くらい。</description><pubDate>Fri, 19 Nov 2021 12:30:00 GMT</pubDate><content:encoded>&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;3ツイするくらいの軽さでブログ書くタイプの人類になりたい&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1461659317094924289?ref_src=twsrc%5Etfw&quot;&gt;November 19, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;3は極端かもしれない。6くらい。&lt;/p&gt;
&lt;p&gt;ツイートのハードルが低すぎてなにもかもをそこに投下してしまうが、あれはたいてい記録にはならなくて、自分でもただただ忘れ去るだけになる。僕は割と残しときたい派なので、そうじゃないほうがいいけど、代わりにブログとかにしようとするとちゃんとやりたい病が発症してハードルが上がってしまう。しかしそれで結局なにも残さないのも本意ではない。&lt;/p&gt;
&lt;p&gt;そういうわけなのでハードルを下げる必要があり、そのためにはこだわりすぎないための制約がいる。さっきラーメン屋でそういうことを思いついたので、帰り道を歩きながら全部書き上げてしまうことにした。&lt;/p&gt;
&lt;p&gt;少なくとも140文字を超える程度の量のテキストを自由に書くことに固有の楽しみというのがあって、それはツイートとはまた種類が異なる。明示的な文字数の制約とか、タイムラインのコンテキストとか、そういうルールはとても無視できるものではない。&lt;/p&gt;
&lt;p&gt;ゆっくり目に歩いていたけどさっき家の前を通り過ぎてしまった。とりあえず今日はこんなところでいい気がする。こういうタイプのしゃべるような書き方をしていると、段落を区切ることを忘れてしまう。ラフになればなるほど、全文がフラットになっていくような気がする。そういえば連ツイってそういう感じする。僕が今こうやって書いたのは連ツイせずに連ツイしただけのことなのかもしれない。まあでもいったんそれでいいです。&lt;/p&gt;</content:encoded></item><item><title>『Every Layout——モジュラーなレスポンシブデザインを実現するCSS設計論』、素直さという選択 #everylayout_ja</title><link>https://yuheiy.com/20211011-publication-of-everylayout</link><guid isPermaLink="true">https://yuheiy.com/20211011-publication-of-everylayout</guid><description>友人の腹筋ローラーの力を信じろさんと共に監訳を担当した書籍『Every Layout——モジュラーなレスポンシブデザインを実現するCSS設計論』が出版されます。現在、Amazonで予約受付中です。当初の予定よりもかなり遅れてしまいましたが、内容はいまだ鮮やかなままに思えます。</description><pubDate>Mon, 11 Oct 2021 00:20:00 GMT</pubDate><content:encoded>&lt;figure&gt;
&lt;img src=&quot;/_astro/cover.BTdnldcS_2lOy8h.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;2151&quot; height=&quot;3037&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;友人の&lt;a href=&quot;https://x.com/8845musign&quot;&gt;腹筋ローラーの力を信じろ&lt;/a&gt;さんと共に監訳を担当した書籍『&lt;a href=&quot;https://www.hanmoto.com/bd/isbn/9784862465177&quot;&gt;Every Layout——モジュラーなレスポンシブデザインを実現するCSS設計論&lt;/a&gt;』が出版されます。現在、&lt;a href=&quot;https://www.amazon.co.jp/dp/486246517X&quot;&gt;Amazonで予約受付中&lt;/a&gt;です。当初の予定よりもかなり遅れてしまいましたが、内容はいまだ鮮やかなままに思えます。&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_01.igKIzCRB_Z2mi9L0.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_02.DhLCxbMY_ZgOzsU.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_03.D9fcp2Kq_ZmNChz.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_04.BsC6nBZb_ZoWjkd.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_05.BeLPOIyz_ZeFISO.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_06.CTXS-GRd_592H1.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_07.ohPbMWAI_ZsEk5D.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_08.DjkNi2QP_2pAs5V.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_09.aXm-wKI5_1UN2xf.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;figure&gt;
&lt;img src=&quot;/_astro/sample_10.CgGyVSHq_Z1pJUj1.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;4300&quot; height=&quot;3036&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;目次:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://book.borndigital.jp/support/EveryLayout/EveryLayout_contents.pdf&quot;&gt;日本語版に寄せて&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://book.borndigital.jp/support/EveryLayout/EveryLayout_contents.pdf&quot;&gt;監訳者まえがき&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Chapter 1: 基礎
&lt;ul&gt;
&lt;li&gt;1–01: ボックス&lt;/li&gt;
&lt;li&gt;1–02: コンポジション&lt;/li&gt;
&lt;li&gt;1–03: 単位&lt;/li&gt;
&lt;li&gt;1–04: グローバルスタイルとローカルスタイル&lt;/li&gt;
&lt;li&gt;1–05: モジュラースケール&lt;/li&gt;
&lt;li&gt;1–06: 公理&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chapter 2: レイアウト
&lt;ul&gt;
&lt;li&gt;2–01: Stack&lt;/li&gt;
&lt;li&gt;2–02: Box&lt;/li&gt;
&lt;li&gt;2–03: Center&lt;/li&gt;
&lt;li&gt;2–04: Cluster&lt;/li&gt;
&lt;li&gt;2–05: Sidebar&lt;/li&gt;
&lt;li&gt;2–06: Switcher&lt;/li&gt;
&lt;li&gt;2–07: Cover&lt;/li&gt;
&lt;li&gt;2–08: Grid&lt;/li&gt;
&lt;li&gt;2–09: Frame&lt;/li&gt;
&lt;li&gt;2–10: Reel&lt;/li&gt;
&lt;li&gt;2–11: Imposter&lt;/li&gt;
&lt;li&gt;2–12: Icon&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;索引&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;もとになっているのは、ウェブサイト「&lt;a href=&quot;https://every-layout.dev/&quot;&gt;Every Layout&lt;/a&gt;」です。同サイトにある「Relearn CSS layout」というコピーの通り、本書は、すでにある程度CSSについての経験を積んでいる制作者のための書籍です。レイアウトがテーマではありますが、ある決まった見た目を模写する練習帳のようなものではなく、CSSのもっと抽象的な設計方法について論じたものです。&lt;/p&gt;
&lt;p&gt;少し乱暴に言えば、「レスポンシブデザイン」と「CSS設計」と「デザインシステム」をまとめて一つの視点で解説したような内容になっています。これらは本来、CSSという技術領域の上で重なり合う分野であり、切っても切れない関係です。しかし議論としては各論に分散する場面が多く、それぞれの分野が相互に影響し合う領域については、あまり知識が集積されてこなかったように感じます。そうした分野の交差点において、体系化された理論を提示できることは、本書の一つの意義です。&lt;/p&gt;
&lt;p&gt;また、CSSらしい考え方を身につけるための本とも言えます。1章では、CSS固有の性質をうまく活かしながら、いかにして効率的で堅牢なスタイリングを実現するかという基礎的な方法論について詳しく解説しています。経験豊かな開発者が無意識に身につけているものの、なかなか言葉にできなかったような経験則を、巧みに繋ぎ合わせて体系化しています。&lt;/p&gt;
&lt;p&gt;2章では、これらを踏まえつつ、具体的なレイアウトパターンの紹介に移ります。一つ一つが「レイアウトプリミティブ」と呼ばれるコンポーネントになっています。非常に再利用性の高いコンポーネントであり、うまくプロジェクトに導入できれば、レイアウトのための必要なCSSが大幅に削減できて、結果的に全体の設計もシンプルになります。&lt;/p&gt;
&lt;p&gt;このような書籍は私自身にとっても待望でしたが、一方で、これまで現れてこなかったのも仕方のないことに思えるのです。&lt;/p&gt;
&lt;p&gt;CSSを巡る状況は非常に複雑です。&lt;/p&gt;
&lt;p&gt;CSSはスタイリングのための言語であると同時に、ウェブデザインの素材そのものであり、「デザインのためにデザインされたもの」でもあります。つまり、デザイナー自身が使うためにあるのです。 スタイルが&lt;em&gt;どうあるか&lt;/em&gt;よりも、&lt;em&gt;どうあるべきか&lt;/em&gt;を起点としながら事象を導くのがCSSの考え方です。言わばその遠回しなルールの記述こそがデザインであり、CSSの勘所です。&lt;/p&gt;
&lt;p&gt;しかし、デザインカンプを中心としたワークフローでは、デザイナーはCSSに触れず、あるいはCSSについて意識することなく制作してしまうという状況が珍しくありません。すると自らのデザインにおいて、CSSが持つ構造との矛盾が生まれたり、前提の誤解があったとしても、それに気づくことができません。その結果、成果物を引き継いだCSSの担当者は、多かれ少なかれ不条理に苛まれることになります。筋の悪い構造を無理やり実現しなければならない場面も多く、管理するCSSは不必要に複雑なものとなっていきます。&lt;/p&gt;
&lt;p&gt;また、デザインカンプ前提のワークフローからキャリアを始める多くの開発者にとっては、デザインカンプの通りに模写することこそが最重要事項と捉えられてしまいがちです。CSSをうまく活用したよい慣習について学べる機会も少なく、構造上の問題を覆すために設計の前提を見直すような発想に至れるだけの力も身につけられません。&lt;/p&gt;
&lt;p&gt;昨今では、CSSのためのツール選定としては非常に多様な選択肢があります。CSS in JSやCSS Modules、ユーティリティファーストCSSなど、それぞれの思想をもってCSSの使い方を見直そうとする姿勢が見受けられます。とはいえ、いずれもCSSを置き換える類いものではなく、CSSを少し抽象化した、CSSの延長線上にあるものです。プレーンで普通のCSSをうまく使えるからこそ、こうした発展的な技術もよく理解できるのです。しかし、普通のCSSの経験が乏しいままに、いきなりこうしたツール群に気を取られてしまうと、学習としては遠回りなものになって、たくさんの時間を無駄にしてしまいます。&lt;/p&gt;
&lt;p&gt;個人的な経験から言えるのは、CSSの難しさの割合の大部分を占めるのは、その周辺事情の複雑さだということです。ズレの積み重ねによって、CSSは本来よりもずっと難しい技術になっています。この問題を根本的に解決するには、ズレを少しずつ解消して、付随的な複雑さを可能な限り取り除くことです。そのためには、いくつもの方向からさまざまなアプローチが必要となるでしょう。&lt;/p&gt;
&lt;p&gt;こうした状況がある中において、本書の最大の特徴と言えるのは、その包括的なアプローチです。CSS開発者のためだけの部分最適な理論ではなく、ユーザーを含めた全体から見て本質的にどのようにデザインすべきか。複雑さを本質的に取り除くためにはそうした視点が欠かせないのです。しかしそのためには大きな発想の転換が求められることもあり、目の前の仕事に適用するのが難しい場合もあるはずです。ワークフローを変える必要に迫られるかもしれませんし、見方によっては理想論と一蹴されてしまうでしょう。&lt;/p&gt;
&lt;p&gt;しかし重要なのは、いくつもの「考え方」を持っていることです。目の前の状況を相対化して、よりよいものにしていくためには、多くのことを学び、選択肢の数を増やし、都度適切なものを選び取れるようにしておく必要があります。その意味で、本書は、多くの人にとって新鮮な驚きをもたらしてくれる一冊であるはずです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.co.jp/dp/486246517X&quot;&gt;Every Layout-モジュラーなレスポンシブデザインを実現するCSS設計論 | ヘイドン・ピカリング, アンディ・ベル, 安田 祐平, 横内 宏樹, 佐藤 英一, 株式会社Bスプラウト |本 | 通販 | Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hanmoto.com/bd/isbn/9784862465177&quot;&gt;Every Layout モジュラーなレスポンシブデザインを実現するCSS設計論 ヘイドン・ピカリング(著/文) - ボーンデジタル | 版元ドットコム&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wgn-obs.shop-pro.jp/?pid=164656047&quot;&gt;【PDFダウンロード版】Every Layout－モジュラーなレスポンシブデザインを実現するCSS設計論 - ボーンデジタルオンラインブックストア&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/&quot;&gt;Relearn CSS layout: Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;翻訳のレビューとしては、&lt;a href=&quot;https://x.com/terkel&quot;&gt;terkel&lt;/a&gt;さんと&lt;a href=&quot;https://x.com/rokuzeudon&quot;&gt;ろくぜうどん&lt;/a&gt;さんにご協力いただきました。文章として違和感があったり、表現が伝わりづらかったりした部分については、お二人の助言のおかげでかなり改善できたように思います。&lt;/p&gt;</content:encoded></item><item><title>リハビリ</title><link>https://yuheiy.com/20210929-rehabilitation</link><guid isPermaLink="true">https://yuheiy.com/20210929-rehabilitation</guid><description>ブログを書くのはおよそ8ヶ月ぶりになる。僕にとっては、それなりに長らく書かなかったことになる。</description><pubDate>Tue, 28 Sep 2021 17:59:00 GMT</pubDate><content:encoded>&lt;p&gt;ブログを書くのはおよそ8ヶ月ぶりになる。僕にとっては、それなりに長らく書かなかったことになる。&lt;/p&gt;
&lt;p&gt;いろいろと忙しかった。だけど、それ自体が理由なのではなくて、「今はブログなんか書いている場合じゃない」というプレッシャーを感じて勝手に書けなくなっていた。&lt;/p&gt;
&lt;p&gt;もちろんたまには、短期的にどうしても無理な場面はある。けど今はそれよりも、どうしても無理な場面がしばらく続いた結果、習慣が抜けて走り出せなくなっていた、というのが近いと思う。書かない期間が長引けば長引くほど、足取りは重くなり、無意識にハードルが上がってしまう。軽快さは徐々に損なわれて、うまくやれなくなる。&lt;/p&gt;
&lt;p&gt;だから、再開するにはちょっとずつやったほうがいいだろう。大したことを書こうとせずに、投稿することだけを目標にしよう。&lt;/p&gt;
&lt;p&gt;この記事は散歩しながら書いた。最近はできるだけ毎日、散歩するようにしている。一万歩って案外大変だ。歩数を測るようにしてみて初めてわかった。夜なので、風呂に入ったらさっさと寝よう。&lt;/p&gt;</content:encoded></item><item><title>垂直方向のマージンにはmargin-topを優先的に使う理由</title><link>https://yuheiy.com/20210127-prefer-margin-top</link><guid isPermaLink="true">https://yuheiy.com/20210127-prefer-margin-top</guid><description>margin-bottomではなくmargin-topを使う派である旨をツイートしたら理由を尋ねられたので、それに対する回答です。大きくは次の3つです。</description><pubDate>Tue, 26 Jan 2021 17:13:39 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;margin-bottom&lt;/code&gt;ではなく&lt;code&gt;margin-top&lt;/code&gt;を使う派である旨をツイートしたら理由を尋ねられたので、それに対する回答です。大きくは次の3つです。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;末尾の要素の存在が任意である場合が多いため&lt;/li&gt;
&lt;li&gt;Stackレイアウトとの取り合わせやすさのため&lt;/li&gt;
&lt;li&gt;隣接セレクターを使った場合分けができるようにするため&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;CSS、基本コンポーネントの上にマージン取る派と、下にマージン取る派がいると思うですけど、自分は今までずっと下で。というのは、その方が直感的だと感じるからなんですけど、見出しの下って結構縮めるよね？それを上マージンでやると結構頭こんがらがらない？って思うんだけどどうなんだろう&lt;/p&gt;— Takazudo (@Takazudo) &lt;a href=&quot;https://twitter.com/Takazudo/status/1348988615620128769?ref_src=twsrc%5Etfw&quot;&gt;January 12, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;上です&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1348990956989661184?ref_src=twsrc%5Etfw&quot;&gt;January 12, 2021&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;h2&gt;1. 末尾の要素の存在が任意である場合が多いため&lt;/h2&gt;
&lt;p&gt;コンポーネントやページなど、ある特定の区間内の末尾の要素は存在したりしなかったりすることが多い。重要度の高い情報を上から順に並べるとき、末尾の要素は相対的に重要度の低い情報を扱うことになり、ビューとしてはその有無が任意になったりする。&lt;/p&gt;
&lt;p&gt;たとえばページタイトルは必須だけどページの説明文は任意になる場合。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;#if&lt;/span&gt;&lt;span&gt; description&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-3&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;/if&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;あるいはカードパターンの下部にあるリンクやボタンが任意になる場合。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;card&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-2&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {{&lt;/span&gt;&lt;span&gt;#if&lt;/span&gt;&lt;span&gt; link&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-3&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;{{link}}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;さらに詳しく&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {{&lt;/span&gt;&lt;span&gt;/if&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;カードパターンにはタグが含まれていることもあるかもしれない。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;card&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-2&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {{&lt;/span&gt;&lt;span&gt;#if&lt;/span&gt;&lt;span&gt; tags&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-3&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {{&lt;/span&gt;&lt;span&gt;#each&lt;/span&gt;&lt;span&gt; tag&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; tags&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {{&lt;/span&gt;&lt;span&gt;/each&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {{&lt;/span&gt;&lt;span&gt;/if&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;こういうとき、末尾の要素には&lt;code&gt;margin-bottom&lt;/code&gt;がついていない方が都合がいい。&lt;/p&gt;
&lt;p&gt;カードパターンなどでボックス状の見た目になっているときには、&lt;code&gt;padding&lt;/code&gt;や&lt;code&gt;border&lt;/code&gt;を使ってマージンが相殺されない状態になっている。するとボックスの下部には、親の&lt;code&gt;padding-bottom&lt;/code&gt;と末尾の要素の&lt;code&gt;margin-bottom&lt;/code&gt;が足し合わされた量の余白ができてしまって不自然な見た目になる。&lt;/p&gt;
&lt;p&gt;この問題を回避するために、要素内の末尾の要素からは&lt;code&gt;margin-bottom&lt;/code&gt;を取り除くやり方がある。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.card&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :last-child&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-bottom&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかしそもそも最初から&lt;code&gt;margin-top&lt;/code&gt;を使うようにしておけば、このような規則を追加する必要がない。&lt;/p&gt;
&lt;p&gt;もし先頭の要素の存在が任意である場合には、逆に&lt;code&gt;margin-bottom&lt;/code&gt;を使った方がよくなる。ただ末尾の要素が任意である場合に比べて、先頭の要素が任意である場合の方が経験則上は格段に少ない。僕は実際にそのような場面に出くわしたときのみ例外的に&lt;code&gt;margin-bottom&lt;/code&gt;を使うこともあるが、そうでない限りは基本的に&lt;code&gt;margin-top&lt;/code&gt;を使っている。&lt;/p&gt;
&lt;p&gt;すべてを&lt;code&gt;margin-top&lt;/code&gt;に統一したければ、マークアップ側の工夫で&lt;code&gt;margin-bottom&lt;/code&gt;を使わないこともできる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;#if&lt;/span&gt;&lt;span&gt; subtitle&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;subtitle&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;mt-2&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;/if&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2&gt;2. Stackレイアウトとの取り合わせやすさのため&lt;/h2&gt;
&lt;p&gt;Stackレイアウトというのは&lt;a href=&quot;https://every-layout.dev/layouts/stack/&quot;&gt;Every Layoutで紹介されているCSSのパターン&lt;/a&gt;のことで、具体的には次のような実装になる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.stack&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class=&lt;/span&gt;&lt;span&gt;&apos;stack&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;{{link}}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&amp;gt;さらに詳しく&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;こうすることで自動的に子要素の要素間にのみ同じ大きさの余白ができるようになる。すると末尾の要素の存在が任意になっていたとしても余白は自動的に調整されるので、余白の大きさが等しい場合にはStackレイアウトを採用すると先述の問題を回避できる。&lt;/p&gt;
&lt;p&gt;あるいは余白の大きさが違ったとしても、都度隣接セレクターを使うことで、特定の要素同士が並んだ場合にのみ余白を設定する実装ができる。&lt;/p&gt;
&lt;p&gt;いずれにしてもStackレイアウトを採用する場合には&lt;code&gt;margin-top&lt;/code&gt;を必ず使うことになるので、Stackレイアウトを常用する僕としては、Stackレイアウトを使わない場面でも一貫性の意味で&lt;code&gt;margin-top&lt;/code&gt;に統一しておく方が混乱が少なくて済む。&lt;/p&gt;
&lt;p&gt;また複数のStackレイアウトが横並びになった場合に、特定の要素の配置を下部に合わせる用法もある。&lt;/p&gt;
&lt;figure&gt;&lt;/figure&gt;
&lt;p&gt;末尾の要素の前に&lt;code&gt;margin-bottom: auto&lt;/code&gt;を指定すると、要素の高さに関わらず次に来る要素が下部に揃うようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.stack&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-direction&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;column&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.stack&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.stack&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; :nth-child&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-bottom&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;例では2番目の要素に&lt;code&gt;margin-bottom: auto&lt;/code&gt;が指定されているが、同時に3番目の要素の&lt;code&gt;margin-top&lt;/code&gt;が引き続き生きていることが重要になる。2番目の要素の&lt;code&gt;margin-bottom: auto&lt;/code&gt;によって、余っている領域いっぱいまで3番目の要素が下部へ移動すると同時に、3番目の要素自身の&lt;code&gt;margin-top&lt;/code&gt;によって、余っている領域の大きさに関わらずつねに空く余白の最低限の大きさが設定される。つまりこのレイアウトには&lt;code&gt;margin-top&lt;/code&gt;と&lt;code&gt;margin-bottom&lt;/code&gt;の両方が必要になる。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;margin-bottom&lt;/code&gt;はこのパターンを実現するために温存している。&lt;/p&gt;
&lt;h2&gt;3. 隣接セレクターを使った場合分けができるようにするため&lt;/h2&gt;
&lt;p&gt;コンポーネント等の組み合わせによって垂直方向の余白の大きさに違いがある場合、隣接セレクターを活用すると余白の大きさを場合分けできるようになる。「デフォルトでは段落の上に&lt;code&gt;1.5rem&lt;/code&gt;の余白ができて、段落が見出しと隣接する場合には少し狭めの&lt;code&gt;1rem&lt;/code&gt;にしたい」ということであれば次のようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; p&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;margin-bottom&lt;/code&gt;では同様のやり方をするのは難しい。Aの隣のBに対して宣言するという表現になる以上、AとBとの間の余白を設定するためには&lt;code&gt;margin-top&lt;/code&gt;を使うしかない。隣接セレクターを使って&lt;code&gt;margin-bottom&lt;/code&gt;を調整できると役に立つ場面もあるかもしれないが、まれだろう。&lt;/p&gt;</content:encoded></item><item><title>ぜんぶかきなおす</title><link>https://yuheiy.com/20210101-rewrite-all</link><guid isPermaLink="true">https://yuheiy.com/20210101-rewrite-all</guid><description>丸2日くらいかけて、このサイトのHTML/CSSやサイト生成の仕組みを全部書き直した。わかりやすい変化としては、以前のバージョンではフォントサイズとか余白とかがビューポートのサイズに関わらずつねに一定だったところが、今のバージョンではビューポート幅にもとづいたファクターに応じてすべてのフォントサイズが変化するようになっている。</description><pubDate>Fri, 01 Jan 2021 02:56:21 GMT</pubDate><content:encoded>&lt;p&gt;丸2日くらいかけて、このサイトのHTML/CSSやサイト生成の仕組みを&lt;a href=&quot;https://github.com/yuheiy/yuheiy.com-v5/commit/99b31c35ba936257fa5b14b61bb4a6356b427e6a&quot;&gt;全部書き直した&lt;/a&gt;。わかりやすい変化としては、以前のバージョンではフォントサイズとか余白とかがビューポートのサイズに関わらずつねに一定だったところが、今のバージョンではビューポート幅にもとづいたファクターに応じてすべてのフォントサイズが変化するようになっている。&lt;/p&gt;
&lt;p&gt;具体的には、まずビューポート幅にもとづいたファクターは次のように実装している。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;331.2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6.99&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;342.4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6.98&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;353.6&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6.97&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;/* 同様に続く */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1417.6&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6.02&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1428.8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6.01&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1440&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --scale-factor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;もっとも狭いビューポート幅では&lt;code&gt;7&lt;/code&gt;になって、&lt;code&gt;1440px&lt;/code&gt;まで広がると&lt;code&gt;6&lt;/code&gt;になる。そしてこれらの間の幅では間の値で補間される。メディアクエリで地道にやっているのがダサいけど、考え得る限りではこれが一番簡単になりそうだった。&lt;a href=&quot;https://github.com/yuheiy/yuheiy.com-v5/blob/e836b14653874ebf1b1c866633cda0a19971122d/src/_styles/main.scss#L68-L87&quot;&gt;Sassを使えば楽に作れる&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;続いてこの値を用いて、&lt;a href=&quot;https://standard.shiftbrain.com/blog/harmonious-proportions-in-type-sizes&quot;&gt;調和数列にもとづいた計算方法&lt;/a&gt;でフォントサイズのスケールを定義する。モジュラースケールでもいいんだけど、問題は&lt;a href=&quot;https://standard.shiftbrain.com/blog/music-math-typography&quot;&gt;指摘されている通り&lt;/a&gt;。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-base&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-sm&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-lg&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-2xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--scale-factor&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;このように宣言しておくことで、次のように計算されるようになる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-base&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-sm&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 0.875rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-lg&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 1.166rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 1.4rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-2xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 2.333rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-base&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-sm&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 0.857rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-lg&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 1.2rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 1.5rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-2xl&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt;) * &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-base&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;/* 3rem */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;余白のサイズもこれにもとづいたものに設定している。外から見えるところとしてはそんなところ。後は裏側の、僕にしかわからないような変更点がいろいろ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;テンプレートエンジンをLiquidからHandlebarsに置き換えた
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rome.tools/&quot;&gt;Romeのウェブサイト&lt;/a&gt;はEleventy製なんだけど、Liquidが採用されているのを見て、Nunjucksより機能が絞れてて良さそうと思ったけど、いろいろ中途半端だった。気の迷いだった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Handlebarsを使うためのフィルターやデータフロー周りの調整&lt;/li&gt;
&lt;li&gt;YAMLファイルをタブインデントを使って書いてパース時に変換していたけど、4スペースに変えた
&lt;ul&gt;
&lt;li&gt;これもRomeのパクり。普通で良い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;シンタックスハイライトを実装した&lt;/li&gt;
&lt;li&gt;投稿の&lt;code&gt;description&lt;/code&gt;は本文の最初の段落と同じ内容だったけど、同マークダウンファイルのFront Matterにコピペして設定していたので、自動的に本文から読み取るようにした&lt;/li&gt;
&lt;li&gt;投稿の並びをファイル名順にしていたけど、&lt;code&gt;published&lt;/code&gt;フィールドにもとづいて並べるようにした&lt;/li&gt;
&lt;li&gt;フィードの生成をプラグインのサンプルとして示されているテンプレート（Nunjucks）そのままでやっていたので、一貫性のためにHandlebarsで書き直した&lt;/li&gt;
&lt;li&gt;データファイルにいろいろ重複したデータがあったので一元化した
&lt;ul&gt;
&lt;li&gt;おもにフィードのテンプレートでサボってたせい&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hail2u/hail2u.net/blob/8e0df3431ec2ff8859a79cb337c208aa8d4b649d/src/metadata.json&quot;&gt;Hail2uの&lt;code&gt;metadata.json&lt;/code&gt;&lt;/a&gt;を参考にした&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;いくつかのテンプレートで同ファイル内にFront Matterを書いていたけどマークダウンファイル以外では全部外部ファイルに書くようにした&lt;/li&gt;
&lt;li&gt;投稿ファイルを変更してGitHubにプッシュしたらGitHub Actionsで自動的に変更日時が書き換えられるようにしていたけど複雑なので廃止&lt;/li&gt;
&lt;li&gt;Netlify CMSを使えるようにした&lt;/li&gt;
&lt;li&gt;404ページを作成&lt;/li&gt;
&lt;li&gt;フッターの「編集履歴」リンクを投稿の詳細ページ以外では出さないようにした&lt;/li&gt;
&lt;li&gt;リンク一覧のコンポーネントを&lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;と見出しでマークアップしていたけど、単に&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;と&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;にタイトルは&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;にした&lt;/li&gt;
&lt;li&gt;アイコンの冗長なIDをシンプルにした
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;icon-chevron-right&lt;/code&gt; → &lt;code&gt;icon-chevron&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;icon-arrow-back&lt;/code&gt; → &lt;code&gt;icon-back&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSSファイルをインライン化していたけど外部ファイル化した&lt;/li&gt;
&lt;li&gt;余白の名前を&lt;code&gt;space-sm&lt;/code&gt;とか&lt;code&gt;space-lg&lt;/code&gt;みたいにしてたけど連番に変えた&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://every-layout.dev/layouts/cluster/&quot;&gt;Cluster&lt;/a&gt;パターンの&lt;code&gt;margin&lt;/code&gt;の設定が面倒なのでやめて、Flexboxに&lt;code&gt;gap&lt;/code&gt;を使うようにした
&lt;ul&gt;
&lt;li&gt;Safariには3月のリリースまで実装されないけど時は早く過ぎるのでオッケーにした&lt;/li&gt;
&lt;li&gt;余白はClusterのモディファイア的に実装していたが、これによって&lt;code&gt;gap&lt;/code&gt;用の汎用的なユーティリティクラスに分けられるようになった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;まだいろいろある気がするけどとりあえずこんなところ。これらのことがかなりうまいやり方で達成できた。そこに満足感がある。&lt;/p&gt;
&lt;p&gt;サイトの構造とか見た目はちょっとしか変わってなくて、そこだけ更新しようとすればまあできたんだけど、こういう微妙な負債みたいなものは累積してそのままになりがちなので思い切って全部書き直したりすることがある。声を大にしてはっきりと「これが生産性を下げている」と言えるわけではない微妙なやつがいろいろあるというのがポイントで、あるひとつの問題だけ片付けさえすればすべてがよくなるような状況はあまりない。なんとなくいまいちな設計が全体的に蔓延していて、うまく説明できないけどちょっとずつ一貫性が崩れてるとか、微妙にDRYになってないとか、橋渡し的なコードが多いとか。&lt;/p&gt;
&lt;p&gt;すでにある程度できているコードベースを運用していると、あくまでそれを基準にして次に取る手を決めていくことになるので、それなりの理由がない限りは現状維持になるのが自然だ。一方で、一から作るなら全然違う完成形がイメージできるというのも当然あるはずで。現実的には、そう思うたびに都度全部書き直しているとどれだけ時間があっても足りないので、日々費用対効果みたいなものを意識しながら気持ちに折り合いをつけていくしかない。しかしことこのような個人的かつ小さいプロジェクトについては、むしろ積極的に全部書き直すようにしている。&lt;/p&gt;
&lt;p&gt;「あくまで僕の場合は」と言っておくけど、一から書くという気になっていないと最善のコードは書けない。すでにある程度のコードを積み上げてしまっている限り、それを利用した上で次の手を選択するという狭い範囲の中で発想が無意識に制限されてしまう。「その状況においては」良い選択ができるかもしれないけど、制限が外れてしまえば第三者的には妥協の産物になる。&lt;/p&gt;
&lt;p&gt;書き直すということは一度書いてしまってある程度わかっているものをもう一度書くということで、作る対象に対しての理解度がかなり上がっている状態からのスタートを意味する。やらないといけない仕事や起き得る問題は把握できていて、場当たり的な措置を取らざるを得なかった箇所も覚えている。一度書き終えることではじめて全体的に見渡せるようになって、本当にあるべきだった形がやっとわかってくる。人生は2周できないが設計は2周できる。&lt;/p&gt;
&lt;p&gt;そしてこの2周目の経験というのは普段なかなか得難いものだと言える。全部書き直す機会はそうそうない。作ることのフィードバックとして受けた痛みに対して、小手先の対応でお茶を濁すのではなくて、根本的にどうすればこのような問題が起こらないのかを考えた上で、実証することがこのフェーズではできる。全部書き直すとまではいかなくとも、日々の1コミットの裏で積み重ねられる検証や意思決定の数々は紛れもなく設計の結果だろうが、これはまた焦点が別のところにある。真に全体を見るというのは究極的にはやはり全部書き直すことだと思う。&lt;/p&gt;
&lt;p&gt;またそのプロジェクトだけの経験に限らず、新たにどこか別の場所で得た知見やコミュニティの中で共有されたものなんかもあるだろう。当時は満足していたコードが、久しぶりに見たらもう酷くダメな出来に見えることもある。そうなるだけの時間を経てきた今の自分が持てる知識のすべてを統合して全部書き直したとき、再構成された設計は見違えるようになるのはもとより、それを経た自分もまた変わっている。&lt;/p&gt;
&lt;p&gt;コードの一貫性を壊すのは妥協だが、妥協せざるを得ない状況を作るのは設計である。つねに完全さを目指していれば選択のブレはなくなるが、既存の仕組みとうまく整合性が取れずにハマらない瞬間はどうしてもある。しかしそうなってしまう場面を減らすことはできる。&lt;/p&gt;
&lt;p&gt;いつも全部書き直すことはできない。でもその経験のおかげで、新たに作るものであっても、部分的には書き直しの洗礼を受けた完成度に最初から到達できる。書き直しの経験はその場限りのものではなく、さまざまな解釈の可能性を将来に残してくれる。&lt;/p&gt;</content:encoded></item><item><title>歩行とか</title><link>https://yuheiy.com/20201226-walking-etc</link><guid isPermaLink="true">https://yuheiy.com/20201226-walking-etc</guid><description>「歩行 Advent Calendar 2020」を作ったけど、25日中15日しか歩行できなかった。作った当初は本当に25日分全部埋める気でいたけど、実際のところ3日目でいきなり穴を開けてしまっているし、この60%という微妙な達成度が僕の緊張感のなさを絶妙に体現しているようで、なるべくしてなった気がした。</description><pubDate>Fri, 25 Dec 2020 18:25:04 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://adventar.org/calendars/5933&quot;&gt;歩行 Advent Calendar 2020&lt;/a&gt;」を作ったけど、25日中15日しか歩行できなかった。作った当初は本当に25日分全部埋める気でいたけど、実際のところ3日目でいきなり穴を開けてしまっているし、この60%という微妙な達成度が僕の緊張感のなさを絶妙に体現しているようで、なるべくしてなった気がした。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;中学、高校と運動部に所属していて、中でも高校では部はそれなりに厳しかったこともあり、体力は結構あった。それだけに、部活を引退してしばらくしてから、あるとき全力疾走ができなくなっていると気づいた落胆はなかなかだった。動きが全然イメージに着いて来なくて、自分がすごくダウングレードしたような気持ちになった。&lt;/p&gt;
&lt;p&gt;それからは自発的にトレーニングするようになった。単に運動不足解消というレベルでなく、できるだけ動ける体を維持するために、それなりに。長距離のランニングと短距離のダッシュ、ウェイトトレーニングをやっていたけど、すぐにランニングに主軸を置いて長い距離を速く走るための練習をするようになった。&lt;/p&gt;
&lt;p&gt;最初はひたすら長い距離を走ったりしていたけど、いくつか本とかを読んだりしながら、怪我のリスクを減らすために走行距離を抑えながら負荷を高めるためのインターバル走を取り入れたり、有酸素運動による筋肉量の低下を抑えるために並行してウェイトトレーニングをしたりしていた。&lt;/p&gt;
&lt;p&gt;しかしまあ皮肉なことに、ふくらはぎを鍛えるためのカーフレイズを行っていたら、ある日そこが肉離れになって動けなくなった。負荷を掛けすぎてしまったのもあるし、冬場だったせいでもあるんだろう。数ヶ月後に予定していたハーフマラソンへの出場も絶たれてしまった。当時の怪我はいまだに寒くなってくると痛むし再発しそうになる。&lt;/p&gt;
&lt;p&gt;代わりにウェイトトレーニングに主軸を移した。実家は田舎だったこともあり近所にジムがなかったため、数年分貯め込んだお年玉を使って重量可変式のダンベルとフラットベンチを買って自分の部屋に置き、日々トレーニングに励んでいた。いつも家族からはうるさいと苦情を受けていたが、たしかに今の自分にそういう隣人がいたらブチ切れているし、今では申し訳なく思う。&lt;/p&gt;
&lt;p&gt;それから専門学校に進学するために都会で下宿を始めた。いやこれは半分は嘘で、都会で下宿を始められるような学校を選んで、その理由は伏せたまま進学させてもらった。ただこれは入学してから知ったことだけど、その専門学校は近くにある大きなジムと提携していて、1年を通して利用できる契約を3千円という破格で行えることになっていた。相場を知らない人のために言うと、民間のジムだと月に1万円とかはベタなところである。一見大盤振る舞いな感じがするけど、多くの学生は安いからとりあえず契約しておくものの全然通わないので余裕で採算が取れるらしい。&lt;/p&gt;
&lt;p&gt;本腰を入れてバーベルを使ったトレーニングを始めたのはそれからだ。理由はよく覚えてないけど、ダンベルよりも重い重量を扱えるしフォームがシンプル、マシントレーニングよりも可動域が広い分より多くの筋肉に刺激を与えられる、プリミティブでかっこいい、みたいな感じだったと思う。基本的に種目はBIG3しかしなかった。スクワット、ベンチプレス、デッドリフトの3つを総称して、トレーニングの王様という意味で一般にそう呼ばれる。当時の僕のバイブルは&lt;a href=&quot;https://athletebody.jp/&quot;&gt;AthleteBody.jp&lt;/a&gt;で、トレーニング内容としてはひたすら&lt;a href=&quot;https://athletebody.jp/2014/07/19/big3-routine/&quot;&gt;BIG3ルーティーン&lt;/a&gt;を実践していた。そのジムに通っていた2年間で一番ピークだったときでは、デッドリフトはMAXが140kgだった。ほかはそれほど強くなかったのでよく覚えていない。&lt;/p&gt;
&lt;p&gt;ちなみにジムに入場した日数が一定の基準を超えていると学校の単位になるので数がカウントされていたんだけど、在学している2年間は僕の利用日数の多さは学校全体でずっと2位だったらしい。1位の人は毎日風呂だけを利用していたという。&lt;/p&gt;
&lt;p&gt;当時はほとんど狂信的に食事に力を入れていて、僕は痩せ型なのでとにかく増量するために異常な量の飯を食べていた。1日に米6合と鶏胸肉600g、プロテインパウダー、ミキサーにかけたほうれん草とか。消化がしんどかったので強力わかもとを飲んでいた。これを半年くらい続けることで体重が8kgくらい増えて人生でもっともデカい状態に到達して、トレーニングでMAXを記録したのもその辺りの時期だったけど、胃は完全に限界だった。もう一日中吐きそうになっていて、でも少しウエッとなると栄養が無駄になってしまうので、どうにかして意識を散らして誤魔化していた。しかしさすがにすぐ無理になった。&lt;/p&gt;
&lt;p&gt;就職のために東京へ上京してきたころから、できるだけ食事は普通にするように心がけてきた。量が少なくておいしいものを積極的に選ぶようにしたり。けどしばらく胃の調子はだいぶ悪かったと思う。このままでは改善されないんじゃないかという気もしていたけど、時間が経って少しずつマシにはなってきた。ジム通いは再開したけど、食事は普通にするようにした。それでも今でも少し悪い。&lt;/p&gt;
&lt;p&gt;初めてゴールドジムに入会して、設備の充実具合にただただ感動していた。ジムのトレーナーに施設を案内されているとき、設備を見て「すごいですね」と言ったら、「すごいでしょう」と言われた。そのジムはビルの中でいくつかの階に分かれていて、地下2階にフリーウェイトのためのフロアがあり、マジの肉塊しかいない空間なんだけど、その中で僕もバーベルを握り始めることになった。&lt;/p&gt;
&lt;p&gt;ただ残念ながら専門学生時代のピークまで到達できたことがない。これは食事のこともあるものの、トレーニングにあの頃ほどの時間を注げなくなったことが大きい。専門学生時代には、月曜・水曜・金曜にはなにがあろうとも絶対にジムに行くと決め込んでいて、なによりもそれを優先していた。授業もせいぜい夕方には終わる。仕事はそうでなくなって、遅くまで残業もすることもあったし、そこからジムで追い込もうという気持ちになれるほど体力が残っていなかった。代わりに週末に通ったりするようになったけど、それでも頻度としてはガタ落ちで、結局トレーニング量の減少には逆らえなかった。&lt;/p&gt;
&lt;p&gt;そうしてモチベーションの上がらない中、ある日ジムでデッドリフトをしているとき、普段と違う変な力の入り方のままバーベルを引いてしまい、腰に鋭い痛みが走った。その日はそこで切り上げて帰ってそのまま寝たけど、翌日になるとベッドから起き上がれなかった。そこから数週間くらいはずっと痛くて、違和感は何ヶ月も続いた。その間、能動的な運動はきっぱりやめてしまっていて、長らく復帰できそうにないなと思っていた。&lt;/p&gt;
&lt;p&gt;それをきっかけに腰周辺のいろんな部分が痛むようになってきた。それまでは意識したことすらなかったんだけど、寝るときに肩を下にして寝ると翌日の腰の調子が悪くなってしまうようになった。調べてみるとこの姿勢は腰への負担がそれなりに大きいらしい。あと椅子に座っている間ずっと鈍い痛みが続いたりした。この時期はずっとこの辺の不調にイライラしながら生活していた記憶がある。&lt;/p&gt;
&lt;p&gt;それから結構な頻度で整体に通うようになった。だいぶ回復して来たような気がしたので、試しにジムを再開してみようと軽く腰を動かしたら、「ヤバい、死ぬ」という感じになって、これには時間が掛かると悟った。それからも整体へは通っていたが、この辺りでコロナ禍に見舞われて通院をやめてしまった。ジムへもそれ以来行ってない。&lt;/p&gt;
&lt;p&gt;家から出るなと言われて、「いやあ僕はまったくそんなこと余裕ですけどね」とか思っていたら、異常に体力が落ちて最寄り駅まで歩いただけで筋肉痛になるようになった。寝てもすぐ起きてしまうし、明らかに生活に支障が出ていた。一方で腰の調子は、あの日から2年近くが経過してだいぶマシになっていた。&lt;/p&gt;
&lt;figure&gt;&lt;blockquote&gt;&lt;p&gt;毎日歩いた方がいい、外&lt;/p&gt;— 全部入りHTML太郎 (@_yuheiy) &lt;a href=&quot;https://twitter.com/_yuheiy/status/1306897088064569344?ref_src=twsrc%5Etfw&quot;&gt;September 18, 2020&lt;/a&gt;&lt;/blockquote&gt;

&lt;/figure&gt;
&lt;p&gt;そこで僕はこう考えて、ごく短い距離から、できる限り毎日歩くことを始めた。最初は駅まで行って帰ってくるだけだったけど、その向こう側まで余計に歩くようにしたり、それを2往復するようにしたりだとかで、1日に8km程度歩く毎日が続くようになった。その状態で1ヶ月くらいは筋肉痛になったり疲れ果てたりしてたけど、歩き始めてから今日までで3ヶ月くらいが経ってだいぶ余裕が出てきた。&lt;/p&gt;
&lt;p&gt;日常の中で筋力的な向上を実感した瞬間として、僕は昔から5本指の靴下を好んで履いていて靴下は全部これなんだけど、履くのがスムーズにいかない。ちゃんと履けるまでちょっと時間が掛かるので、弱っていたころにはこれが本当にしんどかった。これを今では立ったまま、片足立ちの状態でできるところまで回復した。全盛期にはこの片足立ちのバランス感覚にかなり自信があったんだけど、これが少し戻ったみたいで嬉しい。&lt;/p&gt;
&lt;p&gt;ついでに歩くだけでは無酸素運動が不足しているので、2日に一度腕立て伏せをするようになった。これも最初は全然できなかったけど、最近では普通にすると負荷が足りないのでプッシュアップバーを使って、それもかなりストリクトなフォームで行えるようになっている。これに関してもかなり筋力が戻ってきた。&lt;/p&gt;
&lt;p&gt;こういうルーティンには1時間半くらい掛かってしまっているんだけど、リモートワークで通勤時間がなくなったおかげでなんとか相殺できている。もっとも、通勤してたら不要な運動だったかもしれないが。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;ここまで書いてきて、なんでこれを書き始めたんだっけと思ったけど思い出せない。ブログは最近あんまり書けていない。これは偶然というより、実はEvery Layoutの翻訳が遅れている罪悪感から精神的なアレで書きづらくなっているという理由でして、まだ出せてないことについては本当にすみません。進めてはいます。という感じなんだけど、なぜか急に筆がノッてしまったので書いてしまった。ついでにEvery Layoutについて言えば、ちょうど最近やっている仕事でこの理論をかなりうまく活用できていて、もしかしたら「&lt;a href=&quot;https://yuheiy.hatenablog.com/entry/2020/05/18/094715&quot;&gt;実践的レイアウトプリミティブ&lt;/a&gt;」のその先の発展形の話をどこかの機会で出せるかもしれない。そんな気がする。&lt;/p&gt;</content:encoded></item><item><title>「JavaScript sprinkles in Basecamp turned Stimulus」の雑要約</title><link>https://yuheiy.com/20201103-javascript-sprinkles-in-basecamp-turned-stimulus</link><guid isPermaLink="true">https://yuheiy.com/20201103-javascript-sprinkles-in-basecamp-turned-stimulus</guid><description>「JavaScript sprinkles in Basecamp turned Stimulus」より。Stimulusの設計意図を理解できる貴重な資料なのでまとめた。</description><pubDate>Tue, 03 Nov 2020 08:59:05 GMT</pubDate><content:encoded>&lt;p&gt;「&lt;a href=&quot;https://changelog.com/podcast/286&quot;&gt;JavaScript sprinkles in Basecamp turned Stimulus&lt;/a&gt;」より。&lt;a href=&quot;https://stimulusjs.org/&quot;&gt;Stimulus&lt;/a&gt;の設計意図を理解できる貴重な資料なのでまとめた。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;DHHにとってプログレッシブエンハンスメントとは、コンテンツのプログレッシブ性を実現するためというよりは、アプリケーション開発のための道標のようなもの。ベースとしてのHTMLはまずサーバーに生成させて、動的部分としての少しの差分をJavaScriptに扱わせる。BasecampやGitHubのように動的部分の少ないアプリケーションにとってはそれが適切なやり方。React/ReduxやAngular、そのほかクライアントサイドのMVCフレームワーク等を試してきたが、それによってプロダクトのコードが良くなることはなかった。&lt;/p&gt;
&lt;p&gt;BasecampにあるJavaScriptを調査して、どのような慣習があるか、それを踏まえてどのようなスタイルが受け入れられるかのインスピレーションを得た。メンタルモデルに合わせたやり方をフレームワークによってバックアップされるとより良いコードが書けるようになるはずであると。&lt;/p&gt;
&lt;p&gt;StimulusによってBasecampのコードに見られた特定の問題や悪いパターンを解決したかった。まず要素選択の方法について。ある要素の親の親の親を取得するなどしてDOM構造にべったりだった。誰かがマークアップを少し変更するとこれは簡単に壊れてしまう。CSSクラスをターゲットにするパターンもあったが、CSSクラスはスタイリングを関心とした名前を持っているので、JavaScript側の関心と合わない。スタイル変更のためにマークアップを変更したりしてしまうとすぐまた壊れてしまう。&lt;/p&gt;
&lt;p&gt;Stimulusではコントローラーに固有なターゲット名をつけることにした。選択する要素は明示的になり、コントローラーの中で好きな位置に移動することができて、input要素でもbutton要素でもspan要素にでもアタッチできる。それだけでなく、HTMLを読んだときに、どの要素がどの振る舞いに使われるのかという目的がかなり明確になる。&lt;/p&gt;
&lt;p&gt;またBasecampでは同じような機能を実装する重複するコードがいくつも存在していたが、CSSクラス名やHTML要素名と密になっていたので汎用化できなかった。Stimulusによってマークアップと疎になることで再利用性が獲得できる。Reactのようなコンポーネント指向のJavaScriptでも、振る舞いがテンプレートから切り離せなくなって再利用性がなくなるので好きじゃない。&lt;/p&gt;
&lt;p&gt;次にアクションについて。これまでBasecampが行っていたのは、HTMLに&lt;code&gt;data-behavior&lt;/code&gt;属性と機能名を記述しておいて、イベントが発火されたときにこれらの属性を読み取って実行するというもの（&lt;a href=&quot;https://signalvnoise.com/posts/3167-code-spelunking-in-the-all-new-basecamp&quot;&gt;Code Spelunking in the all new Basecamp&lt;/a&gt;）。DHHとしてはこれではHTMLを見ても何をやっているのかわからないのが嫌だった。このボタンをクリックするとどういうことが起こる、というのを明示的にしたかった。なのでHTMLに直接JavaScriptのイベントハンドラを記述するようにした。これでいちいちJavaScriptのコードを読む必要がなくなった。&lt;/p&gt;
&lt;p&gt;これらをコントローラーというコンセプトのもとにカプセル化した。Basecampのさまざまな振る舞いを抽出するのに、ターゲットとアクション、コントローラーというシンプルな3つのコンセプトだけで十分だったのには驚いた。&lt;/p&gt;
&lt;p&gt;また条件に応じてCSSクラス名を追加するような機能はどのようにして汎用化できるのかというと、StimulusのData MapsによってHTMLの&lt;code&gt;data-*&lt;/code&gt;属性に個別のクラス名を渡せるようになっている。これによってデザイナーが些細な変更のためにコントローラーを触る必要もなくなる。コントローラー内にはCSSクラス名を直接含めるべきではない。&lt;/p&gt;
&lt;p&gt;非常に複雑なUIを作ろうとしているのであればReactなどのような重いフレームワークは理にかなっているが、一方でGitHubやBasecampのようなアプリケーションが取り組んでいるフレームワークは過小評価されているように感じる。昨今のJavaScriptフレームワークの盛り上がりによって、サーバーはJSONを生成するだけのものになって、クライアントサイドでもふたたび以前のサーバーと同じような仕事をするためにいろんなことをいちからやり直している。Basecampでは、必要があればサーバーが返したHTMLの破片をStimulusが使う。&lt;/p&gt;
&lt;p&gt;BasecampのコードベースをStimulusへ移行するに当たって、HTML内に自己文書化されたコードが記述されるようになることでドキュメントの量をかなり減らすことができた。大量のコードコメントが意味するのは、複雑なコードを書いてしまったことか、あるいは抽象化されることを待っている一連の規則の存在である。&lt;/p&gt;
&lt;p&gt;Stimulusでは状態はHTML上に保持されている。コントローラーのルート要素にデータ属性として状態を格納できるが、基本的にはこれは最小限にする。多くの場合、HTMLに付与されているCSSクラス名や属性の有無などで大抵の状態は表現できる。&lt;/p&gt;
&lt;p&gt;Turbolinksは便利だが、jQueryや通常のJavaScriptの実装と組み合わせるのに難があって避けられていた。StimulusはMutationObserverを採用して動的なページ書き換えに対応することによってこのような問題をきれいに解決できた。&lt;/p&gt;
&lt;p&gt;しかしTurbolinksは再評価されるだろうか。ある痛みは何か別の利益を得るためのトレードオフとなるが、まずメリットを得られるかわからない場合はコストが高すぎるし、メリットがゼロだと感じるならその人は自分には価値がないと言うだろう。そしておそらく、昨今のモダンなJavaScriptのフレームワークを採用することで、かなりのコストもかかってしまうことに気づく人も増えていて、それらを経験した人は、これはもうただただ苦痛だったと語るだろう。Basecampが取り組もうとしているのはこの痛みを取り除くことだが、あなたがその痛みに苦しむまではBasecampがやろうとしていることを十分に理解できないだろう。痛みを知るまでは評価できないだろう。&lt;/p&gt;
&lt;p&gt;実際の痛みから切り離されたベストプラクティスは意味をなさないことがよくある。人はそれが必要なときになるまで自分ごとにする準備ができていない。誰かが何かに苦しむまで価値は理解されない。&lt;/p&gt;
&lt;p&gt;しかし私たちは協力して歴史的背景を人に伝えることもできる。&lt;a href=&quot;https://stimulusjs.org/handbook/origin&quot;&gt;The Origin of Stimulus&lt;/a&gt;（&lt;a href=&quot;https://yuheiy.hatenablog.com/entry/2019/05/02/204549&quot;&gt;和訳&lt;/a&gt;）はそれを意図したもの。ある技術を採用するためには「FacebookがReactを使っているから」だけでは十分な理由にならない。Basecampで解決しようとしているのとは違う問題と背景がある。Facebook自身でさえ、最初からいま確立されているやり方で始めてしまっていれば、Facebookそのものが存在しなかっただろう。&lt;/p&gt;
&lt;p&gt;Basecampがこれらを推進しているからといって、誰もがこれを使うようになるという幻想は持っていない。Reactなどモダンなツール群には勢いがあって、そういうものだと思うし、これらの支配に勝つ必要はない。開発コミュニティは市場シェアがすべてであるような考えを持っていることがあるが、ウェブの素晴らしさはその多様性である。バックエンドは必要に応じてあらゆる実装言語で書くことができて、だからより多くの人が関わることができるし、自分に合うものを選択できるので、このおかげでDHHはRubyとRailsに出会うことができた。さまざまなパラダイムが増殖する中で、トランスパイラなどを用いて、JavaScriptの世界でもこの多様性が進歩すれば良いことだと思う。&lt;/p&gt;</content:encoded></item><item><title>画像ファイルもCSSやJSファイルと一元化したディレクトリで管理する</title><link>https://yuheiy.com/20200918-flatten-assets-directories</link><guid isPermaLink="true">https://yuheiy.com/20200918-flatten-assets-directories</guid><description>ウェブサイトのソースファイルはファイル形式にもとづいてディレクトリ分けされている場合が多いと思う。たとえば次のように。</description><pubDate>Fri, 18 Sep 2020 09:16:35 GMT</pubDate><content:encoded>&lt;p&gt;ウェブサイトのソースファイルはファイル形式にもとづいてディレクトリ分けされている場合が多いと思う。たとえば次のように。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; dist/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; images/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;       ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;       └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; icons/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; chevron.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; scripts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; main.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; styles/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; main.css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; images/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;       ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │&lt;/span&gt;&lt;span&gt;       └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; icons/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; chevron.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; scripts/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; header.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; main.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; styles/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │&lt;/span&gt;&lt;span&gt;   ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │&lt;/span&gt;&lt;span&gt;   │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; _header.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │&lt;/span&gt;&lt;span&gt;   └──&lt;/span&gt;&lt;span&gt; main.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;images&lt;/code&gt;・&lt;code&gt;scripts&lt;/code&gt;・&lt;code&gt;styles&lt;/code&gt;ディレクトリがルートにあって、ファイルはまずそのいずれかに格納される。各ディレクトリの中では、その中にサブディレクトリを作成して、あるファイルが特定の目的にもとづいていることを表現していく。&lt;/p&gt;
&lt;p&gt;ここからファイル形式によるディレクトリ分けを取っ払って、最初からその目的にもとづいた構造にしたい。たとえばこうなる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├──&lt;/span&gt;&lt;span&gt; dist/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt;     ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt;     └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; icons/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; chevron.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; main.css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; main.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; index.html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; assets/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; header.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; header.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; icons/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; chevron.svg&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; main.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;ファイル形式で区別するのをやめて、すべてのファイルを&lt;code&gt;assets&lt;/code&gt;ディレクトリに一元化するようにした。ファイルを探したり編集したりする上ではまずファイルが何のために存在しているかが重要になるし、近い場所に集まっている方がファイル同士に関連性を見いだしやすい。&lt;/p&gt;
&lt;p&gt;ちなみに、&lt;code&gt;components&lt;/code&gt;ディレクトリの直下に&lt;code&gt;header.js&lt;/code&gt;と&lt;code&gt;header.scss&lt;/code&gt;が配置されている点については気になる人がいるかもしれない。たぶん次のような構造の方が一般的だろう。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; header.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; header.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;しかしこれは、特定のコンポーネントに含まれるファイル数が少ない場合には冗長になる。今回の例では、アプリケーション的ではなくドキュメント的なウェブサイトのソースコードを想定していて、そういう場合、JavaScriptファイルを必要としないCSSだけのコンポーネントがほとんどになったりする。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; breadcrumb/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; breadcrumb.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; button/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; button.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; card/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; card.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; footer/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; footer.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; header.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; header.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;すると、複数ファイルを必要とするごく一部のコンポーネントに合わせたディレクトリ構造を実現するために、その他大多数のコンポーネントを取り扱うための手間が増えてしまう。ならば逆に複数ファイルを必要とするコンポーネントの方を例外として捉えて、単一ファイルでの完結が基本の構造にした方が合理的。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└──&lt;/span&gt;&lt;span&gt; components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; header/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; ├──&lt;/span&gt;&lt;span&gt; background.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │  &lt;/span&gt;&lt;span&gt; └──&lt;/span&gt;&lt;span&gt; logo.png&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; breadcrumb.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; button.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; card.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; footer.scss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├──&lt;/span&gt;&lt;span&gt; header.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └──&lt;/span&gt;&lt;span&gt; header.scss&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;このようなディレクトリ構造を実現するためにはこれに合わせたビルド設定を行わなければならない。ブラウザに読み込まれたアセットファイルのキャッシュを破棄するために、&lt;a href=&quot;https://railsguides.jp/asset_pipeline.html#%E3%83%95%E3%82%A3%E3%83%B3%E3%82%AC%E3%83%BC%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9&quot;&gt;ファイル名にハッシュ文字列を加えるCache busting&lt;/a&gt;が利用できると望ましいので、その利用も前提として解説する。&lt;/p&gt;
&lt;p&gt;webpackで画像などのファイル名をハッシュ化する場合、そのファイル名を指定するためには直接パスを書き込むのではなく、webpack経由で読み込んだデータを割り当てる必要がある。&lt;/p&gt;
&lt;p&gt;NG:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// `src/assets/components/header.js`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;img.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;/assets/components/header/logo.png&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;OK:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// `src/assets/components/header.js`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; logo &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;./header/logo.png&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;img.src &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; logo;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// -&amp;gt; `/assets/components/header/logo.[contenthash].png`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;CSSファイル内の&lt;code&gt;url()&lt;/code&gt;関数からもファイルを参照するためには、CSSファイル自体もwebpack経由でビルドする必要がある。そのように設定すれば次のように参照できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// `src/assets/components/header.scss`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.header&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background-image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;./header/background.png&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // -&amp;gt; `/assets/components/header/background.[contenthash].png`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;またwebpackの外からファイル名を参照したい場合には、&lt;a href=&quot;https://github.com/danethurber/webpack-manifest-plugin&quot;&gt;Webpack Manifest Plugin&lt;/a&gt;を利用するとファイル名の対応付けをJSONファイルとして出力できる。出力されるJSONファイルを読み込めばwebpackの外の仕組みからもファイル名を参照できるようになる。&lt;/p&gt;
&lt;p&gt;これらを前提とすると&lt;code&gt;webpack.config.js&lt;/code&gt;はだいたい次のような感じになる。いろいろ簡略化している。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; path&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;path&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sass&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;sass&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; Fiber&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;fibers&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; MiniCssExtractPlugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;mini-css-extract-plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; TerserJSPlugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;terser-webpack-plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; OptimizeCSSAssetsPlugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;optimize-css-assets-webpack-plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; ManifestPlugin&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;webpack-manifest-plugin&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  context: path.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;src&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;assets&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  entry: &lt;/span&gt;&lt;span&gt;&apos;./main.js&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  output: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    path: path.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;dist&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;assets&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    filename: &lt;/span&gt;&lt;span&gt;&apos;[name].[contenthash:8].js&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    chunkFilename: &lt;/span&gt;&lt;span&gt;&apos;[name].chunk.[contenthash:8].js&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    publicPath: &lt;/span&gt;&lt;span&gt;&apos;/assets/&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  module: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    rules: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        test:&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(scss&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;css)&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        use: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          MiniCssExtractPlugin.loader,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &apos;css-loader&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            loader: &lt;/span&gt;&lt;span&gt;&apos;sass-loader&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            options: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              implementation: sass,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              sassOptions: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                fiber: Fiber,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        exclude: [&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(js&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;mjs)&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(scss&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;css)&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        use: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            loader: &lt;/span&gt;&lt;span&gt;&apos;file-loader&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            options: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              name: &lt;/span&gt;&lt;span&gt;&apos;[path][name].[contenthash:8].[ext]&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  optimization: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    minimizer: [&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; TerserJSPlugin&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; OptimizeCSSAssetsPlugin&lt;/span&gt;&lt;span&gt;()],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; ManifestPlugin&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      fileName: path.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(__dirname, &lt;/span&gt;&lt;span&gt;&apos;dist&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;webpack-manifest.json&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    new&lt;/span&gt;&lt;span&gt; MiniCssExtractPlugin&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      filename: &lt;/span&gt;&lt;span&gt;&apos;[name].[contenthash:8].css&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ].&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;そして&lt;code&gt;main.js&lt;/code&gt;にはこのように書いておく。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// `components`ディレクトリ直下にあるすべてのSCSSファイルを読み込む&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;importAll&lt;/span&gt;&lt;span&gt;(require.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;./components&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;scss&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; importAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  context.&lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(context);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// webpackのモジュールからは読み込まれていないファイルもビルドに含めることで、&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// `assets`ディレクトリに配置したあらゆるファイルを`webpack-manifest.json`経由で参照できるようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;require.&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &apos;.&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(jpg&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;jpeg&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;png&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;gif&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;eot&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;otf&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;webp&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;ttf&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;woff&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;woff2&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;mp4&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;webm&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;wav&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;mp3&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;m4a&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;oga)&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Sassファイルを読み込む上での注意点として、&lt;code&gt;main.scss&lt;/code&gt;のようなSass用のエントリーポイントを作成せずに、JavaScriptファイルからファイルをひとつずつ読み込むようにしておいた方が良い。Sass固有のモジュール機能を使うと、css-loaderに渡ってくる前にコンパイルが済んで結合されてしまうので、&lt;code&gt;url()&lt;/code&gt;関数で参照するファイルのパス解決がすべて&lt;code&gt;main.scss&lt;/code&gt;の位置を基準に行われてしまう。すると&lt;code&gt;components&lt;/code&gt;ディレクトリ内のファイルにおいても&lt;code&gt;main.scss&lt;/code&gt;の位置からパスを記述しなければならなくなる。&lt;/p&gt;
&lt;p&gt;次に、webpackのモジュールからは読み込まれていないファイルも、出力される&lt;code&gt;webpack-minifest.json&lt;/code&gt;から参照できるようにするために、あらゆるファイルが自動的に読み込まれるようにしておく。もしこの設定を行わない場合も、JavaScriptから&lt;code&gt;import&lt;/code&gt;されたり、CSSの&lt;code&gt;url()&lt;/code&gt;関数に指定されれば、対象のファイルがwebpackのビルドに含まれつつJSONファイルにもパスが追加される。しかしそれ以外のファイルはビルドには含まれなくなってしまうので、JavaScriptやCSSから読み込まれていないファイルをwebpackの外から参照できなくなってしまう。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Jestを利用している場合、JavaScriptファイルから画像ファイルなどを読み込むとエラーになるので、&lt;code&gt;jest.config.js&lt;/code&gt;に次のように設定しておくと回避できる。&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  moduleNameMapper: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &apos;&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &apos;&amp;lt;rootDir&amp;gt;/src/assets/test/__mocks__/fileMock.js&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;src/assets/test/__mocks__/fileMock.js&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &apos;test-file-stub&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;設定ファイルの詳細な書き方などは、前述したような設定に開発用ビルドなども含めた&lt;a href=&quot;https://github.com/yuheiy/_shifted/tree/master/boilerplate-static&quot;&gt;ボイラープレートを公開している&lt;/a&gt;ので参照されたし。&lt;/p&gt;</content:encoded></item><item><title>ブログ投稿のハードルと雑さについて</title><link>https://yuheiy.com/20200817-zatsu</link><guid isPermaLink="true">https://yuheiy.com/20200817-zatsu</guid><description>個人的な話、Twitterにいろいろと長文も書いたりするようになってからブログをあまり書かなくなった。タイムラインのツイートはすぐに流れていく分、今パッと思いついた話とか、まだ十分整理されてないようなテキストを出しやすい。</description><pubDate>Sun, 16 Aug 2020 09:33:36 GMT</pubDate><content:encoded>&lt;p&gt;個人的な話、Twitterにいろいろと長文も書いたりするようになってからブログをあまり書かなくなった。タイムラインのツイートはすぐに流れていく分、今パッと思いついた話とか、まだ十分整理されてないようなテキストを出しやすい。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ji-sedai.jp/series/outliner/03.html&quot;&gt;アイデアをTwitterに書きやすいのは取り返しがつかないからだと言ってる人がいた&lt;/a&gt;。まず最初のツイートをすると、それが少なくとも何人かに読まれてしまった事実は取り返しがつかなくなるので、その責任の引き受けから連鎖してほかのものも書けると。その話を読んで、ツイートはリアルタイムにしゃべることに似ている気がした。一度しゃべり始めてしまった以上は、とりあえず着地するまではしゃべりを続けないといけないというのと似ている。&lt;/p&gt;
&lt;p&gt;そうしたとりあえずのものがツイートとして消化されてしまうとすれば、ブログを書くためには「あえて」そうする動機が必要になってくる。「あえてのもの」は即席じゃないし、ある程度考えられていてまとまっている。しかしそれを書くためには、考えがキリのいいところまで到達したタイミングを見計らわなければいけない。半端なタイミングで書いてしまうと公開後にすぐ継ぎ足したくなる。つまりすぐ古くなる。&lt;/p&gt;
&lt;p&gt;こうした問題に対して、&lt;a href=&quot;https://scrapbox.io/shokai/%E6%AD%BB%E3%82%93%E3%81%A0%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%92%E7%BD%AE%E3%81%84%E3%81%A6%E3%81%8A%E3%81%8F%E5%80%89%E5%BA%AB%E3%81%AB%E3%81%97%E3%81%AA%E3%81%84&quot;&gt;Scrapboxは完成をなくす形でアプローチしている&lt;/a&gt;。書くことのすべての段階においてつねに最新の状態で公開されていて、完成を意味する印づけがない。ずっと途中なのでいつでも更新できる。考え続けるためのデザイン。&lt;/p&gt;
&lt;p&gt;一方で僕はブログ投稿にはけじめをつける意味も感じている。その時点での自分のスナップショットを取って、いったん忘れるために考え終わらせる。適当な大きさの考えの塊を切り取って保存する。そして仮想メモリに空きを作って次に行く。&lt;/p&gt;
&lt;p&gt;これは一見つねに更新し続ける考え方と相反している。しかしScrapboxのように塊をノードとして切っていくのは僕にとって難しかった。その切り取り方自体がもしかしたら毎日変わっていくかもしれないようなもので、日々ページに差分を加えていく運用よりは、捉え方も語り方もまるごと変わった新しい塊を都度作っていく方が向いているように思えた。塊として一度吐き出してしまえれば、忘れたつもりになっても自分の中ではある程度覚えているし、過去のその断片を足がかりにして今の自分が勝手に更新されている。もっともこれがどの程度スケールしているかはわからないし、自分ひとりで書いていく場合という前提の話でもある。&lt;/p&gt;
&lt;p&gt;そんなわけで僕は都度完成させていくやり方を好んでいる。そのためにはツイートでは単位が小さすぎるし、連続ツイートでも書き捨ての向きが強すぎる。だからブログ投稿に執着しているが、依然としてそこにはハードルがある。&lt;/p&gt;
&lt;p&gt;そこで僕はブログ投稿に雑さを取り入れようと意識してきた。ある程度キリがいいところまで来た感じがすれば——それもそこまでちゃんとしてなくても——、完成度をあまり高めないでも気楽に出せるように。僕は特に、ブログの投稿先にちゃんとしている感じがあると、どうしてもちゃんと書かないといけないモードに入ってしまう。ブログ投稿の側（がわ）であるところのブログという体裁そのものに力を入れてしまうと、ウェブ制作者としてはやはりかなり意識してしまう。ちゃんとしてないものを出しにくくなる。さらに体裁というのも古びてくるので更新したくなるが、これにはそれなりのリソースを吸い取られて、結果投稿自体をする気がなくなってしまう。僕はこれまでに自分用の個人ブログを10近く作っては捨ててきた。&lt;/p&gt;
&lt;p&gt;だから側を見て見ぬ振りするためにあえてレンタルブログ（死語？）をしばらく使っていた。テーマもあらかじめ用意されているものの中から選んで、カスタマイズのためにオリジナルのCSSは書いたりしないという縛りつきにした。わかる人にはわかると思うが、ブログひとつ作るのでも徹底的にやろうとすると際限がなく、サイト生成用のスクリプトを延々とリファクタリングし続けたり、かっこいい管理画面を作ろうとしようしたりしてしまう。しかしレンタルブログは適度に機能が制限されていて、作るためにほとんど労力がかからずそれほど愛着も湧かないので、とりあえず書き続けるためにはちょうどいい代物だった。&lt;/p&gt;
&lt;p&gt;しかしながら、ちょっとした「自分の」サイトを作りたいという人が生まれながらにして持つ欲求にはやはり抗えず、レンタルブログを使い続けながらもその気持ちだけが膨らんできた。完全にはコントロールできない外部サービスに自分のコンテンツを預けている不安も少し大きくなってはいる。ついでにサイトを作るコストも減っている。また出先ではレンタルブログの管理画面を使って記事の編集をする機会もあったが、引きこもりがちになってから書くことをローカル環境でやりやすくもなっているのでGitベースにするのもありだ。後々管理画面が必要になったら&lt;a href=&quot;https://www.netlifycms.org/&quot;&gt;Netlify CMS&lt;/a&gt;でも入れればいい。&lt;/p&gt;
&lt;p&gt;そうは考えつつも実際にこれを実行するきっかけになったのは、&lt;a href=&quot;https://rokuzeudon.hatenablog.jp/entry/newblog&quot;&gt;はてなブログマニアだと思っていた友人がはてなブログをやめたこと&lt;/a&gt;だ。これも時代の流れなのかなみたいな自分への雑な言い訳がこのおかげでできるようになった。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;自分のサイトを作りたいとは言いつつも、一応1ページだけのちょっとした自己紹介サイトのようなものは持っていた。この自己紹介サイトにサブドメインを生やして分離した別サイトを作ろうかとも思ったが、同一サイトを拡張する形にした。別サイトとして分けると、そこに専用性を感じて形式張ってしまうし、飽きたらサブドメインごと捨てるのが気軽にできてしまう。自分でもある程度参照する機会があるこのサイトにあえてブログを組み込むことで、意識する場面を増やしたり取り返しがつきにくいようにした。&lt;/p&gt;
&lt;p&gt;ブログを組み込む前からこのサイトには、自分の投稿したさまざまな場所に散らばっているリソースを集約させる役割を持たせていた。以前のレンタルブログのほかにも、別のところで書いた記事とか、発表したスライドだけ独立させたページとかがいくつかあって、それらもスナップショットとして見返す場面があるので一覧できるようにしていた。列挙するほどでもないものは除外して主要なものだけを。そのリソースについてもいろいろなフォーマットがあって、メディア別に分類したり、「発表資料」のようなカテゴライズをするにも無理があったので、汎用的なビューにしてすべてを一元化して扱っていた。&lt;/p&gt;
&lt;p&gt;サイトに生えたこのブログもその雑多なフォーマットの一部であって、また以前と同様に主要なリソースだけピックアップする形を継承したかったので、主要なリソースの一覧は従来通り維持しつつ、このサイトの投稿の一覧は別に作ることにした。&lt;/p&gt;
&lt;p&gt;ついでに取り返しのつかなさと関連して、&lt;a href=&quot;https://www.kanzaki.com/docs/Style/URI&quot;&gt;URLは未来永劫変わらないことが理想&lt;/a&gt;なので、後から気が変わっても大丈夫なようにサイト構造をURLに反映させないようにした。サイト内にあるブログカテゴリのURLであれば、&lt;/p&gt;
&lt;p&gt;&lt;samp&gt;&lt;p&gt;&lt;a href=&quot;https://yuheiy.com&quot;&gt;https://yuheiy.com&lt;/a&gt;&lt;em&gt;/blog/20200817-zatsu&lt;/em&gt;&lt;/p&gt;&lt;/samp&gt;&lt;/p&gt;
&lt;p&gt;のようにサイト構造がパスとして表現される場合がよくあるが、&lt;/p&gt;
&lt;p&gt;&lt;samp&gt;&lt;p&gt;&lt;a href=&quot;https://yuheiy.com&quot;&gt;https://yuheiy.com&lt;/a&gt;&lt;em&gt;/20200817-zatsu&lt;/em&gt;&lt;/p&gt;&lt;/samp&gt;&lt;/p&gt;
&lt;p&gt;というように&lt;a href=&quot;http://chushoww.blogspot.com/2009/08/um-web.html&quot;&gt;階層を取っ払ったフラットなパス&lt;/a&gt;にした。将来的に「ブログ」という括りをやめたくなったとしてもこうしていれば問題ない。あとURLに日付を入れずにタイトルだけにする運用にも憧れていたが、&lt;a href=&quot;https://standard.shiftbrain.com/&quot;&gt;実際にやってみて大変だった&lt;/a&gt;のでやめておいた。&lt;/p&gt;
&lt;p&gt;もともとこのサイトが規模的にショボかったので、なにか増やせるならなんとなく増やしたいような気持ちもあった。実はこれが一番大きい動機かもしれない。&lt;/p&gt;</content:encoded></item><item><title>Tailwind CSSの設計思想を業務に活かす</title><link>https://www.codegrid.net/series/2020-tailwind-talk</link><guid isPermaLink="true">https://www.codegrid.net/series/2020-tailwind-talk</guid><pubDate>Thu, 09 Jul 2020 00:00:00 GMT</pubDate></item><item><title>UIにおける見えるけど利用できない非活性な領域の実装とinert属性について</title><link>https://standard.shiftbrain.com/blog/unavailable-inert-regions-and-inert-attribute</link><guid isPermaLink="true">https://standard.shiftbrain.com/blog/unavailable-inert-regions-and-inert-attribute</guid><pubDate>Tue, 30 Jun 2020 00:00:00 GMT</pubDate></item><item><title>CSSのユーティリティクラスと「関心の分離」&amp;mdash;&amp;mdash;いかにしてユーティリティファーストにたどり着いたか（翻訳）</title><link>https://yuheiy.hatenablog.com/entry/2020/05/25/021342</link><guid isPermaLink="true">https://yuheiy.hatenablog.com/entry/2020/05/25/021342</guid><pubDate>Mon, 25 May 2020 00:00:00 GMT</pubDate></item><item><title>レイアウトプリミティブ</title><link>https://standard.shiftbrain.com/blog/layout-primitives</link><guid isPermaLink="true">https://standard.shiftbrain.com/blog/layout-primitives</guid><pubDate>Mon, 16 Dec 2019 00:00:00 GMT</pubDate></item><item><title>「代替」の意味を探して</title><link>https://yuheiy.github.io/meanings-of-the-alternative/</link><guid isPermaLink="true">https://yuheiy.github.io/meanings-of-the-alternative/</guid><pubDate>Tue, 10 Sep 2019 00:00:00 GMT</pubDate></item><item><title>レスポンシブデザインに見るデザインカンプと実装との溝</title><link>https://standard.shiftbrain.com/blog/the-gap-between-design-and-implementation-in-responsive-design</link><guid isPermaLink="true">https://standard.shiftbrain.com/blog/the-gap-between-design-and-implementation-in-responsive-design</guid><pubDate>Mon, 26 Aug 2019 00:00:00 GMT</pubDate></item><item><title>mix-blend-modeとスタックコンテキスト</title><link>https://standard.shiftbrain.com/blog/mix-blend-mode-and-stacking-context</link><guid isPermaLink="true">https://standard.shiftbrain.com/blog/mix-blend-mode-and-stacking-context</guid><pubDate>Mon, 29 Apr 2019 00:00:00 GMT</pubDate></item><item><title>制作者のためのHTML</title><link>https://yuheiy.github.io/html-for-creators/</link><guid isPermaLink="true">https://yuheiy.github.io/html-for-creators/</guid><pubDate>Fri, 08 Mar 2019 00:00:00 GMT</pubDate></item><item><title>ユーザー自身の道具になるためのインターフェイス</title><link>https://yuheiy.github.io/interface-to-become-your-own-tool/</link><guid isPermaLink="true">https://yuheiy.github.io/interface-to-become-your-own-tool/</guid><pubDate>Mon, 18 Feb 2019 00:00:00 GMT</pubDate></item><item><title>ページ内リンクの実装から考える、a要素のclickイベントとその振る舞い</title><link>https://standard.shiftbrain.com/blog/default-action-for-click-event-of-a-element</link><guid isPermaLink="true">https://standard.shiftbrain.com/blog/default-action-for-click-event-of-a-element</guid><pubDate>Fri, 26 Oct 2018 00:00:00 GMT</pubDate></item></channel></rss>