Chienomi

はるらぼSPAを改修

開発::website

私の初めてのVanilla JavaScript SPA作品であるはるらぼだが、初期作品ということもあって現在の目から見るとかなり甘い。

記事更新も少ないことからなかなか手を入れる機会もなく、放置気味だった。

しかし、(Chienomiよりももっと)真面目な記事を上げる予定はそれなりにあるので、整備を必要としていた。

LWMPでVanilla JavaScript SPAの技術力が向上したことも踏まえて、重い腰を上げてついに改修に踏み切った。

ステートまわり

従来、はるらぼは標準のstateを使わずに独自のステート管理を導入していた。 これはstateで発生する微妙な不整合やタイミング問題を回避するためだったが、ある程度マシになる案を思いついたためにstateを利用する仕様にした。

ただし、メニューまわりのステートは素早くback/forwardを繰り返すと変になってしったため導入せず、ページ遷移する場合はメニューは自動で閉じるという挙動にした。

これに伴って従来存在した<- backメニューが消え、代わりにブラウザバッグが有効に機能するようになった。

論文モードの導入

はるらぼは世界観重視なので、必然的に長文は読みづらい。 一応文字のネオンを止める機能はあるのだが、それでもなお読みづらいという声がある。

そこで、読みやすさに特化した論文モードを導入した。

見た目はリーダーモードに近く、スタイルがほぼ入っていないように見えるかもしれないが、CSS自体は若干凝っていて、別にアクセシビリティ重視でもなく、SPAはそのまま機能する。

何が違うのかというと、bodyのclassを設定するようにしており、子孫結合子を使って適用するCSSを切り替えている。

UI切り替え機能も割と珍しいと思うが、ここまで大胆な切り替えは他にまずなさそうだ。

なお、実はブラウザのリーダービューもちゃんと機能するようになっているから、いらないといえばいらないのだが、リーダービューを活用するという発想がない人が多いようなので入れておいた。 本文をリファレンスとしてリンクシェアしたときにkawaiiバージョンを見せないためというのもある。

入れ子CSS

CSSはCSS Nesting Moduleによって入れ子表記が可能になった。

実際のところドキュメント構造をなぞるようなCSSというのはあまり良いものではないため、CSS Nesting Moduleを必要とする場面は8割くらいはそもそも必要とする必然性について考えを改めたほうが良いものだが、残り2割は主に特定のコンテンツブロックの中に対するスタイルを定義するために該当するものすべての祖先要素としてコンテンツブロックを書かねばならないことになり、このような場合にぜひ欲しいものだった。

今回のはるらぼ場合、body要素のclassを参照するため、入れ子にできるとかなり楽に書ける。

CSS Nesting Moduleの対応はCan I useによると

  • Chrome 120
  • Edge 120
  • Safari 17.2
  • Firefox 117
  • Opera 106
  • Chrome/Android 138
  • Firefox/Android 140

でサポートされたらしい。 だいたい、2023年内にはサポートされたが、まだ怪しい、のレベルか。

入れ子CSSの挙動については、こんなHTMLを作るとわかるが、だいたい自然に書ける。

<html>
  <head>
  <style>
#X {
  .y { color: red; }
  .y div { color: blue }
}
#X2 {
  div { color: green; }
}
#X3 {
  div, span { color: aquamarine; }
}
form {
  div { color: orange; }
}
  </style>
  </head>
  <body>
    <div>A
      <div id="X">X
        <div class="y">y
          <div>B</div>
        </div>
      </div>
    </div>
    <div id="X2">
      <div>X2a</div>
    </div>
    <div id="X3">
      <div>X3a</div>
      <span>X3b</span>
    </div>
    <form>
      <div>C</div>
    </form>
  </body>
</html>

が、なぜかa要素だけは& aと書かないと機能しなかった。

&の解釈が難しいかもしれないが、

.x {
  :hover { font-weight: bold; }
}

このルールは

.x *.hover { font-weight: bold; }

に等しい。

.x {
  & em { font-weight: bold; }
}

このルールは

.x em { font-weight: bold; }

に等しい。

.x {
  em & { font-weight: bold; }
}

このルールは

em .x { font-weight: bold; }

に等しい。

要は&は名前の置き換えのように機能する(実際の挙動は違う)。

とはいえ、はるらぼは実験的要素を許容しているから使っているだけで、2023年末にようやくサポートされたような機能を私がウェブで使うことは将来的にもほとんどないだろう。

だが、実はそのことを逆に利用することもできる。 例えば

.foo {
  display: none;
}

.highfeature {
  .foo {
    display: initial;
  }
}

のようにすると、入れ子セレクターを解釈しないブラウザではnoneに、解釈するブラウザではinitialになる。 これにより、新しい機能を利用可能な最新環境のユーザーに拡張された体験を提供するいう選別が可能になる。

従来だとprefers-color-schemeがGoogle Chrome 76, Firefox 67を要求するものとして使いやすかったが、2024年クラスの環境を選別する方法としてより使いやすい入れ子セレクターという手段が増えたわけだ。

/* Basic CSS Support */
.super_function, .hyper_function { display: none; }

/* Chrome >=76, Firefox >=67 */
@media only screen and ((prefers-color-scheme: light) or (prefers-color-scheme: dark)) {
  .super_function { display: initial; }
}

/* Chrome >=112, Firefox >=117 */
.hyper_funcion_container {
  .hyper_function { display: initial; }
}

意外なアクセシビリティ

ハイブリッドSPAにした効果もあり、NetsurfやDilloでも読むこと自体は可能。

CSSをどの程度解釈したとしてもMenu, About, Articlesは表示されるようになっていて、さらにメニューの中身は入れ子セレクターの中にあるため高度なCSS解釈ができるブラウザ以外はメニュー自体も表示される。

そこから記事リストにアクセスでき、記事リストから記事にアクセスできる。