Svelteでデジタル時計コンポーネントを作る
成果物
スクリーンショット
prefers-color-scheme: light |
prefers-color-scheme: dark |
---|---|
ページ
モチベーション
秒で見えるデジタル時計が部屋にほしかった。
が、探しても秒が見えないものや、個人的に良さげだと思う時計が自分の中では見当たらなかった。
そんな中、以前 miyaoka さんが一週間の日付だけを見れるカレンダーをつくっていたのをふと思い出した。
そこで、自作してPWAとしてデジタル時計のようにすぐ見れるようにしてみてもいいのではないかとひらめき今に至る。
なぜ PWA にしたかったかというと、アドレスバーなどが画面上では不要な情報だったのでそれを除去したかったから
技術選定
アイデアとして秒で作れそうだったので index.html と VanillaJS でも良いんじゃないかと思ったけど
せっかくなので最近気になってる Svelte を使用してみることに
カウントできるようにする
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>
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 といったスクリーンリーダーへの考慮も含んでいるので採用。
色についてはポートフォリオサイトでも使用している自作 normalize.css から参照
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
の設定を追加。
svelte({
compilerOptions: {
customElement: true, // 追加
}
}),
Svelteファイル
tagの名前を定義。一番上に設定。
<svelte:options tag="oclock-component" />
設置するHTML
今回の場合は ./public/index.html
に Svelte ファイルで設定した tag の名前を Web Components として設置。
<oclock-component></oclock-component>
所感
Svelte は個人的にやりたいと思っていたことや、簡単なアイデアを実現するにベストなフレームワークに思える。今回それが要件を満たしていたのでよかった。
アイデア自体は以前浮かんだものだったが、いろいろな実装を混みで半日かからずで出来たことも地味に嬉しい。
より簡易的に作るのであれば以前触った FicusJS が結構良さそうな感じかもしれない。
WAI-ARIA については以前作っていた誕生日までのカウントダウンページが同様の対応ができそうだと思った。
role="timer"
自体も今回調べてみて出会えたのでよかったが、よりアクセシビリティを考慮できたカウントをつくれないか引き続き考えていきたい。
JavaScriptが無効のとき、なにも表示されなくなるので、注意書きを追加した