Star Rating Breakdown Card — HTML CSS JS Snippet

Star Rating Breakdown Card · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Large score with half-star pure CSS technique
Bar chart animates on IntersectionObserver viewport entry
Percentages driven by data-pct attributes — no JS changes needed
Sentiment badge row with green and yellow variants
Featured review with initials avatar fallback
Fully responsive flex layout
Zero dependencies — vanilla HTML, CSS, JavaScript

About this UI Snippet

Star Rating Breakdown Card — Animated Bar Chart, Sentiment Badges & IntersectionObserver Reveal

Screenshot of the Star Rating Breakdown Card snippet rendered live

A rating breakdown card communicates product or service quality at a glance by displaying a numeric aggregate score alongside a distribution histogram of individual star ratings. Amazon pioneered this pattern and it has become standard across e-commerce, SaaS review pages, and app stores. The key insight is that the distribution often tells a more nuanced story than the average — a product with a 4.2 average but a large spike of 1-star reviews signals a quality consistency problem that the average hides.

HTML structure

The card uses a two-column flex layout inside .card-header: the left .score-block holds the large numeric score, star icons, and review count; the right .bars-block holds the five distribution rows. Each .bar-row is a flex container with a label, a track div containing the fill div, and a percentage label. The fill div's actual width starts at zero and animates to its target value on page entry.

Half-star CSS technique

