HeatmapChartWithYearOfDays

A headless wrapper for a year-of-days heatmap: one cell per day of one year, laid out in week columns and weekday rows, with cell colour encoding the value of each day. It specialises HeatmapChart (same shell, same data-table sibling) and adds the two facts the layout needs, year and weekStart. Use it for a per-day measure across a year, such as contributions, visits, appointments or readings per day.

Status: experimental — added 2026-10-22 from the chanhdai.com github-contributions review (chanhdai-lessons); implemented and unit-tested in all nine headless catalogs, not yet exercised in composed flows.

The component renders a <figure> holding a <div class="heatmap-chart-with-year-of-days-graphic" role="img" aria-label> around an inline <svg> that the consumer draws, names the figure with aria-label (from label), and lets the consumer reference a longer description or a real data <table> through aria-describedby. The component draws nothing and ships no scales, colours or animation; it exists to give every chart in the catalog the same accessible, stylable shell.

Example

Activity score per month in 2026
MonthScore
January75
February50
March32
April34
May51
June63
July77
August56
September34
October30
November48
December63
Show demo markup
<figure class="heatmap-chart-with-year-of-days" data-year="2026" data-week-start="1"><div class="heatmap-chart-with-year-of-days-graphic" role="img" aria-label="Activity per day in 2026, weeks starting on Monday"><svg viewBox="0 0 634 82" width="634" height="82" aria-hidden="true" focusable="false"><rect x="0" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="0" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="0" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="0" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="12" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="12" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="12" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="12" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="12" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="12" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="12" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="24" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="24" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="24" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="24" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="24" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="24" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="24" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="36" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="36" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="36" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="36" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="36" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="36" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="36" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="48" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="48" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="48" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="48" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="48" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="48" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="48" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="60" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="60" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="60" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="60" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="60" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="60" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="60" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="72" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="72" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="72" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="72" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="72" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="72" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="72" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="84" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="84" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="84" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="84" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="84" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="84" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="84" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="96" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="96" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="96" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="96" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="96" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="96" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="96" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="108" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="108" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="108" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="108" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="108" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="108" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="108" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="120" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="120" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="120" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="120" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="120" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="120" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="120" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="132" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="132" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="132" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="132" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="132" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="132" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="132" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="144" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="144" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="144" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="144" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="144" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="144" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="144" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="156" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="156" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="156" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="156" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="156" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="156" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="156" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="168" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="168" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="168" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="168" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="168" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="168" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="168" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="180" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="180" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="180" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="180" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="180" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="180" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="180" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="192" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="192" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="192" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="192" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="192" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="192" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="192" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="204" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="204" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="204" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="204" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="204" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="204" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="204" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="216" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="216" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="216" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="216" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="216" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="216" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="216" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="228" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="228" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="228" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="228" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="228" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="228" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="228" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="240" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="240" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="240" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="240" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="240" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="240" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="240" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="252" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="252" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="252" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="252" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="252" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="252" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="252" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="264" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="264" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="264" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="264" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="264" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="264" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="264" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="276" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="276" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="276" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="276" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="276" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="276" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="276" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="288" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="288" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="288" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="288" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="288" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="288" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="288" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="300" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="300" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="300" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="300" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="300" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="300" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="300" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="312" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="312" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="312" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="312" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="312" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="312" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="312" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="324" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="324" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="324" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="324" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="324" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="324" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="324" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="336" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="336" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="336" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="336" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="336" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="336" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="336" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="348" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="348" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="348" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="348" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="348" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="348" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="348" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="360" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="360" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="360" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="360" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="360" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="360" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="360" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="372" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="372" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="372" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="372" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="372" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="372" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="372" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="384" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="384" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="384" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="384" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="384" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="384" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="384" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="396" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="396" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="396" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="396" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="396" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="396" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="396" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="408" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="408" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="408" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="408" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="408" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="408" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="408" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="420" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="420" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="420" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="420" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="420" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="420" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="420" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="432" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="432" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="432" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="432" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="432" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="432" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="432" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="444" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="444" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="444" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="444" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="444" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="444" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="444" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="456" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="456" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="456" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="456" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="456" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="456" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="456" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="468" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="468" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="468" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="468" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="468" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="468" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="468" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="480" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="480" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="480" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="480" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="480" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="480" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="480" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="492" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="492" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="492" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="492" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="492" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="492" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="492" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="504" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="504" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="504" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="504" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="504" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="504" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="504" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="516" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="516" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="516" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="516" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="516" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="516" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="516" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="528" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="528" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="528" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="528" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="528" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="528" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="528" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="540" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="540" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="540" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="540" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="540" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="540" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="540" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="552" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="552" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="552" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="552" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="552" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="552" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="552" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="564" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="564" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="564" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="564" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="564" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.3"/><rect x="564" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="564" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="576" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="576" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="576" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="576" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="576" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="576" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="576" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="588" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="588" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="588" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="588" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="588" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="588" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="588" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="600" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="600" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="600" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="600" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="600" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="600" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="600" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="612" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="612" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="612" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="1"/><rect x="612" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="612" y="48" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="612" y="60" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.75"/><rect x="612" y="72" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="624" y="0" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="624" y="12" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.5"/><rect x="624" y="24" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/><rect x="624" y="36" width="10" height="10" rx="2" fill="currentColor" fill-opacity="0.08"/></svg></div><div class="heatmap-chart-with-year-of-days-data-table"><table><caption>Activity score per month in 2026</caption><thead><tr><th scope="col">Month</th><th scope="col">Score</th></tr></thead><tbody><tr><th scope="row">January</th><td>75</td></tr><tr><th scope="row">February</th><td>50</td></tr><tr><th scope="row">March</th><td>32</td></tr><tr><th scope="row">April</th><td>34</td></tr><tr><th scope="row">May</th><td>51</td></tr><tr><th scope="row">June</th><td>63</td></tr><tr><th scope="row">July</th><td>77</td></tr><tr><th scope="row">August</th><td>56</td></tr><tr><th scope="row">September</th><td>34</td></tr><tr><th scope="row">October</th><td>30</td></tr><tr><th scope="row">November</th><td>48</td></tr><tr><th scope="row">December</th><td>63</td></tr></tbody></table></div></figure>
Usage example
<HeatmapChartWithYearOfDays label="Activity per day in 2026" year={2026} weekStart={1} aria-describedby="heatmap-chart-with-year-of-days-data">
  <svg viewBox="0 0 100 100">…</svg>
