| @ブログ

ダークモードとライトモードの切り替えをグローバルナビゲーション(上の方の半透明の白い領域)から簡単に行えるようにした(これまではこのサイトについてのページで切り替える必要があった)。デフォルトは OS の設定準拠で、 OS がダークモードであればダークモードに、ライトモードであればライトモードで表示される。手動でテーマを切り替えると Cookie に設定値を保存する。機能と見た目は MDN Web Docs を参考にした。

CSS がぐちゃぐちゃなのでかなり難儀した( 3 年前にも同じことを書いている)。ダークモードとライトモードの切り替えなんて自分しかしていないと思うがかなり満足度の高い休日プログラミングだった。

| @技術/プログラミング

外出自粛かつ自分自身にコロ助疑惑があったのでずっと部屋に閉じこもってサイトのデザインをいじってた。

2020-04-07 デザインアップデート.jpg

上がこれまでで、左下が今後、右下が画像がワイドな場合のバージョン。これまで横幅 1280px 想定にして画像も横幅 1280px のサイズで表示するようにしていた。

ただしこれだと写真は見やすくても文字が読みにくい。人間の目は 1280px 繰り返し左右に移動させるのには適していないようだった( N=1 )。

そういうわけでまたまた cho45 さんのブログのレイアウトをパクって、文章は横幅短めに、写真はでかいサイズで表示するようにした。

文章部分の幅を 800px にして、画像を読み込んだときに一定の条件にマッチしたら写真の横幅を 1280px で表示するようにした。 margin-left: -250px; してるのがミソ。横幅 800px だと大分文章は読みやすいし、でかい写真は大きく見えて便利。

@media screen and (min-width: 1422px) {
  #content #main article .body > p {
    img[class~="large"] {
      width: $content-max-width;
      max-width: $content-max-width;
      margin-left: -250px;
    }
  }
}

JS はこんな感じのコードを書いた。

const checkImageSize = (target) => {
  if (typeof target === 'undefined') {
    return;
  }
  const width = target.naturalWidth;
  const height = target.naturalHeight;
  const isPhoto = RegExp('(lh3\.googleusercontent\.com|\.jpe?g$)').test(target.src);
  if (width > 1279 && width > height && isPhoto) {
    target.classList.add('large');
  }
}

const lazyImageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const lazyImage = entry.target;
      lazyImage.src = lazyImage.dataset.src;
      lazyImage.addEventListener('load', (event) => checkImageSize(event.target));
      lazyImageObserver.unobserve(lazyImage);
    }
  });
});

const selectors = "#content article .body img, #content article .similar img";
const lazyImages = [].slice.call(document.querySelectorAll(selectors));

if ("IntersectionObserver" in window) {
  for (let image of lazyImages) {
    const src = image.src;
    image.dataset.src = src;
    if (lazyImages.indexOf(image) === 0) {
      const promise = new Promise(resolve => resolve(image));
      promise.then(checkImageSize).catch(setTimeout(checkImageSize, 100))
      continue;
    }
    image.src = "";
  };

  lazyImages.forEach(lazyImage => {
    lazyImageObserver.observe(lazyImage);
  });
}

画像の読み込みが起こったタイミングにフックして画像のサイズチェックを行い、条件にマッチしたら img タグに class を追加し、 CSS のメディアクエリと合わせ技で大きく表示するようにしている。以前やった画像の遅延読み込みのコードを改良した。

その他、 HTML 5 対応が中途半端だったのでマークアップを見直して、適度に <header><secition><article><footer> を使ってマークアップし直した。 このせいだと思うけど Google Adsense の自動広告が差し込まれる位置が変わった。これまでヘッダーの下に入り込んでいたやつが <header><article> の間に入るようになった。

今後も外出禁止が続いたらひたすら自分のブログをいじってしまう予感。こういうエネルギーを仕事とか個人サービス開発とかに当てられるとよいのだろうけどなかなかそういう方向には向かない。

| @ブログ

dark-mode-and-light-mode.jpg

このサイトのデザインは黒地に白文字でダークモードという概念が出てくる前からダークモードだった。家のパソコンで見るときには見づらいと思うことはなかったが、外出先で日中にスマートフォンでサイトを見ると黒地に濃い赤色のリンクは見づらいなぁと思うことがあった。最近、 Mac や iPhone でダークモードの概念が浸透してきたので、自分のサイトの場合は昼間用のライトモード対応を行うことにした。こういう感じのメディアクエリを書けばよい。

@media (prefers-color-scheme: light) {
  ...
}

ただ、元々の CSS が結構ぐちゃぐちゃな書き方で難儀した。ダークモードとライトモードの切り替えが行えるサイトは必然的に CSS のメンテナンス性が高い状態だと思う。明暗を反転したときにこの色はこうなる、という対応関係が綺麗に提示できるような状態は、利用するカラーをシステマティックに整理できているということだと思う。 Sass で書いてあるなら利用する色はきちんと変数化されていて適切な命名がなされているとかそういう状態だと思う。 CSS の設計健康診断としてダークモード・ライトモード切り替え対応をやってみるのは結構いいと思う。

| @ブログ

background-image random pick up

写真や画像があると文章だけよりも記憶に定着しやすいらしい。なのでヘッダー画像のバリエーションを増やしてみることにした。これまで自分が撮ってきた写真の中で気に入ってるもの、ヘッダー画像にちょうど良さそうなやつを 14 枚選んで、これまで表示していた門司の「平民食堂」のやつと合わせて 15 枚をランダムピックアップして表示させている。本当ならランダムではなく記事ごとにイメージに合う画像を選定すべきなのだろうけどそこまではやってない。「このブログ主はこういう写真の場所に行ったりこういう写真をいいと思ってるんだな」ということが伝われば良いと思ってる。

ランダム表示に関して、最初は JavaScript で CSS の background-image を書き換える方式でやっていたが、 DOM の読み込み完了時に動く都合上、かくつきが出てしまう。それで画像の選定は Ruby で行って data-attribute として指定し、予め CSS にそれぞれの data-attribute に応じた background-image を書いておくことにした。これでかくつきはほぼほぼなくなった。

<div id="header" data-background-image="heimin">
</div>
#header[data-background-image="heimin"] {
  background-image: url(header-bg-heimin.jpg);
}

このほかにも CSS 周りを結構いじってて、パンくずリストが載る部分は文字が読みにくくなるのでグラデーションでシャドーを掛けてる。これまで float でやってた配置の制御を flexbox に置き換えたりもやった。今のデザインのベースは 10 年前に作ったので、当時はグラデーションなんかは CSS で実現できず横幅 1px の画像をリピートする方法を使ったが、これで画像を捨てて全て CSS で実現できそう。良い世の中になってきてる。