Chienomi

新しいウェブサイトを続々リリース

開発::web

Mimir Yokohamaのウェブサイトの全面リニューアルを3月から進行しているが、その中で色々な知見を蓄積することができ、それを活かしてHarukamy’s ChatshowRadio Harukamy 公式サイトをリリースした。

Mimir Yokohamaのほうはその開発中の様子を動画として公開している。 Mimir Yokohamaは本文も含む大きな改変が必要であるため、リリースの目処は立っていない。

今回はこれらのサイトで活用した技術、考察を深めた結果としての意見を述べていこう。

ウェブフォント

Mimir Yokohama及びHarukamy’s Chatshowではウェブフォントを採用した。 もともとHarukamy’s Memorandaにはタイトル部分に採用されていたが、今回は本文フォントとして採用した形である。

前提として、ウェブフォントは「フォントを埋め込んだほうが、いくつもロゴ画像を読み込むよりも軽い」という考え方に立っているものである。 しかしそれは大きくても100kB程度の欧文フォントの話であり、だいたい4MBあたりが標準になる和文フォントでは事情が大きく違う。

1サイトあたり4MBというのは、日本のウェブサイトだと当たり前にあるが、極度に重いと言ってよく、迷惑な類になる。 スマートフォン通信量に対するアクセス負担と見られるページ数を数えてみればいいし、常時512kbps SIMの人や、1GB SIMの人のことを考えてみればいい。

だが、私はずっと考えていた。

「和文の本文は、かな/カナだけで良いのでは?」

一般に本文フォントの範疇であれば、欧文フォントはそこまで大きく印象を変えるものというのは多くない。 slab体を使ったり、Railwayのような特徴的なものを使えば印象は変わるが、どちらかといえば欧文ウェブフォントはロゴやタイトルなどに向いたものだと考えられる。

一方、和文の場合、漢字グリフの重要度というのは割と低い。 なぜならば、PCの場合だと漢字を十分に表示できるサイズ(物理ピクセルで理想的には48px、少なくとも24px)で表示している人は非常に少なく、ほとんどの場合漢字がギリギリ認識できるサイズにすぎない。それではデザインを反映する余地にかなり乏しい状態である。 だいたい現在では16px scale1.5か16px scale2あたりで、24pxか32pxあたりが多い。デスクトップだともっと少ない可能性が高い。デザインを表現するには、少々足りない。

また、それだけスペースがきつきつだとデザインを反映する余地が少ないし、そうでなくとも手間がかかるから漢字のデザインに凝ったフォント自体少ない。 派生フォントだと漢字グリフに関してはそのまま使うことが圧倒的に多い。

だが、本文において特にひらがなのグリフが変わることは、本文に非常に大きな影響を与える。

実際に試してみよう。

源ノ角ゴシック
JKゴシックM + 源ノ角ゴシック
源ノ明朝
クレーOne + 源瑛こぶり明朝

JKゴシックはかな/カナのみ、クレーはそれに加えASCIIが使われている。 だが、それ以外の文字となじまないというほどではないし、それでいながらしっかりと意図を反映することができているのではないだろうか。

サイズ的にはWOFF2でJKゴシックは14kB、クレーは33kBと十分許容できるサイズとなっている。

こうしたことから、ひらがなグリフに限定したウェブフォント、かなり可能性を感じる。 商業的なウェブサイトであればむしろ余計な要素である可能性が高いが、雰囲気が大事になるようなサイトや、本文の長いサイトでは使いどころは多いだろう。

むしろ問題は、「ひらがなウェブフォントとしての利用可能性」のほうだ。

和文フォントでまともなライセンスが設定されていることはほとんどなく、日本のクリエイターが契約や著作権に対して無頓着であるという問題がここでも発揮されてしまっている。 OFLのフォントをベースとして利用しているにも関わらず、独占的ライセンスで公開しているフォントも珍しくない。 (OFLはコピーレフトライセンスである。)

商業フォントに関しても、ウェブフォントとしてまともに利用できる契約条件になっているものはほとんどなく、ましてサブセットWOFF2を生成できるような契約はほぼ見かけない。そのため、そもそも選択肢として挙げられるフォントが極端に少ないのだ。

