The fastest answer: HTML provides the structure, while CSS, SVG, or JavaScript draws the bars. Use HTML and CSS for a small, static graphic; inline SVG when you need scalable, individually addressable shapes; and Chart.js when values, responsiveness, orientation, or interaction are data-driven. Whichever method you choose, define categories, numeric values, one shared scale, and a clear baseline before writing the markup.
Choose the right bar-graph method
| Approach | Best for | Strengths | Trade-offs |
|---|---|---|---|
| HTML and CSS | A few fixed values | No dependency; values remain ordinary document content; easy to style | You must calculate bar sizes and build responsiveness yourself |
| Inline SVG | Static or moderately dynamic graphics that must scale cleanly | Vector output; each rectangle and label can be addressed; supports <title> and <desc> |
Coordinates and scaling require more geometry than CSS bars |
| Canvas with Chart.js | Data-driven, responsive, configurable or interactive charts | Ready-made axes, tooltips, orientation, spacing and responsive behavior | External dependency; canvas is not semantic HTML and needs an accessible alternative |
A bar graph compares discrete categories. Keep every bar on the same baseline and scale. Do not use a truncated axis for ordinary comparisons unless the break is made unmistakable; starting at zero is the safest default.
Build a simple bar graph with HTML and CSS
This version uses a list, so category names and values are present in the document even if CSS is disabled. The percentage is the value divided by the chart’s declared maximum. Here, the maximum is 100.
<div class="bar-chart" role="img" aria-label="Monthly sign-ups: January 42, February 68, March 55, April 87">
<div class="bar-row">
<span class="bar-label">January</span>
<div class="bar-track"><span class="bar-fill" style="--value: 42%"></span></div>
<span class="bar-value">42</span>
</div>
<div class="bar-row">
<span class="bar-label">February</span>
<div class="bar-track"><span class="bar-fill" style="--value: 68%"></span></div>
<span class="bar-value">68</span>
</div>
<div class="bar-row">
<span class="bar-label">March</span>
<div class="bar-track"><span class="bar-fill" style="--value: 55%"></span></div>
<span class="bar-value">55</span>
</div>
<div class="bar-row">
<span class="bar-label">April</span>
<div class="bar-track"><span class="bar-fill" style="--value: 87%"></span></div>
<span class="bar-value">87</span>
</div>
</div>
<style>
.bar-chart { max-width: 42rem; font: 1rem system-ui, sans-serif; }
.bar-row { display: grid; grid-template-columns: 7rem 1fr 2.5rem; gap: .75rem; align-items: center; margin: .6rem 0; }
.bar-label { text-align: right; }
.bar-track { height: 1.4rem; background: #e5e7eb; border-radius: .25rem; overflow: hidden; }
.bar-fill { display: block; width: var(--value); height: 100%; background: #2563eb; }
.bar-value { font-variant-numeric: tabular-nums; }
@media (max-width: thirtyrem) {
.bar-row { grid-template-columns: 5rem 1fr 2rem; gap: .4rem; }
}
</style>
Replace the values and the maximum consistently. If your data ranges from 0 to 2,000, a value of 1,000 is 50%, not 1,000%. For negative numbers, use a centered zero axis instead of a one-sided fill; the CSS pattern above is intended for non-negative values.
#1 Best Overall
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Create a scalable bar graph with inline SVG
SVG lets you draw each bar as a <rect>. The viewBox makes the drawing scale with its container. A title and description give assistive technology a text introduction; a data table remains the most dependable way to expose every value.
<svg viewBox="0 0 500 300" role="img" aria-labelledby="chart-title chart-desc" style="max-width:100%;height:auto">
<title id="chart-title">Monthly sign-ups</title>
<desc id="chart-desc">January 42, February 68, March 55, April 87.</desc>
<line x1="55" y1="250" x2="480" y2="250" stroke="currentColor" />
<rect x="80" y="145" width="55" height="105" fill="#2563eb" />
<rect x="180" y="80" width="55" height="170" fill="#2563eb" />
<rect x="280" y="112.5" width="55" height="137.5" fill="#2563eb" />
<rect x="380" y="32.5" width="55" height="217.5" fill="#2563eb" />
<text x="107" y="270" text-anchor="middle">Jan</text>
<text x="207" y="270" text-anchor="middle">Feb</text>
<text x="307" y="270" text-anchor="middle">Mar</text>
<text x="407" y="270" text-anchor="middle">Apr</text>
</svg>
<table>
<caption>Monthly sign-ups</caption>
<tr><th scope="col">Month</th><th scope="col">Sign-ups</th></tr>
<tr><td>January</td><td>42</td></tr>
<tr><td>February</td><td>68</td></tr>
<tr><td>March</td><td>55</td></tr>
<tr><td>April</td><td>87</td></tr>
</table>
For a generated SVG, calculate each rectangle’s x position, width, and height from the same scale. Keep labels readable when categories are numerous; rotate or abbreviate them only when the full names are also available as text.
Create a data-driven chart with Chart.js
Complete browser example
Chart.js documents a canvas-based bar chart. Put the canvas in a dedicated container, load the library, then provide labels, values, and options. The CDN URL below is an example; for an application build, install Chart.js through your package manager and pin the dependency according to your deployment policy.
Rank #2
<div class="chart-container" style="position:relative;height:22rem;width:100%">
<canvas id="signups-chart" role="img"
aria-label="Monthly sign-ups: January 42, February 68, March 55, April 87">
Monthly sign-ups: January 42; February 68; March 55; April 87.
</canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const canvas = document.getElementById('signups-chart');
new Chart(canvas, {
type: 'bar',
data: {
labels: ['January', 'February', 'March', 'April'],
datasets: [{
label: 'Sign-ups',
data: [42, 68, 55, 87],
backgroundColor: '#2563eb',
borderColor: '#1d4ed8',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, title: { display: true, text: 'Sign-ups' } }
},
plugins: { legend: { display: true } }
}
});
</script>
The documented beginAtZero: true setting gives the vertical axis a zero baseline. To make horizontal bars, add indexAxis: 'y' in options. This is useful for long category labels. Chart.js also exposes controls for bar thickness and category spacing; adjust those when many categories become crowded rather than shrinking labels until they are unreadable.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Make the chart accessible
- Provide a visible heading and units, such as “Orders (per month)” rather than an unexplained number.
- For canvas, add an accessible name with
role="img"andaria-label, or fallback text inside the canvas. Canvas content is not exposed as semantic HTML to screen readers, as documented by Chart.js accessibility guidance and the MDN canvas reference. - For substantial data, include an equivalent HTML table or concise textual summary so users can retrieve every category/value pair.
- SVG can include
<title>and<desc>; connect them witharia-labelledbyand keep the underlying text meaningful. - Do not rely on color alone. Add labels, patterns, or direct values and maintain readable contrast.
- Interactive behavior needs real keyboard and assistive-technology testing. The W3C Graphics-ARIA guidance notes that static checks alone cannot confirm interactive accessibility.
Responsive sizing and visual accuracy
Let the chart container control responsive width. With canvas, changing only CSS display dimensions scales the bitmap and can distort it; set intrinsic width and height appropriately or follow the chart library’s responsive-container guidance. On small screens, horizontal bars often preserve category labels better than vertical bars. Keep units, tick intervals, and decimal precision consistent, and avoid mixing percentages and raw counts on one axis.
Common problems and fixes
Bars have the wrong length
Check that every percentage uses the same maximum and that the axis starts at the intended baseline. A mixture of maxima makes visual comparison invalid.
The chart is blank
Open the browser console. A missing Chart.js script, a misspelled canvas ID, invalid JavaScript, or a blocked CDN request prevents construction. Confirm the script loads before the initialization code and that the data arrays have equal lengths.
Labels overlap
Use indexAxis: 'y', increase the container height, shorten displayed labels while retaining full accessible text, or reduce category count. Do not rotate labels without checking touch and screen-reader usability.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →The canvas looks blurry
Do not stretch a small intrinsic canvas with CSS alone. Set its intrinsic dimensions or let Chart.js manage responsive sizing through a positioned container.
Rank #4
Screen-reader users hear nothing useful
Add an accessible name and a fallback summary, then provide a data table when individual values matter. A color legend by itself is not a text alternative.
Negative values disappear
The CSS fill example assumes values are zero or greater. Use a zero-centered SVG, a diverging layout, or Chart.js scales that include both negative and positive ranges.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
For automated page images rather than a data visualization, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one request. Its cleanup steps accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo API documentation for options such as full-page or element capture, device presets, retina scale, custom CSS/JavaScript, waits, blocked resources, authentication headers, caching, signed links, asynchronous webhooks, bulk capture, and usage reporting.
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account.
When each approach is the practical choice
- Choose HTML/CSS when the dataset is short, fixed, and best represented as normal page content.
- Choose SVG when crisp scaling, print output, or individual graphical elements matters.
- Choose Chart.js when data changes regularly or you need built-in axes, tooltips, orientation and spacing controls.
- Whichever you choose, expose the values as text and verify the result at narrow widths and with keyboard and screen-reader workflows.
Frequently Asked Questions
Can I create a bar graph using only HTML?
HTML alone supplies structure, not bar geometry. Combine it with CSS for static bars, or embed SVG markup; JavaScript is needed for data-driven drawing and interaction.
Should a bar graph always start at zero?
For ordinary magnitude comparisons, yes. A zero baseline makes bar lengths honest. If you use another baseline, label the break clearly and explain it.
Is SVG or Canvas better for accessibility?
SVG can expose titled and described vector content, while canvas requires an accessible name and text alternative because its pixels are not semantic HTML. A data table is useful with either approach.
The Bottom Line
Use CSS for a few fixed bars, SVG for scalable addressable graphics, and Chart.js for configurable data-driven charts. In every case, keep one scale and baseline, label the values in text, and test the responsive and accessible result.
Quick Recap
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.