The half-star is implemented with a pure CSS trick: the star character (★) is displayed in gray as its base color, then a ::before pseudo-element is positioned absolutely over it with the same character in yellow (#f59e0b). The pseudo-element has width: 55%; overflow: hidden, which clips it to just over half width, creating a convincing half-fill. No JavaScript or SVG required — just two layered characters.

Bar animation via IntersectionObserver

Rather than animating on page load (which wastes CPU if the card is below the fold), the bars animate when the card enters the viewport. An IntersectionObserver with threshold: 0.3 fires when 30% of the card is visible. The callback iterates over all .bar-fill elements, reads their data-pct attribute, and sets element.style.width = pct + '%'. Because the CSS already defines transition: width 0.8s cubic-bezier(0.16, 1, 0.3, 1), setting the inline style triggers the smooth easing animation. After triggering, observer.unobserve() prevents re-animation on scroll.

Color coding the bars

All five bars share the same amber fill (#f59e0b) in this implementation. An alternative approach colors them by star level: 5★ green, 4★ lime, 3★ yellow, 2★ orange, 1★ red. This can be done with :nth-child(n) selectors on .bar-row or with individual data-color attributes read in JavaScript.

Sentiment badges

The badge row uses flex-wrap so badges reflow on narrow viewports. Green badges (background: #dcfce7; color: #15803d) indicate positive sentiment patterns derived from review text analysis. Yellow-tinted badges signal mixed signals. This badge system is common in Google Play Store and Trustpilot product summaries.

The reviewer snippet section

A featured review below the histogram adds social proof specificity. The avatar uses initials (generated from the reviewer name) with a blue background — this is the standard fallback when profile photos are unavailable. The star row uses plain HTML star characters sized to 12px rather than SVG icons, keeping the markup simple.

Accessibility considerations

The numeric score and review count are visible text so no aria labels are needed there. The star elements should have aria-hidden="true" since the score is already conveyed by the adjacent "4.7" text. The bar chart is decorative — the percentage labels next to each bar already convey the data to screen readers, so no additional ARIA roles are required.

React integration

Accept a rating prop (number), a reviews prop (total count), a distribution prop (object mapping star count to percentage), and a sentiments array. Render bars with inline style={{ width: '0%' }} initially and use useEffect with an IntersectionObserver ref to trigger animation on mount. The featured review can be passed as a featuredReview prop object.

Use cases beyond products

This component adapts well to service ratings (restaurants, hotels), employee survey results (Net Promoter Score distribution), course ratings on learning platforms, and app store quality breakdowns. The bar chart pattern is universally understood and builds immediate credibility compared to a simple star average. See also the star rating snippet for the interactive input version, the review card snippet for individual testimonials, and the testimonial masonry snippet for grid layouts of multiple reviews.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to piece together the half-star trick or the reveal timing by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the half-star's before pseudo-element, clipped to 55% width with overflow hidden, sits layered over the gray base star to fake a partial fill with no SVG or JavaScript involved, and why the bar-fill animation is triggered by an IntersectionObserver rather than firing immediately on page load. The same assistant can help you optimize it — ask whether observing the whole .card element and calling unobserve once is the right granularity, or whether it should observe each .bar-row individually for a staggered reveal. It's also useful for extending the card: ask it to color-code each star-level bar differently, add a filter that shows only reviews matching the clicked bar's star level, or wire the sentiment badges to real keyword-extraction results from review text. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a star rating breakdown card in plain HTML, CSS, and JavaScript with no library — a big aggregate score, a 5-to-1-star distribution bar chart, sentiment badges, and a featured review snippet.

Requirements:
- Render a large numeric average score next to a row of star characters, where a fractional star (e.g. .7) is achieved with pure CSS: display a base star character in gray, then layer a before pseudo-element containing the same character in the accent color, absolutely positioned over it and clipped with a percentage width and overflow hidden matching the fractional amount — no SVG, no JavaScript-driven partial fill.
- Render five distribution rows (5 star down to 1 star), each with a label, a track bar, and a percentage caption, where every bar-fill element starts at 0% width in its inline style and carries its target percentage in a data attribute.
- Do not animate the bars on page load. Instead, use an IntersectionObserver watching the card container with a partial visibility threshold (e.g. 0.3): only when the card scrolls into view should the code read each bar's data-percentage attribute and set its actual width, letting the existing CSS width transition animate it in. Call unobserve once triggered so it never re-animates on repeated scrolling.
- Add a row of small sentiment badges with at least two visual variants (e.g. a positive/green style and a mixed/yellow style) reflecting short extracted phrases.
- Add a featured single review below a divider, including an initials-based avatar (derived from the reviewer's name, not an image) as the fallback for a missing profile photo.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Copy the HTML card structureInclude the .card-header with both .score-block and .bars-block. Each .bar-row needs a .bar-fill with a data-pct attribute set to the percentage (0–100).
  2. 2
    Add your rating dataChange the score-big text, adjust data-pct values on each .bar-fill, and update the .bar-pct text labels to match your real distribution data.
  3. 3
    Paste the CSSThe CSS is self-contained. Adjust #f59e0b for your brand color. The .bar-fill transition handles the animation — no changes needed.
  4. 4
    Add the IntersectionObserver JSThe script fires when the card scrolls 30% into view and sets each bar width from data-pct. It unobserves after the first trigger to prevent re-animation.
  5. 5
    Customise sentiment badgesEdit the .badge elements inside .sentiments. Use badge-green for positive and badge-yellow for mixed. Add badge-red for common negative feedback themes.

Real-world uses

Common Use Cases

Product Pages
E-commerce rating breakdown to build purchase confidence
Course Platforms
Show student satisfaction distribution on course listings
App Stores
Mobile app or SaaS review distribution widget
Service Businesses
Hotel, restaurant, or agency quality distribution display

Got questions?

Frequently Asked Questions

Accept a distribution prop (object mapping 1–5 to percentages) and use useEffect with an IntersectionObserver ref to trigger bar width animation after mount.

Yes — use :nth-child CSS selectors on .bar-row .bar-fill or set data-color attributes and read them in the JS to apply different background colors per row.

See the interactive star rating snippet at /ui-snippets/star-rating/ for a click-and-hover input version.

Yes — replace the hardcoded data-pct values by reading your API response and setting element.style.width directly in JS, then trigger the transition with a requestAnimationFrame.

Open the Export menu (or the Test Exports preview) in the snippet toolbar. It generates a plain React component, a React + Tailwind version where the bar and badge styles become utility classes, a Vue 3 single-file component with the IntersectionObserver logic in script setup, and an Angular standalone component. Each converter preserves the markup, CSS bar animation, and behaviour — the observer that triggers the bar fill is deferred to the framework mount lifecycle (useEffect, onMounted, or ngAfterViewInit), so the card animates the same way in React, Vue, and Angular. For production, pass the distribution in as a component prop or input instead of reading data-pct attributes.