クレーOneはフォントワークスが作ったフォントだが、GitHubで公開されており、OFLによって提供されている。

JKゴシックは少し微妙だ。JKゴシック自体はプロプライエタリライセンスだが、「自由に使って良い」と明記されている。 だがさらに問題があり、漢字部分はM+だと言うのだが、OSDN版M+は既に開発終了している。一方、GitHub版はOFLで、どちらをベースにしたかについて記載がなく、GitHub版だとJKゴシックはOFLを継承する義務がある。 だが、この場合「自由に使って良い」という明言のなされたものであるため、「OFLであるべきではないか?」いう疑義を挟む必要性が、少なくとも私にはない。文面通り受け取れば、WOFF2サブセットを生成してウェブフォントとすることに問題はないからだ。

源ノ角ゴシックや源ノ明朝、GitHub版M+をベースにしたフォントに関しては、OFLを継承しなければならないため、これらのフォントはOFLであることが期待される。 実際、ある程度の割合でOFLになっているため選択の余地がある。 一方、IPAフォントはコピーレフトライセンスではないため、IPAフォント派生のものに関しては利用できないものが多い。

これらのフォントをベースにしたものに関してはある程度期待できるが、それ以外についてはほぼ無理だと思ったほうが良い。 大部分は事実上、どのような用途にも利用できない形態の利用許諾である。

なお、OFLのフォントをウェブフォントとして利用する以上、OFLに従った処理が必要になる。 Chatshowではフォントについてのページに記載がある。

メニューのエクスパンション

新しいMimirのウェブサイトはスライドインする文書情報とエクスパンションとなるメニュー、Chatshowにはスライドインするメニューが採用されている。

基本的な考え方としては、メニューは親クラスによって表示/非表示状態の2つの適用状態があり、非表示状態では縦に出るものはheight 0、横に出るものはwidth 0となっている。そして、transitionを設定することで両者をアニメーションを伴って切り替える。

この切り替えはCSS単体でも可能である(checkboxを利用する)が、この機能よりもJavaScriptのほうが動作可能性が高いため、以前はMimirではCSSのみだったが、今回は素直にJavaScriptを使っている。なお、相変わらずvanillaである。

だが、問題はwidth 0のほうだ。

基本的に、overflow: hidden;であれば、無条件にheight: 0pxは実現できる。 ところが、width: 0px;はコンテンツの縮小限界よりも狭くはならない、という問題がある。 max-widthを伴う方法もあるが、場合によってはこれも思ったようにならない。

この解決方法として「レイアウト負値で外に出す」というのがある。

これらのメニューは固定でスライドインされることからもわかるように、position: fixed;になっている。 そのためレイアウトプロパティがあるのだが、これをright: -5vwとしている。

するとどうなるか。縮小限界のコンテンツ幅は5vwよりも大きいのだが、画面外にレイアウトされることによって内包ブロックは自身の最小幅を保とうとしなくなるため、widthはちゃんと0になる。 だから、別にright: -0.1vwでもこのブロックは消える。

なお、スライドインされると困る場合はmax-widthでコントロールすることを考えるのが良いだろう。

Chatshowのおんなのこ

この女の子についての情報はChatshowに記載がある

この子はPCモードの場合、意図的にボックスにかぶるように配置される。 だが、これが結構難しい。

ボックスにかぶるようにボックスを配置する単純な方法としてはこんな感じ。

<html>
  <body>
     <div style="width: 800px; margin: auto">
       <div style="width: 100%; background-color: green; height: 1200px;"></div>
       <div style="position: relative; width: 250px; height: 650px; background-color: black; bottom: 650px; left: 700px;"></div>
     </div>
  </body>
</html>

だが、常に固定で画面上でボックスにかぶった状態で配置したいので、position: fixedがほしい。 だが、fixedにするとメインボックスに対する相対的な座標を指定する方法がない。 帯状にボックスを配置して、その中にメインボックスよりも大きいボックスをおいて右端に配置、という方法もあるが、これをするとこの帯がクリックを奪ってしまう。

このため、

function setimg() {
  const mbr = mb.getBoundingClientRect()
  ib.style.right = (mbr.right - 800 - 150) + "px"
}