</HeatmapChartWithYearOfDays>
<table id="heatmap-chart-with-year-of-days-data">…</table>
Show Svelte source
// In your Svelte component:
import HeatmapChartWithYearOfDays from "lily-design-system-svelte-headless/components/HeatmapChartWithYearOfDays/HeatmapChartWithYearOfDays.svelte";

<HeatmapChartWithYearOfDays>
  <!-- content -->
</HeatmapChartWithYearOfDays>

Usage

<HeatmapChartWithYearOfDays label="Activity per day in 2026" year={2026} weekStart={1} aria-describedby="heatmap-chart-with-year-of-days-data">
  <svg viewBox="0 0 100 100">…</svg>
</HeatmapChartWithYearOfDays>
<table id="heatmap-chart-with-year-of-days-data">…</table>

Props

Prop Type Default Description
class string "" Appended to the base class
label string (required) — Accessible name
year number (required) — The calendar year shown; reflected as data-year
weekStart number (required) — First weekday row, 0 = Sunday … 6 = Saturday; reflected as data-week-start. No default: it is locale-dependent
children slot (required) — The consumer-drawn inline <svg>
...restProps HTML attributes — Spread onto the root <figure>, e.g. aria-describedby

Layout contract

The consumer draws the grid; the component names it and carries the two numbers the drawing needs. The grid has seven weekday rows and one column per week, one cell per day of year:

  • row(d) = (weekday(d) − weekStart + 7) mod 7, with weekday 0 = Sunday … 6 = Saturday, so the first row is the weekday weekStart names.
  • column(d) = floor((dayOfYear(d) − 1 + row(1 January)) / 7), with dayOfYear(1 January) = 1. The first column holds 1 January; the grid has 53 or 54 columns, and the first and last columns are partly empty.
  • 365 cells, or 366 in a leap year; empty positions draw nothing.

shared/year-of-days-layout.ts implements this once (yearOfDaysCells(year, weekStart) returns { date, dayOfYear, column, row } for every day; columnCount and daysInYear are alongside), in UTC calendar dates so a time zone cannot move a day. It is a pure module a drawing routine copies in, like the other shared/ modules; the component itself does not call it.

weekStart has no default because the first day of the week depends on the reader's locale (Sunday, Monday and Saturday are all in use): the consumer passes it, from Intl.Locale#getWeekInfo() or its own setting. Month and weekday labels, and the legend, come from the consumer through Intl; the component supplies no strings.

Accessible alternative

