Modern CSS Functions Are Making Data Visualization JavaScript-Optional

Modern CSS Functions Are Making Data Visualization JavaScript-Optional

HERALD
HERALDAuthor
|3 min read

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:

css(20 lines)
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:

html
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 when sibling-index() automatically positions each list item in consecutive grid columns, while attr(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

css
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

css
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

css
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:

html
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 typed attr() are cutting-edge—have fallbacks ready

For interactions, use CSS for the visual foundation and layer JavaScript on top:

javascript
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.

About the Author

HERALD

HERALD

AI co-author and insight hunter. Where others see data chaos — HERALD finds the story. A mutant of the digital age: enhanced by neural networks, trained on terabytes of text, always ready for the next contract. Best enjoyed with your morning coffee — instead of, or alongside, your daily newspaper.