という形のonResizeイベントハンドラを登録してJavaScriptで移動させている。

なお、当初このスクリプトファイルの中でsetimg()することで初期位置を設定しようとしていたのだが、ブラウザバックやリロードなど、一部の条件で配置がおかしくなることがあった。 そのため、window.onloadイベントハンドラとしても登録しており、スクリプト中では逆に

ib.style.right = "0px"

として女の子を毎回移動させるようにした。 なんとなく、「君をみつけたら駆け寄ってくる」って感じ。

女の子のエモート

女の子は表情が5種類あり、記事によって異なる表情を見せる。

これは、PureBuilder Simplyの機能、というかPandocの機能を使っており、YAML front matterで

---
title: foobar
emote: wonder
---

とか書くようにした。 これによってemoteの値を親要素のクラスに反映して、女の子の表情を変更するようにしたのだ。

ちなみに、ドキュメント中の要素で分岐するパターンが存在する場合、YAML front matterで使い分ける、というのはPureBuilder Simplyの流儀である。

なお、「わざわざemote書くのかよ」と思う人も安心してほしい。 PureBuilder Simplyはblessという機能によって、これらのメタ変数をドキュメント処理時に操作できる。 だから、別にAIによって文章から感情を判定してemote変数をセットするようにしても構わない。私はやらないが。

Chatshowのゆらめく星

単純にCSS animation.

#MenuStar {
  animation: rstar 3s linear infinite alternate;
  font-size: 36px;
  cursor: pointer;
}

@keyframes rstar {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(30deg); }
}

なお、★はGIMPでテクスチャを貼っている。

ACCSカード

Chatshowのカード形式のACCSメニューはPiecesで既に採用されている

シンプルなflexの利用例となる。

ちなみに、レイアウトはカテゴリもflexアイテムになるかどうかという点で、ChatshowとPiecesは少しだけ違う。

Mimirのメニュータブ

Mimirのメニュータブはコンパクトに書くため工夫を凝らした。

まず、基本的に親クラスによって全体が制御される構造だ。

.menucontent {
  display: none;
}

.tabact_service #MenuContentService,
.tabact_article #MenuContentArticle,
.tabact_special #MenuContentSpecial,
.tabact_contact #MenuContentContact,
.tabact_info #MenuContentInfo {
  display: block;
}

この親クラス設定はJavaScriptのonclickハンドラだが、個別に設定しているわけではない。

menu_tab.addEventListener("click", function(e) {
  var idname = e.target.id
  var typename = idname.replace("MenuTab", "")
  menu_ctl.className = "tabact_" + typename.toLocaleLowerCase()
  e.stopPropagation()
})

この理解にはDOM Level2 イベントへの理解が必要だ。 DOM Level2イベントはまず伝播する性質があるかどうか、という概念がある。 そして伝播する性質を持つ場合、伝播を(Event.stopPropagation()によって)停止させない限り伝播していく。 イベントの発生順序は、先に追加されたイベントが優先だ。

menu_tabオブジェクトは個々のメニュータブではなく、メニュータブを格納するコンテナである。 メニュータブにはイベントハンドラはないが、そこで発生したclickイベントが伝播し、一番最初のハンドラとしてメニュータブコンテナのイベントハンドラに捕捉される。 これによって親要素のイベントハンドラがイベントをキャプチャできるのは理解できると思うが、もうひとつ重要な点が、Event.targetはハンドラが設定された要素ではなく、イベントを発生させた要素を指すということである。

つまり、このe.targetはメニュータブコンテナ要素ではなくメニュータブ要素になる。 そのため、「どのメニュータブをクリックしたのか」をe.target.idで知ることができる。

さらにIDからプレフィックスを外すことでタブの名前を得る。そして、その名前を親要素のクラス名に反映する。

イベントハンドラの使い方と命名の妙によって、かなりコンパクトに実現することができた。

なお、メニュータブはflexで、スマートフォンモードでは不足しがちな幅を活用するためflex-basisプロパティが外されている。

メニューの格納

bodyやdocumentでclickを拾う、という技法は覚えておくといい。

// Touch to close event
document.body.addEventListener("click", function() { closeInfo(); closeMenu() });

