Every personal site eventually turns into an RPG character sheet, so I leaned into it rather than fighting it. The stat bars are plain CSS — no JavaScript, no charting library.
How a stat bar works
Each bar is a track with a fill inside it, and the fill width is a percentage. On load it animates in from zero, which is the only “juice” in the whole component.
.stat-fill {
background: linear-gradient(90deg, var(--ki-sky-deep), var(--ki-sky-soft));
animation: ki-fill .9s cubic-bezier(.22, .61, .36, 1) both;
}
Why percentages
Because everything is driven from hugo.yaml. Adding a skill is a three-line
change, and the value can be anything from 0 to 100.
The XP bar
The XP bar is the same idea with segment ticks drawn over it using a
repeating-linear-gradient, so it reads as a JRPG progress bar rather than a
plain progress element.
<span class="xp-track"><span class="xp-fill" style="width: 68%"></span></span>
Accessibility
Every bar carries a role="meter" with aria-valuenow, aria-valuemin, and
aria-valuemax, so a screen reader announces the number instead of nothing at
all. The animation is disabled under prefers-reduced-motion.
Things I tried first
- A canvas-based bar graph. Way too much JavaScript for what is one
div. - A pure-CSS grid of blocks, like an HP bar in an old JRPG. Looked great for HP, terrible for anything with a non-round number.
- The current approach.
Next up
Skill trees, and probably an achievement popup when a post hits a milestone. Maybe not.