
The key insight: We're hitting a tipping point where CSS-only data visualization isn't just possible—it's actually easier than reaching for JavaScript libraries for simple charts.
For years, CSS bar charts felt like clever hacks. You'd wrangle Flexbox, manually calculate percentages, and still end up with brittle code that broke the moment your data changed. But modern CSS functions are flipping this narrative entirely.
The Game-Changing Functions
The real breakthrough isn't any single feature—it's how `sibling-index()`, typed `attr()`, and CSS Grid work together to eliminate the traditional pain points.
Take this elegantly simple approach:
1.chart {
2 display: grid;
3 grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
4 align-items: end;
5 height: 200px;
6}
7
8.chart li {The HTML becomes refreshingly semantic:
1<ul class="chart" role="figure">
2 <li data-value="75" style="--color: #e74c3c;">Q1 Sales</li>
3 <li data-value="40" style="--color: #3498db;">Q2 Sales</li>
4 <li data-value="90" style="--color: #2ecc71;">Q3 Sales</li>
5 <li data-value="65" style="--color: #f39c12;">Q4 Sales</li>
6</ul><> The magic happens whensibling-index()automatically positions each list item in consecutive grid columns, whileattr(data-value number)directly converts HTML data attributes into CSS length values./>
No JavaScript. No manual positioning. No fragile calculations.
Why This Matters Beyond "Cool Factor"
This isn't just about showing off CSS skills. There are legitimate architectural benefits:
Performance wins: CSS-only charts eliminate JavaScript bundle bloat. Your dashboard loads faster, especially on mobile connections.
Accessibility by default: Semantic HTML gives you ARIA roles, keyboard navigation, and screen reader support without extra work. A <table> with chart styling remains a perfectly readable data table.
Maintenance simplicity: Data lives in HTML attributes where non-developers can edit it. No need to understand JavaScript object structures or library APIs.
Progressive enhancement: Charts render immediately with CSS, then JavaScript can add interactions later if needed.
Advanced Patterns That Actually Work
Once you grasp the basics, you can build surprisingly sophisticated visualizations:
Dynamic Scaling
1.chart {
2 --max-value: 100;
3 --scale-factor: calc(100% / var(--max-value));
4}
5
6.chart li {
7 height: calc(attr(data-value number) * var(--scale-factor));
8}Grid Lines and Axes
1.chart::before {
2 content: "";
3 position: absolute;
4 width: 100%;
5 height: 100%;
6 background-image:
7 repeating-linear-gradient(
8 to top,
9 transparent 0%,
10 transparent calc(25% - 1px),
11 #ddd calc(25% - 1px),
12 #ddd 25%
13 );
14 pointer-events: none;
15}Responsive Breakpoints
1@container (max-width: 600px) {
2 .chart {
3 grid-template-columns: 1fr;
4 grid-template-rows: repeat(auto-fit, minmax(0, 1fr));
5 }
6
7 .chart li {
8 grid-row: sibling-index();
9 grid-column: 1;
10 }
11}The Practical Workflow
Start with [Charts.css](https://chartscss.org/) for rapid prototyping:
1<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chart.css/dist/chart.min.css">
2
3<table class="charts-css bar multiple">
4 <tbody>
5 <tr>
6 <th scope="row">Q1</th>
7 <td style="--size: 0.75;">75%</td>
8 </tr>
9 <tr>
10 <th scope="row">Q2</th>
11 <td style="--size: 0.40;">40%</td>
12 </tr>
13 </tbody>
14</table>Then customize the CSS variables and grid properties for your specific design needs.
Current Limitations (And Workarounds)
Be realistic about what CSS can't do yet:
- Complex interactions: Tooltips, zooming, and click handlers still need JavaScript
- Counter limitations: CSS counters only work with integers, not decimals
- Browser support:
sibling-index()and typedattr()are cutting-edge—have fallbacks ready
For interactions, use CSS for the visual foundation and layer JavaScript on top:
1document.querySelectorAll('.chart li').forEach(bar => {
2 bar.addEventListener('mouseenter', (e) => {
3 // Show tooltip using CSS classes
4 e.target.classList.add('hovered');
5 });
6});Why This Matters
We're witnessing CSS evolve from a styling language to a legitimate application development platform. These chart techniques represent something bigger: the boundaries between CSS, JavaScript, and HTML are becoming more fluid and intentional.
For dashboard prototypes, static site generators, or any project where you need quick visual feedback without JavaScript complexity, CSS-only charts are no longer a compromise—they're often the better choice.
The next time you reach for Chart.js or D3 for a simple bar chart, ask yourself: do I actually need JavaScript for this? The answer might surprise you.