ボタンを押したときのclickイベントで閉じられたら操作できないので、それらのイベントは伝播を止める。

// Menu Button event
document.getElementById("MenuTglBtn").addEventListener("click", function(e) { toggleMenu(); e.stopPropagation() })
document.getElementById("InfoNested").addEventListener("click", function(e) { openInfo(); e.stopPropagation() })

毎回stopPropagationにカッコをつけ忘れて「動かない」ってなるの私だけだろうか。

なお、前述の通り先にセットされたイベントが先に発生するので、touch to closeのイベントは最後に追加するようにしないと動かない。

気持ち悪いセミコロン

Mimirのほうのコードにはこんなのがある。

;
(function() {

このセミコロンなんだ? と思うかもしれない。

基本的にJavaScriptは行末がstatement tarminatorになるため、セミコロンは必要ない。

ところが、JavaScriptはこのターミネート力が非常に弱い。 関数オブジェクトをカッコで囲って匿名化し、即座に呼び出すのは昔からあるJavaScript手法だが、セミコロンなしだと直前の表現にカッコがついているものとみなされてしまう。

だから、あまり知られてないけど、JavaScriptは

alert ("FOO")

みたいに余計なスペースを入れても有効だったりするし、

alert ("FOO"  .toLowerCase())

なんてこともできる。

なので、この匿名関数を使う手法を使う場合、もっと明示的なstatement terminatorが必要となる。 このようにJavaScriptはホワイトスペースを全面的に無視する仕様なので、そもそもセミコロンをつけないポリシーだが、入れざるをえないのでより明示的にするために

;

という行を置いたのだ。

なお、現代のJavaScriptではより扱いやすいスコープがあるため、そもそも即時呼び出しの匿名関数の必要性に乏しい。 ただ、let, constがES6(2015)の機能であることを踏まえて、Mimir Yokohamaでは引き続きオールドスクールなコードとなっている。 一方、Chatshowは「動かなければそれでもいい」という考え方がより強いため、constを使うコードになっている。

const mb = document.getElementById('MainBox')
const ib = document.getElementById('ImageBox')
const mm = document.getElementById('MenuMenu')

ただ、このコードはconstキーワードを使うメリットがなく、全体を匿名関数で囲むことで名前空間を汚さないで済むことから、この記事がアップロードされる前に修正されるだろう。アクセシビリティは大事だ。

ちなみに、普段は20年前からタイムスリップしてきたようなJavaScriptを書いている私だが、 仕事で私が書いたコンポーネントはサーバーはNode16を使い、フロントもクラスベースでReactを使うものなので、そっちでは非常にモダンなコードを書いていたりする。

なお、ここで話したことを利用すると、JavaScriptコード中に

;;;;;null;;;;;

とか書ける。

;

の評価はundefinedなのだから、;;;null;;;と書いてもundefinedになりそうなものだが、Chromium, Firefoxともに空ステートメントは前ステートメントの戻り値を引き継ぐため、;;;null;;;を評価するとnullになる。 だが、これはstatement terminatorであり、(;;;null;;;)みたいなことはできないし、;;;"ABC;;;.toLowerCase()とかもできないので、この評価結果がどうなるか、というのは活用の余地はない。

また、全体を匿名関数で囲って名前空間を汚さないが、特定のオブジェクトを関数外にエクスポートしたいと考え、なおかつセミコロンは絶対使いたくない場合、

(function() {
  /* ... */
  menubox = anonElement
})
var menubox

という技がある。

JavaScriptのvar宣言は関数全体をスコープとするが、その宣言がどこでなされるかに関係なく関数全体で有効である。

匿名関数のカッコが「後ろに来る場合」メソッド呼び出しとみなされてしまってセミコロンが必要となるのだから、宣言を後ろにもってきて先頭をカッコにすれば良いのだ。 エクスポートする変数が後ろで宣言されていても、先頭に存在する匿名関数の中でその変数はちゃんと見えている。

だがこのコード、最高に気持ち悪い。

compatibility/accessibility policyとデザイン

メニューの配置

基本的には従来どおり、ELinksやw3m, DilloやNetscape Navigator4でも閲覧可能であり、古いバージョンのブラウザでもおよそ動作し、モダンなブラウザであればより多くの機能を使える、という点で変わりはない。

JavaScriptを拒否する環境においては、Chatshowは単純にトップに戻るナビゲーションによって補う方式。 意外と不便がないため、Memorandaでは標準としたものだ。

今回のサイトのひとつ前に書いたのが現行のMemornadaである。 新規描き起こしではなかったが、従来存在した「メニュー」を取り除いたというのが大きなトピックで、画面をフルに使い、記事のみによって構成されるというのがその時私の中で最新だった。 ページを軽くすることで、メニュー再選択はバナークリックでトップに戻るという方法としているが、これが快適だった。 考えてみればそもそもメニューを開くためにクリックイベントを起こしているわけで、メニューを開くのとコストが変わらないのであれば、別にトップに戻ってもあまり影響はない。

メニューを開いたがメニューナビゲーションを行わず閉じる、という場合も考えなくてはいけないが、やはりこれもクリックイベントが発生する。だったらブラウザバックしても基本的には変わらない。しかし、スマホの場合ブラウザバックが多くのブラウザで1タップになっていないので、このコストは少し上がる。 また、そもそもメニューによって今のコンテキストが覆い隠されてしまうのは少し体験が悪い。

総合的に考えれば、もちろん適切な設計がなされて上ではあるが、やはり「メニュー表示」がなされるほうが体験は良いと考えられる。 Memorandaは非常に軽く、コンテンツ量も多くないためトップに戻るデザインでも悪くなかったが、「リーダービューのようなreadabilityとナビゲーションUIの両立」はひとつのテーマである。

Memorandaのトップ画面
Memorandaの記事表示

Memorandaでメニューを外したのは、どのようにメニューを配置してもリーダービューほどのシンプルさにはならないためにメニューが邪魔だ、という理由が大きい。ここにメニューを配置するからには、いくつか考えるべきことがある。

サポートされる環境とcompatibility

まず、メニューを利用できることが善とするならば、その線引きを考える必要がある。 全体の候補としては

  • モダンJavaScript (モダン環境のみ動作する)
  • DOM レベル2 JavaScript (まともな環境なら動作する)
  • DOM レベル0 JavaScript (JavaScriptをサポートする可能な限り多くのブラウザで動作する)
  • CSS疑似要素 (CSS3のサポートが必要)

が考えられる。もちろん、ライブラリを使うなどというのは論外なので除外する。

DOM Level2 JavaScript(要はElement.addEventListener)とCSSの:checkedの利用は、どちらもIEならばIE9から動作する。 ただし、Firefoxなどは最初からどちらもサポートしているが、SafariはCSS3の対応は3.1から、Operaは9から(DOM Level2はOpera7から)なので、CSS3のほうが少しハードルが高い。 ちなみに、NetSurfとw3mだとどちらも動作しない。

DOM level0をサポートするのは少し危険だ。 もちろん、将来的な拡張で互いを破壊する可能性もあるが、それ以上にこれらのメニューエレメントなどはそこそこちゃんとしたCSSが動作することを前提に書かれている。 ここでいう「そこそこちゃんと」はMozilla 1.7, Firefox 1.0, Opera 8あたりのことを指しており、言い方を変えるならば「ちゃんと標準に準拠したブラウザである」ことを前提としている。 中途半端にCSSをサポートするブラウザはdisplay: noneだけを読み取ってくれたほうが良い。

このあたりは非常に微妙だが、もともとaddEventListenerをサポートするかどうかを「モダンブラウザ」の判定に使っているため、ここは変えずにいる。

だが、少しだけ「モダンブラウザ」の定義はハードルを上げている。 レイアウトにflexを使っているためだ。 flexはまだ規格化されていないもので、Firefox 20, Chrome 29から動作する。このために「中途半端にサポートされる」可能性は増した。

そのため、救済として

section, nav, aside, article {
  display: block;
}

が追加された。 これは未定義の要素はインライン要素として扱われるが、未定義の要素であってもどのように表示するべきかわかっているのであればそれに従うからだ。 XHTMLにおいてXML要素を含める、というか別の名前空間の要素を含めるとき、この方法で表示方法を指定する。

しかし残念ながらw3mやElinksやDilloは未定義要素のCSSを理解しないので、気休めなのだけど。 (といっても、w3m, ELinks, DilloにこのCSSは必要ない。) まぁ、IE7やAndroidブラウザなんかには効果があるはずだ。

基本的にはインライン要素とブロック要素で分類し、タグを全て除去してブロック要素を空行にしたときに正しく読めるものである必要があり、これを満たすならばどのようなブラウザでも基本的には問題ない。 ただし、メディアクエリなどを使う場合、適用のされ方によってはdisplay: noneなどによって操作不能な状態になっていないかを気にする必要がある。

幅800px

Chatshowは本体幅800pxを採用した。

私はしばらく「全幅表示」を採用してきたが、ここにきてレトロともいえる幅800pxへの回帰である。

主な理由は、「全幅表示にしていると、タブ表示ではタブサイクル時に表示領域が他のサイトと大きく変わるので目が回る」である。 Chienomiの場合、ソースコードの表示という事情があり、可能な限り幅をとったほうがユーザービリティに貢献するが、Chatshowは特にそういった理由がない。

Vivaldiのリーダービューはデフォルトでは中央850pxに表示する仕様であり、あえてレトロな800pxボックスにしてみた。 このことからもわかるように、基本的なデザインはスマホ指向であり、スマホと似たような「読む」体験をPCでも実現する方向性になっている。

「読む」ことに関してはスマホのほうが優れているだろう。本に近いからだ。 タブレットならもっと良いが。つまりは、このデザインの方向性としては「タブレット読み」である。

一定幅を下回る場合は全ビューポートに表示する。 このボーダーは、Chatshowでは後述するデザイン上の理由でだいぶ大きく、950pxを要求する。 1920の半分が960pxなので、ビューポートのデザインによってはタイルしてもPC表示だが、多分ギリギリである。 Vivaldiだとサイドメニューを表示してると下回る。

メニューの配置の仕方

メニューを配置する以上は、リーダービューの邪魔にならないようにする必要がある。

これまで私は「余計なものを表示しない」と考えてきたが、「普通、人は一点を見たままスクロールするので、目は動かない」という点を利用することにした。

Chatshowのメニューは常に右上にある。当然、邪魔するわけだが、Chatshowの場合メニューが動くので余計邪魔である。 しかし、そんなに気にならないはずだ。なぜなら、右上まで見てはいないから。

同じようにあえて読む部分を狭めるようなことにトライしたのがChatshowだ。 Chatshowでは女の子が表示されるが、この女の子はメインのボックスに かぶって表示される。

かぶる範囲はX方向は約100pxである。幅は800pxのボックスだから、1/8ほどが隠れる。 この部分は文字も隠れてしまうわけだが、ここで重要なのは「全くかぶっていない幅」である。 この画像は650pxあるため、1080pxフルのビューポートだとしても、なんと6割も隠れる。

実際はwrappingの関係で頭や脚のあたりは文字はほとんど隠れない。これは、そうなるように調整してある。 また、高さが最低800px必要としている。800pxだと上側13行ほどは頭にかかる分も含めて隠れない。なので、普通に読める。

メニューと違ってこれは常に目に入ってくるが、文字は普通に読めるため、雰囲気出しにちょうどいい。 Chatshowが読み物色が強く、全体を把握しようとすることはあまりないことも利用している。 技術文書のような、話の帰結を探したり、細かく読み返したりするものはこういうことをするとすごく邪魔になる。

SPビューでは女の子はメインボックスの背景に回る。 実はこれは女の子を表示しているボックス自体が別のものになっている。 さらに白がかっているのでかぶっている部分も普通に読めるようになっている。 スマートフォンだと読む領域がそもそも少なかったりするからで、実はこれでも割と邪魔である。 私はかなり大きなスマートフォンを使っているのでそんなに気にならないけど、古いスマートフォンで、しかもナビゲーションを表示しっぱなしにするようなブラウザとの組み合わせだときついだろう。

でも、Chatshowは雰囲気サイトだから。

Mimirは右上と右下固定のフロートである。 Mimirの場合、バナーがずっと残る形式に変わったので、右上の領域はメニューによって侵食されているわけではない。 実運用ではSPモードのバナーの高さは減らされるだろう。

右下固定は少し微妙だと私は思っている。 というのは、Firefox Focusはこの位置にナビゲーションを出してしまうのでかぶるのだ。 PCだとリアル右下ではなく、「右下、ボックスのわき」になるので問題ないのだが。

SPモードは異なるデザインで、右の中央とかに出すのが良いのだろう。

試したデザイン

どうやってるんだ、と思うかもしれないので、こんな感じ。

@media screen and (max-width: 899px) {
  #InfoBox {
    position: fixed;
    width: 100%;
    bottom: 50%;
    top: 50%;
    left: 0px;
    text-align: right;
  }

  #InfoNested {
      height: 70px;
      line-height: 70px;
      width: 20px;
      display: inline-block;
      background-color: #fff;
      border-radius: 80% 0 0 80%;
      border: 1px #666 solid;
      overflow: hidden;
      text-align: center;
      vertical-align: middle;
  }
}

