Closed25

Svelteでデジタル時計コンポーネントを作る

yamanokuyamanoku

モチベーション

秒で見えるデジタル時計が部屋にほしかった。
が、探しても秒が見えないものや、個人的に良さげだと思う時計が自分の中では見当たらなかった。

そんな中、以前 miyaoka さんが一週間の日付だけを見れるカレンダーをつくっていたのをふと思い出した。

https://twitter.com/miyaoka/status/1330871730215161856

そこで、自作してPWAとしてデジタル時計のようにすぐ見れるようにしてみてもいいのではないかとひらめき今に至る。

https://twitter.com/yamanoku/status/1378240444946022403

yamanokuyamanoku

なぜ PWA にしたかったかというと、アドレスバーなどが画面上では不要な情報だったのでそれを除去したかったから

yamanokuyamanoku

技術選定

アイデアとして秒で作れそうだったので index.html と VanillaJS でも良いんじゃないかと思ったけど
せっかくなので最近気になってる Svelte を使用してみることに

https://svelte.dev/

yamanokuyamanoku

CLI

npx degit sveltejs/template oclock
cd oclock

でサクッとできる。

yamanokuyamanoku

カウントできるようにする

Svelte は Vue.js を使ってる人なら馴染みがあるような気がするが、使用する形があまりにもシンプルすぎて心配になる(なった)。

  import { onMount } from "svelte";
  let time = new Date();

  // ゼロパディングするために文字列にして padStart で2桁にしている
  $: hours = String(time.getHours()).padStart(2, "0"); // 時間
  $: minutes = String(time.getMinutes()).padStart(2, "0"); // 分
  $: seconds = String(time.getSeconds()).padStart(2, "0");  // 秒

  // マウントして1秒毎にカウントしてくれるようにする
  onMount(() => {
    const interval = setInterval(() => {
      time = new Date();
    }, 1000);
    return () => {
      clearInterval(interval);
    };
  });

こうすることで以下のような変数を HTML 上で使用することができる

{hours} {minutes} {seconds}
yamanokuyamanoku

マークアップ、WAI-ARIA

今回は下記のような形でマークアップすることにした。

<div
  id="visually-hidden-oclock"
  class="visually-hidden-oclock"
  role="timer"
  aria-live="polite"
  aria-atomic="true"
>
  {hours}:{minutes}
</div>
<div id="visually-oclock" class="visually-oclock" aria-hidden="true">
  {hours}:{minutes}:{seconds}
</div>
yamanokuyamanoku

今回カウントするにあたり、秒までを含めたかったが
スクリーンリーダーで読み上げる際に1秒毎に正確に読み上げることができず、かつ毎回読み上げるごとに煩わしさもあったので、読み上げる部分は時間と分だけにすることにした。

{hours}:{minutes}

また、視覚的に見えるカウントについては支援技術側に伝わらないように aria-hidden="true" で隠蔽するようにした。

<div id="visually-oclock" class="visually-oclock" aria-hidden="true">
  {hours}:{minutes}:{seconds}
</div>
yamanokuyamanoku

余談

role="timer" を調べているときに W3C の WAI-ARIA Issue にて
aria-interval(存在しない WAI-ARIA)を設定している場合、その間隔ごとに aria-live の API に通知が行って反応できるようにするのはどうか、といったアイデアが投稿されていた。

<span role="timer" aria-interval="60">Time Left in this session 5:00</span>

これは以下のような文字入力の場合に何文字入力されたかを検知する際にも便利そうである。

<span aria-live="true" aria-interval="50"> 250 characters left</span>

https://github.com/w3c/aria/issues/821#issuecomment-428629018

yamanokuyamanoku

CSS

天地中央寄せ

display: flex;
justify-content: center;
align-items: center;
height: 100%;

数字の等幅対応

数字を同じ大きさにする表記を有効にする設定

font-variant-numeric: tabular-nums;

その他の仕様や説明は MDN を参照

ダークモード設定

:root 設定と prefers-color-scheme で Light の時と Dark の時とでカラーが切り替わるように設定。

@media (prefers-color-scheme: light) {
  :root {
    --theme-base: #d2d2d2;
    --theme-font: #15202b;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --theme-base: #15202b;
    --theme-font: #d2d2d2;
  }
}
  background-color: var(--theme-base);
  color: var(--theme-font);

Visually-hidden

支援技術のみに伝わるようにHTMLを隠蔽する方法

.visually-hidden {
  position: fixed;
  top: 0px;
  left: 0px;
  width: 4px;
  height: 4px;
  opacity: 0;
  overflow: hidden;
  border: none;
  margin: 0;
  padding: 0;
  display: block;
  visibility: visible;
}

色々なスタイルが存在しているが、 amp-html で定義されているものが iOS の VoiceOver や Android の Talkback といったスクリーンリーダーへの考慮も含んでいるので採用。

yamanokuyamanoku

PWA化

Svelte 側で設定する必要があるかなと思っていたが、
ポートフォリオサイトでもある yamanoku.net は PWA 設定が root でされておりGitHub Pages として公開している。
新たにリポジトリを作って 同様に GitHub Pages の設定をすると https://yamanoku.github.io/oclock/ という形で公開される。
https://yamanoku.github.io/https://yamanoku.net/ に転送される仕組みになっているので、自動的にPWAとして扱えるようになっていた。ラッキー

yamanokuyamanoku

Web Components(Custom Elements)

特にやる必要もないといえばないが、簡単にできるようなので、Web Components にしてみようと思って対応。

必要な設定は以下の通り

Rollup.config.js

customElement の設定を追加。

rollup.config.js
svelte({
  compilerOptions: {
    customElement: true, // 追加
   }
}),

Svelteファイル

tagの名前を定義。一番上に設定。

Svelte
<svelte:options tag="oclock-component" />

設置するHTML

今回の場合は ./public/index.html に Svelte ファイルで設定した tag の名前を Web Components として設置。

<oclock-component></oclock-component>
yamanokuyamanoku

Web Components 対応にあたり、global.css で設定されていた visually-hidden が適応されずに焦った…
そのため Svelte 側にも同様の visually-hidden スタイルを適応。

yamanokuyamanoku

所感

Svelte は個人的にやりたいと思っていたことや、簡単なアイデアを実現するにベストなフレームワークに思える。今回それが要件を満たしていたのでよかった。
アイデア自体は以前浮かんだものだったが、いろいろな実装を混みで半日かからずで出来たことも地味に嬉しい。

より簡易的に作るのであれば以前触った FicusJS が結構良さそうな感じかもしれない。
https://zenn.dev/yamanoku/scraps/95d130be273b17

WAI-ARIA については以前作っていた誕生日までのカウントダウンページが同様の対応ができそうだと思った。
role="timer" 自体も今回調べてみて出会えたのでよかったが、よりアクセシビリティを考慮できたカウントをつくれないか引き続き考えていきたい。

このスクラップは2021/04/25にクローズされました