Days Elapsed
一年を「面」で見る
一年は365日。数字で見ると多いけど、並べてみると案外少ない。
12ヶ月を並べて、過去を塗りつぶして、今日を光らせる。それだけのカレンダーを作った。進捗バーが「一次元」なら、これは「二次元」の進捗表示。

これは一年の進捗バー
なぜ作ったか
進捗バーでは「どれだけ進んだか」はわかる。でも「今どこにいるか」は見えない。
- 空間で把握したかった ── 線ではなく面で。12月がどこにあるか、今が年のどのあたりか、一目でわかるようにしたかった。
- 月の重みを感じたかった ── 1月も12月も同じ1マス。でも並べると、過ぎた月と残った月の差が目に見える。
- GitHubの草が好きだった ── あの「塗りつぶされていく感覚」を、一年単位で味わいたかった。
設計の話
レスポンシブは妥協しない
最初は GitHub 風の「7行 × 53列」で作った。見た目は良かったけど、スマホで横スクロールが発生して使い物にならなかった。
解決策は「月ベースのレイアウト」。12ヶ月を並べて、画面幅に応じて 4列 → 3列 → 2列 と自動で切り替わる。Container Queries を使って、親要素の幅に反応するようにした。
@container (min-width: 600px) {
.year { grid-template-columns: repeat(4, 1fr); }
}
@container (min-width: 400px) and (max-width: 599px) {
.year { grid-template-columns: repeat(3, 1fr); }
}
どの画面でも横スクロールは発生しない。
日付変更を正確に検知する
0時0分0秒に「今日」が移動する。そのタイミングを逃さないために、次の0時までの残り時間を計算して setTimeout をセットしている。
タブがバックグラウンドにあると JavaScript のタイマーは信用できないから、visibilitychange で復帰時に再チェックする仕組みも入れた。
色はすべて oklch()
従来の hsl() は、同じ彩度・明度でも色相によって見た目の明るさが変わる。oklch() は知覚的に均一な色空間で、hue を変えるだけで一貫した見た目が保てる。
--_past: oklch(55% 0.15 var(--_hue));
--_today: oklch(65% 0.22 var(--_hue));
緑でも青でも紫でも、--calendar-hue を変えるだけでテーマが変わる。
アクセシビリティ
派手なアニメーションは人を選ぶ。
prefers-reduced-motion── アニメーションを無効化。今日のセルも静的な強調に切り替わる。forced-colors── Windows のハイコントラストモードでも、過去・今日・未来が区別できるようにした。aria-label── スクリーンリーダーには「2025: 364/365 (99.7%)」のように進捗を読み上げる。
見た目だけじゃなく、見えない人にも情報が届くようにした。
年末に眺める
今、このカレンダーを見ると、ほとんど塗りつぶされている。残っているのは数日だけ。
来年の1月1日になったら、すべてが空になる。そしてまた少しずつ埋まっていく。
その繰り返し。