動作上、これは適切ではないのだが、あくまでデザインテストとして書かれたものである。 「position: fixedで中央寄せしたい場合、left: 50%; right: 50%;あるいはtop: 50%; bottom: 50%;とすれば良い」 というのは覚えておくといいかもしれない。

Mimirのメニューは引き出したとき、それぞれ下と左が少し残るデザインで、両方出しても左下は残る。 これは現在のコンテキストが見えたほうがUXに寄与する、というのもあるが、それ以上に「tap to close」をわかりやすくするためだ。 MimirはChatshowと違いクローズボタンがないデザインとなっている。そして、画面中のどこをクリックしても閉じるのだが(メニュー内の操作をするものだけEvent.stopPropagation()してるので別)、それを直感的にわかりやすくするためのものだ。

ボックスシャドウ

box-shadowプロパティは私のサイトでは今回からの登場だ。 「カードデザイン」と呼ばれるものに近づけたかったのだが、Project Radio Harukamyのサイトでは少し違うペーパーライクなデザインにしている。

特にMimirのほうはメニューもシャドウがあるため、かなり立体感のある配置となっている。

800pxボックスをフラットに表示すると、本当にレトロなブログみたいにしょぼい感じになってしまうので、そのための工夫である。

コードテーマ

今回からDraculaテーマのCSSを作って適用している。

