Closed
24

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

モチベーション

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

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

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

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

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

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

技術選定

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

https://svelte.dev/

CLI

npx degit sveltejs/template oclock
cd oclock

でサクッとできる。

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

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}

マークアップ、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>

role="timer" は 開始時点からの経過時間を示す、または終了時点までの残り時間を示す数値カウンターとして支援技術に伝えるための WAI-ARIA である。

参照ドキュメント

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

{hours}:{minutes}

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

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

余談

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

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 といったスクリーンリーダーへの考慮も含んでいるので採用。

PWA化

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

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>

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

所感

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

より簡易的に作るのであれば以前触った FicusJS が結構良さそうな感じかもしれない。

https://zenn.dev/yamanoku/scraps/95d130be273b17

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

デジタル時計で使われそうなフォントを探してるけど、等幅対応きいとらんけってなってた

https://fonts.google.com/specimen/Orbitron

Web フォントを Orbitron にして時間カウントしている様子

読み込み自体は ?text=0123456789: と指定できるから最小読み込みで済みそうなんだけど肝心のフォントが見当たらんな

このスクラップは13日前にクローズされました
ログインするとコメントできます