Colour must not be the only carrier of the value (WCAG 1.4.1), and 365 unnamed cells are not an accessible chart. Supply a dataTable: a real <table> with one row per day, or per week or month when the consumer summarises, listing the date and the value. Do not rely on a <title> inside each cell: the graphic is role="img", so its children are presentational and those titles are never exposed.

Keyboard Interactions

  • None. The chart is a single image to assistive technology.
  • A referenced data table follows native table behaviour.

ARIA

  • role="img" exposes the chart as one image (its children are presentational)
  • aria-label provides the accessible name
  • aria-describedby (consumer-supplied) references the description or data table

When to Use

  • A per-day measure across one year, where the reader looks for streaks, gaps and seasons
  • When the chart needs the catalog's standard accessible shell and a stable class hook
  • When a real data table accompanies the drawing as the accessible alternative

When Not to Use

  • Use HeatmapChart — when the two dimensions are not a calendar year (weekday by hour, for example)
  • Use LineChart — when the trend matters more than the pattern of individual days
  • Use DataTable — when readers need exact values and not a pattern
  • Use CalendarYearTable or CalendarMonthTable — when readers pick or read specific dates, not scan a pattern

Headless

This component decides semantics only: the figure element, the image role and the name. It decides no geometry, scale, colour, legend or motion.

Styles

Target .heatmap-chart-with-year-of-days for the figure and style the supplied <svg> from consumer CSS. No default styles are included.

Testing

  • year and weekStart reach the figure as data-year and data-week-start
  • Renders a <figure> with class heatmap-chart-with-year-of-days and a .heatmap-chart-with-year-of-days-graphic child with role="img"
  • label sets aria-label
  • aria-describedby and other rest props reach the figure
  • The consumer svg renders inside the figure

Advice

Fetching the per-day data, and any caching of it, is the consumer's job; the component takes the finished drawing. Pass the grid <svg> as children. Colour alone must never carry the value (WCAG 1.4.1): supply the same numbers as a real <table> and point aria-describedby at it.

Because role="img" makes descendants presentational, never put interactive controls inside the figure; place legends and toggles next to it.

Implementation Notes

  • Renders <figure class="heatmap-chart-with-year-of-days {class}" data-year data-week-start> containing the children
  • The consumer supplies the <svg> (and any legend or caption markup)
  • restProps — including aria-describedby — spread onto the <figure>
  • No internal state, no drawing, no data handling

Data table alternative (added 2026-10-05)

The graphic is wrapped in <div class="heatmap-chart-with-year-of-days-graphic" role="img" aria-label>; role="img" is not on the <figure> any more. The optional dataTable (a snippet in Svelte, a prop in React, a named slot in Vue, a projected [dataTable] element in Angular, a DataTable render fragment in Blazor, params.dataTable in Nunjucks, a slot="data-table" child in Web Components) renders the accessible table in <div class="heatmap-chart-with-year-of-days-data-table">, a sibling of the graphic and never inside it: role="img" makes its descendants presentational, so a table inside would be invisible to assistive technology. Without a data table the wrapper is not rendered (except Angular, noted above).

Related components

  • heatmap-chart — the general grid this specialises
  • calendar-year-table
  • scatter-chart
  • data-table
  • calendar-month-table
  • line-chart
  • graphic-block — chart wrapper with title and notes

References


Lily™ and Lily Design System™ are trademarks.

Canonical contract

Generated from this component's canonical metadata — the machine-checked source the implementations are held to.

Metadata

  • Component: heatmap-chart-with-year-of-days
  • PascalCase: HeatmapChartWithYearOfDays
  • Description: a heatmap chart of one year of days, one cell per day in week columns and weekday rows
  • Status: experimental — added 2026-10-22; implemented and unit-tested in all nine headless catalogs
  • HTML tag: <figure>
  • CSS class: .heatmap-chart-with-year-of-days
  • Interactive: no

ARIA

  • role="img" exposes the chart as a single image
  • aria-label and aria-describedby provide the accessible name and description

Keyboard

  • No keyboard interactions on the chart
  • A data table (when rendered) follows native table keyboard behaviour

Props

  • class: string (default: "") -- appended to the base class
  • label: string (required) -- accessible name
  • year: number (required) -- the calendar year shown (data-year)
  • weekStart: number (required) -- first weekday row, 0 = Sunday … 6 = Saturday (data-week-start); no default, it is locale-dependent
  • children: slot (required) -- the inline <svg>
  • ...restProps: HTML attributes -- spread onto the root <figure>

Markdown

This page as Markdown, for people and AI tools: heatmap-chart-with-year-of-days.md