Chienomiは何だったか忘れてしまった。Dank NeonかBreeze Darkだったかな。 Draculaにしたのは、単純に私がずーっとVSCodeでDraculaを使っているから。 シンタックスサポートがしっかりしている上で、とても見やすいので。正確にはDracula Softを使ってるんだけど。

扱いを楽にするため、dracula.cssという別ファイルに分け、PureBuilder SimplyのBless機能を使って必要なときだけ読み込んでいる。 dracula.cssはウェブフォント設定を含むため、不必要に読みたくないのだ。

ちなみに、もともとMimirは3ファイルにCSSが分かれていた。これが1ファイルになったのは、リクエストをたくさん発行すると遅いから。 Chatshowは少し複雑な構造になったため、管理しやすいように分けたが、新しいMimirは引き続き1ファイルになる予定だ。 MimirではコーディングフォントにHermitを読むが、優先度がConsolasやMeslo、Roboto Monoより下になるため、あまり読まれないはず。

CSSトランジション

CSSトランジションの利用は、初めてではないけれど、久しぶりの復活。 昔Mimirのサイトはメニューボタンにトランジションを使っていたのと、Memorandaにメニューがあった頃はトランジションがあった。

このあたりも私は早く反応してくれたほうがいいのでトランジションなんかいらん派なのだけど、UX的にあったほうがわかりやすかったりするので、 特にMimirのメニュー構造を実現するためにトランジションが入った。

これ、かなり便利な機能だ。 昔はsetTimeout()でがんばっていたけど、それよりずっと軽いし、柔軟性も高い。

文書構造

Chienomiは一部の間違ったvalidatorに文句を言わせないために正しい書き方を無視したポリシーになっている。 Mimirのサイトも同様だが、今回この点を変更した。

  • 各セクショニングコンテンツはh1から開始する
  • ヘディング要素がないとしても適切なセクショニング要素を適用する