Skip to content

CSS circular year calendar — Orbit example

A circular year calendar shows all twelve months on a single dial — useful as the centerpiece of a personal-planner app, a year-in-review dashboard, or a homepage hero that doubles as a real, glanceable date display. The metaphor is honest: a year is a cycle, and a circle expresses that more naturally than a 12-grid of rectangles.

This example layers four concentric rings (days, months, seasons, and event markers) around a central year label. The day numbers (1–30) ride on the outer ring as <o-arc> curved-text elements, the months and seasons sit on inner rings, and from-X rotations align each ring so the labels land exactly between the tick marks.

How it works

  • useRange(1, 30) plus .map generates the 30 day-tick vector elements and the 30 day-number <o-arc> elements without writing them by hand. In a vanilla project, generate them with a server template or a one-off for loop.
  • <o-arc> for the month and season labels is what lets the text follow the curve of its ring. Because each <o-arc> is auto-distributed around its parent orbit-N, you only need to drop the labels in order — Orbit handles the angles.
  • flip on the bottom-half labels (Apr–Sep, Summer) keeps them readable. Without flip, “Jun” on the bottom of the dial would render upside-down to anyone reading the calendar.
  • from-X ring offsets (from-355, from-15, from-6) nudge each ring by a few degrees so the day ticks line up with the month boundaries instead of straddling them.
  • Two vector markers with angle-60 and angle-222 highlight the current month and current day. Update them from JavaScript to make the calendar live.

Customization

Make it live by computing the angles from Date.now():

const now = new Date();
const monthAngle = now.getMonth() * 30; // 12 months × 30°
const dayAngle = (now.getDate() - 1) * 12; // ~30 days × 12°
document.querySelector('.month').style.setProperty('--o-angle-composite', monthAngle + 'deg');
document.querySelector('.day').style.setProperty('--o-angle-composite', dayAngle + 'deg');

Color the seasons to make the dial readable at a glance:

.orbit-4 o-arc:nth-child(1) { --o-fill: #c8e6f5; } /* Winter */
.orbit-4 o-arc:nth-child(2) { --o-fill: #d4f5c8; } /* Spring */
.orbit-4 o-arc:nth-child(3) { --o-fill: #f5e8c8; } /* Summer */
.orbit-4 o-arc:nth-child(4) { --o-fill: #f5d4c8; } /* Autumn */

Switch the language of the month labels — replace “Jan, Feb, Mar…” with the equivalents in your locale. The layout is language-agnostic.

Use this in your project

Install Orbit CSS and the markup above gives you the entire calendar dial.