Navigation✨ Premium

Chart Data Labels

CSS-only bar chart: data labels appear on hover, anchored to each bar top and connected by a CSS triangle arrow. Zero JavaScript.

CSSHoverDataViz

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Navigation has 36 effects, including 4 free. Explore the category →

3 usage examples

SaaS Dashboard — Weekly completion rate — Chart Data Labels example 1

① SaaS Dashboard — Weekly completion rate

WhenAn internal dashboard displaying daily KPIs: task completion rate, active sessions, or conversions per weekday.
WhyBars convey the trend at a glance; labels appear on hover to deliver the exact value without cluttering a dense dashboard. The interaction stays unobtrusive.
SettingsMax height 140 px (100% value), default indigo gradient, gap: 14px between groups, label at font-size: .72rem to suit dense screens.
Sports results — Player performance over the season — Chart Data Labels example 2

② Sports results — Player performance over the season

WhenA top-scorers leaderboard in a sports app or live-score widget: 5 strikers have very close season totals (13–15 goals), producing bar heights that are nearly identical.
WhyWhen multiple players have near-identical scores, adjacent bars sit at almost the same height and their labels appear side-by-side on hover. This is exactly the scenario the effect handles gracefully: each label floats independently above its own bar, enabling fine comparison without cluttering the view.
SettingsTightly clustered heights 91–105 px (13–15 goals out of 20 max); emerald palette (linear-gradient(#4ade80, #16a34a)); labels showing "14 G" at font-size: .72rem; gap: 14px between groups.
Article infographic — Engagement rate by channel — Chart Data Labels example 3

③ Article infographic — Engagement rate by channel

WhenAn interactive chart embedded in a blog post, report page, or landing page with product statistics — readers hover to get exact figures.
WhyAn editorial layout benefits from a clean chart without figures at rest: values remain available on demand, and the eye reads the bar hierarchy first.
SettingsWarm palette (#fb923c → #f97316 across bars with distinct shades), max height 160 px for visual hierarchy, border-radius: 4px 4px 2px 2px for a more restrained look.

How it works

The effect is entirely CSS — no JavaScript. Each .chart-bar-group uses flex-direction: column to stack the bar above its axis label. The .chart-data-label is position: absolute with bottom: calc(100% + 8px): it floats 8 px above the bar top regardless of bar height, and centers itself via translateX(-50%).

The label reveal relies on the descendant selector .chart-bar:hover .chart-data-label. At rest, the label is hidden (opacity: 0) and slightly shifted down (translateY(4px)). On hover, opacity moves to 1 and translateY to 0 over 200 ms — a subtle float-up motion that visually anchors the value to the bar. The bar simultaneously gets filter: brightness(1.2) and transform: scaleY(1.04) with transform-origin: center bottom, making it "swell" upward without shifting its baseline.

The label's pointer arrow is a ::after pseudo-element using the CSS border trick: zero width, border-top-color set to the tooltip background, the three other sides transparent — producing a downward-pointing triangle visually connected to the bar top. pointer-events: none on the label prevents it from capturing mouse events over the gap between bar and label, avoiding hover flicker.

The "automatic overlap avoidance" comes from each label following its own bar's height: bars of different heights mechanically produce labels at different vertical positions. There is no collision-detection algorithm — two bars of very similar height would produce adjacent labels at the same level with no correction.

Accessibility

  • prefers-reduced-motion: not handled in the code — transition: opacity .2s, transform .2s fires unconditionally. Add manually: @media (prefers-reduced-motion: reduce) { .chart-data-label { transition: none } }.
  • Keyboard navigation: the effect relies exclusively on :hover. No tabindex or :focus-within is defined on .chart-bar — keyboard users cannot reveal labels.
  • Text contrast: #fff on rgba(15, 15, 30, .92) ≈ 16:1 — WCAG AAA. Values pass both AA and AAA comfortably.
  • Screen reader legibility: values (72%, 53%…) are present in the DOM and readable by assistive technology, even though they are visually hidden at rest.
  • No role or aria-label on the .chart-scene container — add role="img" aria-label="…" to give the chart meaning in an accessible document.

Browser compatibility

Pure CSS3: flexbox, absolute positioning, transitions, gradients, pseudo-elements — supported in all modern browsers since 2016.

Chrome 49+✓ Full
Firefox 45+✓ Full
Safari 10+✓ Full
Edge 16+✓ Full
Mobile iOS✓ Tap activates label
Android Chrome✓ Tap activates label

Without CSS transitions, labels switch instantly from hidden to visible on hover with no animation — data remains accessible. On touch devices, a tap activates the <code>:hover</code> state and reveals the label; tapping elsewhere hides it.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="anchor-demo">
  <div class="chart-scene">
    <div class="chart-bar-group">
      <div class="chart-bar chart-bar-1">
        <span class="chart-data-label">72%</span>
      </div>
      <span class="chart-bar-label">Mon</span>
    </div>
    <div class="chart-bar-group">
      <div class="chart-bar chart-bar-2">
        <span class="chart-data-label">53%</span>
      </div>
      <span class="chart-bar-label">Tue</span>
    </div>
    <div class="chart-bar-group">
      <div class="chart-bar chart-bar-3">
        <span class="chart-data-label">91%</span>
      </div>
      <span class="chart-bar-label">Wed</span>
    </div>
    <div class="chart-bar-group">
      <div class="chart-bar chart-bar-4">
        <span class="chart-data-label">38%</span>
      </div>
      <span class="chart-bar-label">Thu</span>
    </div>
    <div class="chart-bar-group">
      <div class="chart-bar chart-bar-5">
        <span class="chart-data-label">65%</span>
      </div>
      <span class="chart-bar-label">Fri</span>
    </div>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
.chart-bar-N { height } 110px / 80px / 140px / 60px / 100px Each bar's height proportional to its data value. Adjust to reflect your real metrics.
.chart-bar-N { background } indigo / pink / green / amber / orange gradients Brand color per bar — replace the linear-gradient() in each .chart-bar-N with your palette.
bottom: calc(100% + 8px) 8px Gap between the label and the bar top. Increase to 12px if you have decorations above the bars.
transition: opacity .2s, transform .2s 0.2s Entry/exit animation duration. Drop to .12s for a snappier feel, raise to .35s for smoother motion.
translateY(4px) → translateY(0) 4px Float-up amplitude on hover. Use 6px for more drama, 2px for a nearly imperceptible shift.
background: rgba(15, 15, 30, .92) .92 Tooltip background — lower opacity (e.g. .75) for a semi-transparent label, or change color for a light theme.

FAQ

Yes, the effect is 100% CSS — no JavaScript at all. Hover, animation, arrow, and tooltip are handled entirely by CSS selectors and transitions. Copy the HTML and CSS into any project — Angular, React, Vue, Svelte, or plain HTML — with no initialization required.
On iOS and Android, tapping a bar activates the CSS :hover state and reveals its label; tapping elsewhere hides it. The behavior works but is not optimized for touch: no visual affordance suggests interactivity. For mobile-first use, consider showing labels permanently (opacity: 1) or replacing them with static annotations.
The code does not include a prefers-reduced-motion media query. Add this rule after the effect CSS: @media (prefers-reduced-motion: reduce) { .chart-data-label { transition: none; } .chart-bar { transition: none; } }. Labels remain visible on hover (opacity: 1) — only the animation is removed.