シカクいブログ

あるプログラマーの随筆

フロントエンドに対する愚考

classという曖昧な存在

二つのHTMLがある。

<!-- some.html -->
<div id="some">
  <div class="button">ボタン</div>
</div>

<!-- home.html -->
<div id="home">
  <div class="button">ボタン</div>
</div>

さてbuttonというトークはどういう意味を持っているのだろうか?

  1. 要素の役割を説明するための、ただの文字列。
  2. cssから.buttonでスタイルが当たっている。
  3. cssから#some .buttonでスタイルが当たっている。
  4. cssから#home .buttonでスタイルが当たっている。
  5. jsから.buttonで処理が登録されている。
  6. jsから#some .buttonで処理が登録されている。
  7. jsから#home .buttonで処理が登録されている。

答えは上記のどれか、もしくはいくつかを組み合わせたものであり、また二つのbuttonが同じ意味を持つとは限らない。

答えは上記のどれか、もしくはいくつかを組み合わせたものであり、また二つのbuttonが同じ意味を持つとは限らない

ふざけてる。何かしらの県の条例に違反しているに違いない暴挙だ。

HTMLもCSSプログラミング言語ではないが、コードである

誰がなんと言おうとHTMLもCSSプログラミング言語ではない。そんなことはケツを拭いた紙で顔を拭いてはいけないことくらい明らかだ。

しかしだからと言って管理が必要ないわけではない追跡できなくても良いわけではない

CSSに注視する

まずはCSSを吊し上げる。JSも似たようなものだから、脳内でいい感じに変換して欲しい。

/* base.css */
.button {
  width: fit-content;
  padding: 0.5rem;
  border: solid;
}

/* home.css */
#home .button {
  color: red;
}
<!-- some.html -->
<div id="some">
  <div class="button">ボタン</div>
</div>

<!-- home.html -->
<div id="home">
  <div class="button">赤文字ボタン</div>
</div>

はっ。ゴミだ。ファミチキだけ先に食べてしまった後に残ったファミチキバンズくらいゴミだ。

どこがどうゴミなのか

二足歩行動物ならば、上記のコードのどこがダメなのかはすぐに理解できるはずだ。なのでこのセクションは猫に向けて書いたものである。

まずは発想の転換だ。CSSだのHTMLだの、そんな先入観はファミチキバンズに挟んで熱々のMacBookの上に置いておこう。

特殊なプログラミング言語として捉えるのだ

home, someはネームスペース、buttonは関数、cssは関数定義、class="button"button関数に引数としてその要素を渡す、とても気持ち悪い書き方だ。

このルールに従ってJSっぽい擬似コードとして書き換えるとこうなる。

// #1
function button(element) {
  element.width = "fit-content";
  element.padding = "0.5rem";
  element.border = "solid";
}

namespace home {
  // #2
  function button(element) {
    element.color = "red";
  }
}

namespace some {
  // #1のbutton関数が呼ばれる。
  button(window.some.buttonElement);
}

namespace home {
  // #1と#2のbutton関数が連続して呼ばれる。
  // ここだけ見てもそれはわからない(追跡不可能)。
  button(window.home.buttonElement)
}

同名の関数が存在すれば暗黙的にその全てを順番に呼び出してくれる、そんな親切なプログラミング言語だ。なんとネームスペースまで考慮してくれるぞ!

こんなプログラミング言語があったらどうだろうか? 少なくとも私は使いたくない。世の中の主流プログラミング言語の仕組みの傾向から見るに、多くの人たちは私と同意見のはずだ。

どうしてもこの言語を使わざるを得なくなっても、メンテナンス可能なコードにするために、暗黙的な呼び出し機能を回避してこう書き換えるはずだ。

function button(element) {
  element.width = "fit-content";
  element.padding = "0.5rem";
  element.border = "solid";
}

function homeButton(element) {
  // 明示的に呼び出すことで、追跡可能となった。
  button(element);
  element.color = "red";
}

namespace some {
  button(window.some.buttonElement);
}

namespace home {
  // 特定の一つの関数に絞られたため、追跡可能となった。
  homeButton(window.home.buttonElement)
}

なんならこう。

function button(element) {
  element.width = "fit-content";
  element.padding = "0.5rem";
  element.border = "solid";
}

namespace some {
  button(window.some.buttonElement);
}

namespace home {
  button(window.home.buttonElement)
  // 関数にするほどのことでもない。
  window.home.buttonElement.color = "red";
}

以上のことを踏まえて、我々はどうすべきか

トークン(classだろうが何だろうが)は一つの意味しか持つべきではない。マイナンバーも一人一個だ。そういうことだ。

HTMLから最後の最後まで追跡可能な仕組みであること。無限の可能性を考慮するのに、人生は短すぎる。

CSS

  • 同名のブロックを決して定義しない(ネストしていても不可)
  • 当然再利用もしたい。

TailwindCSSを使う。applyが重要だ。

TailwindCSSは全部ベタ書きを推奨しているようなので、仕組みを使っても理念には(完全には)従わない。

@layer components {
  .button {
    @apply border-solid w-fit p-2;
  }
  .home-button {
    // buttonまで追跡可能。
    @apply button text-red-500;
  }
}
<!-- some.html -->
<div id="some">
  <div class="button">ボタン</div>
</div>

<!-- home.html -->
<div id="home">
  <!-- 特定の一つのcomponentに絞られるため、追跡可能。 -->
  <div class="home-button">赤文字ボタン</div>
</div>

なんならこう。

@layer components {
  .button {
    @apply border-solid w-fit p-2;
  }
}
<!-- some.html -->
<div id="some">
  <div class="button">ボタン</div>
</div>

<!-- home.html -->
<div id="home">
  <!-- 新しいcomponentにするほどのことでもない。 -->
  <div class="button text-red-500">赤文字ボタン</div>
</div>

場合によってはstyle-プレフィックスをつけても良いかもしれない。

JS

ReactやVueなら特に工夫する必要はない。

  • classとコールバックは必ず一対一
  • スタイルとの名前衝突回避のため、js-プレフィックスをつける。 スマートではないが、仕方がない。 Stimulusを使おう。
import $ from 'jquery';

$('.js-home-button').on('click', () => { console.log('Clicked home button.'); });
<!-- home.html -->
<div id="home">
  <div class="button">赤文字ボタン</div>
</div>

<!-- some.html -->
<div id="some">
  <!-- 特定の一つのJS処理に絞られるため、追跡可能。 -->
  <div class="home-button js-home-button">ボタン</div>
</div>

ここから先は普通にJS書いていれば追跡可能。

Stimulusは普通にしているだけで条件を満たせる。やはりRailsが最強か(Rails信者脳)。

結果

これでHTMLからその要素に何が起こるのかが追跡可能になった。削除漏れが起きづらい。

逆側からも同じで、そのスタイルorJSがどの要素に当たっているのかの追跡が楽になる。つまり影響範囲の調査も楽。

最後に

知らんけど。