The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A CSS bar chart works when the categories and values are plain, readable HTML and CSS only sets the bar lengths, colours and layout. If the stylesheet fails to load or is overridden, the reader still gets every label and number. The CSS technique is simple. The part that determines whether the chart is sound is keeping the data in the markup, not in the styling.
How the work is divided between HTML and CSS
Progressive enhancement starts from content that works on its own and adds presentation where the browser supports it. For a bar chart, that means each category and its value exist as text before any CSS runs. CSS then reads a number from the markup, converts it into a bar length, and draws the bar. The words and numbers never depend on the bars.
Current CSS covers everything this approach needs: custom properties, calc() and min(). Older tutorials use the label “CSS3” for this, but CSS3 is a set of modules rather than a single version, and the techniques below rely on standard features that browsers have supported for several years.
Start with the data in HTML
The W3C and MDN guidance this article relies on favours meaningful HTML and the correct native element over added ARIA. It does not prescribe one markup pattern for every bar chart. The structure below is a practical choice for a short, single-series chart. The figures are illustrative, not measured.
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
<h3>Books sold by genre (illustrative figures)</h3>
<ul class="bar-chart" style="--max: 80">
<li style="--value: 72">
<span class="bar-label">Fiction</span>
<span class="bar"></span>
<span class="bar-value">72 units</span>
</li>
<li style="--value: 48">
<span class="bar-label">Non-fiction</span>
<span class="bar"></span>
<span class="bar-value">48 units</span>
</li>
<li style="--value: 21">
<span class="bar-label">Poetry</span>
<span class="bar"></span>
<span class="bar-value">21 units</span>
</li>
</ul>
Three details matter here. The heading names the chart in ordinary text. The label and the value sit in their own elements, so they can be read without any styling. The --value property carries the number that CSS uses, while the visible text carries the unit. The number therefore appears twice. If you generate the list from a data source, produce both from the same variable so they cannot drift apart.
Style the items as bars
- Remove the list bullets and lay out each item as a row. Use a grid with three columns: label, bar and value.
- Set a shared scale on the list. The
--maxproperty defines what 100% of the track represents. Every bar must use the same scale, or the lengths will mislead. - Convert each value to a width. Divide the item’s value by the scale and clamp the result at 100%.
.bar-chart {
list-style: none;
margin: 0;
padding: 0;
}
.bar-chart li {
display: grid;
grid-template-columns: 7rem 1fr 5rem;
align-items: center;
gap: 0.75rem;
margin-block: 0.5rem;
}
.bar {
display: block;
height: 1rem;
background: #2b6cb0;
width: min(calc(var(--value, 0) / var(--max, 1) * 100%), 100%);
}
With a scale of 80, the Fiction bar fills 90% of the track, Non-fiction fills 60%, and Poetry fills 26.25%. The min() wrapper stops a value above the scale from spilling past the track, but it also hides the overflow. Choose a scale that covers your largest value, rather than relying on the clamp.
Rank #2
What the reader sees when styling fails
- Stylesheet not loaded. The chart appears as a bulleted list of labels and values. Every category and number is still readable.
- A value is missing or misspelled. The fallbacks in the CSS above (
var(--value, 0)andvar(--max, 1)) keep the width at zero. Without a fallback, the width is invalid and the span can stretch across the whole column, showing a full bar for missing data. - Colours removed. A high-contrast or forced-colours setting can strip the background from a bar. The value text still carries the information, which is why bar colour should never be the only signal.
- Very long labels. A fixed label column can wrap or truncate text. Test the chart with your longest real category name before shipping.
Make the chart understandable to assistive technology
Give complex charts a text description or a data table
The W3C Web Accessibility Initiative explains that assistive technologies may not have access to structural information conveyed visually, and it shows how to link to or provide a fuller text description of a complex image. (W3C WAI, Complex Images) That guidance was written for images, but the same logic applies to a CSS chart. A list of one label and one value per item is already fully described by its text. If a chart has several series, time comparisons or annotations, supply a text summary or convert it into a real <table> with header cells. Presenting it this way is our recommendation for CSS charts, not a rule quoted from the W3C page.
Use native elements and avoid unnecessary ARIA
MDN advises using the correct semantic HTML element before reaching for ARIA, because native elements already supply their semantics and behaviour. (MDN Web Docs, ARIA – Accessibility) A list already announces how many items it contains. The empty bar spans hold no text, so they add nothing to the accessible name, and they need no ARIA attributes. Adding roles to decorative styling is the most common mistake here, and it is covered in the next section.
Rank #3
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
Data chart or progress indicator?
A bar drawn to a length is not automatically a progress indicator. A chart that compares categories is a data display. A progress indicator reports a position on a completion range. The markup differs for each, and so does the role.
| Aspect | Static data chart | Progress indicator |
|---|---|---|
| What the value means | Independent category values compared with each other | A single position on a completion range |
| Example | Books sold per genre | 3 of 5 checkout steps complete |
| Markup | Native list or table with labels and values | Native <progress value="3" max="5"> element with a text alternative inside |
| ARIA role | None. Do not add role="progressbar" to category bars |
Native element supplies the semantics. MDN recommends native <progress> or <input type="range"> where appropriate. (MDN Web Docs, ARIA progressbar role) |
| Role of the bar width | Decorative encoding of the value | Shows how far the task has progressed |
The Fiction chart above is a data chart, so it needs no progress markup. Use <progress> only when the example represents completion. If a region of the page is loading, WAI-ARIA 1.2 says to associate the status with the region using aria-describedby and to set aria-busy="true" on the region while it loads. That guidance applies to real loading progress, not to an ordinary data chart. (W3C, Accessible Rich Internet Applications (WAI-ARIA) 1.2)
Rank #4
Choosing between CSS bars, SVG, canvas and charting libraries
The sources behind this article do not name a winner among CSS, SVG, canvas and charting libraries. The four criteria below help you decide, and the answer depends on whether your chart is static data or progress, and on how much of its meaning must reach assistive technology.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11| Criterion | CSS bars on HTML | SVG | Canvas | Charting library |
|---|---|---|---|---|
| Semantic content in HTML | Labels and values are ordinary elements | Shapes and text are in the DOM; meaning depends on the text and descriptions you add | Pixels only; values are not in the DOM unless you add a fallback | Depends on what the library renders; inspect its output |
| Readable without styling | Yes, if labels and values are in the markup | Only if the text is in the markup | No, unless a text alternative is supplied | Depends on the library |
| Values exposed to assistive technology | Through the list or table text | Through the text and descriptions you provide | Only through a text alternative you provide | Depends on the library’s output |
| Best fit | Small static category data | Custom graphics that need precise shapes | Very large or animated datasets | Complex, interactive charts when the library’s accessible output has been checked |
For a handful of categories, CSS bars on a list or table keep the data in the document with the least machinery. Choose a heavier option when the chart needs behaviour the markup cannot give you.
Quick Recap
Best Value
The Bottom Line
“”
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

