webdevpuneet.com

Half-Star Rating Input — Free HTML CSS JS Precision Star Widget

Half-Star Rating Input · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

Copy & paste

Source Code

<div class="hsr-card">
  <h3>Rate this product</h3>
  <p class="hsr-sub">Click anywhere on a star — the left half sets a half rating, the right half sets a full one.</p>
  <div class="hsr-stars" id="hsrStars" role="slider" tabindex="0" aria-label="Star rating" aria-valuemin="0" aria-valuemax="5" aria-valuenow="0">
    <span class="hsr-star" data-index="0"><span class="hsr-fill"></span></span>
    <span class="hsr-star" data-index="1"><span class="hsr-fill"></span></span>
    <span class="hsr-star" data-index="2"><span class="hsr-fill"></span></span>
    <span class="hsr-star" data-index="3"><span class="hsr-fill"></span></span>
    <span class="hsr-star" data-index="4"><span class="hsr-fill"></span></span>
  </div>
  <p class="hsr-value" id="hsrValue">No rating yet</p>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

True half-star precision
Pointer x-position within each star determines a 0.5 or 1.0 increment, not just whole stars.
Two-layer clip fill technique
A width-clipped gold overlay star sits on top of a gray star — no SVG masks or sprite sheets.
Single render function
One diff-and-clamp formula computes full, half, and empty fill for every star from one rating value.
Live hover preview
Moving the pointer previews the rating before commit; mouseleave restores the last committed value.
Keyboard arrow support
Arrow keys step the rating by 0.5 once the widget is focused, for full keyboard accessibility.
ARIA slider semantics
role="slider" with live aria-valuenow announces the current rating to screen readers.
Human-readable value label
Displays the exact decimal rating like "3.5 out of 5 stars" instead of just filled icons.
Zero dependencies
Pure HTML, CSS, and vanilla JavaScript — no icon font or star-rating library.

About this UI Snippet

Half-Star Rating Input — Pointer-Position Detection with a CSS Overlay Fill

Screenshot of the Half-Star Rating Input snippet rendered live

Most star rating widgets only support whole-number values, but many review systems — app stores, product marketplaces, hotel booking sites — need half-star precision (3.5 out of 5, not just 3 or 4). This snippet implements that with a simple two-layer star technique and pointer-position math, no icon font or SVG masking library required.

Two stacked stars, not five discrete icons

Each .hsr-star renders a gray background star via ::before { content: '★' }, with a second, absolutely-positioned .hsr-fill element stacked exactly on top rendering the same character in gold. .hsr-fill starts at width: 0% with overflow: hidden, so only the portion of the gold star within that width is visible — clipping a full star glyph down to a partial fill is just a matter of animating that width between 0% and 100%.

Detecting which half was clicked

ratingFromEvent() reads e.clientX - rect.left to get the cursor's x-position relative to the star's own bounding box, then compares it against rect.width / 2: var half = x < rect.width / 2. Combined with the star's zero-based index, this produces a rating of index + 0.5 for a left-half click or index + 1 for a right-half click — the same math whether the event comes from a live click or a hover preview.

One render function drives both hover and committed state

render(rating) loops every star and computes diff = rating - i, clamping it to the 0–1 range with Math.max(0, Math.min(1, diff)) before converting to a percentage. A rating of 3.5 against star index 3 gives a diff of 0.5 (half-filled), against index 2 gives 1 (fully filled), and against index 4 gives a negative diff clamped to 0 (empty) — one formula naturally produces full, half, and empty stars for any rating value.

Keyboard access via ARIA slider semantics

The wrapper carries role="slider", aria-valuemin, aria-valuemax, and a live-updated aria-valuenow, with arrow-key handlers stepping the rating by 0.5 in either direction — matching the interaction model screen reader users expect from a slider, not just a decorative row of icons.

Customizing it

Change the star glyph to an SVG or icon font by swapping the content: '★' rule, adjust the step size for quarter-star precision, or wire updateValue() to submit the rating to your backend on commit.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA 5-star input renders with no rating selected and a "No rating yet" label.
  2. 2
    Hover across a starThe fill preview updates live, showing a half-fill on the left half and a full fill on the right half of each star.
  3. 3
    Click to commitThe clicked position sets the rating, and the label updates to show the exact value like "3.5 out of 5 stars".
  4. 4
    Use the keyboardTab to focus the widget, then use the arrow keys to adjust the rating in 0.5-point steps.
  5. 5
    Change the step sizeIn the JS, edit the step variable in the keydown handler and the half/1 branch in ratingFromEvent() for quarter-star precision.
  6. 6
    Wire up submissionIn updateValue(), add a fetch() call to persist the rating once the user commits a value.

Real-world uses

Common Use Cases

Product review forms
Let shoppers leave precise ratings like 4.5 stars instead of rounding to the nearest whole star.
App store and marketplace listings
Match the half-star precision users expect from major app and content stores.
Hotel and travel booking sites
Show and collect fractional quality ratings for listings and stays.
Course and instructor ratings
Collect more granular feedback than a coarse 1–5 whole-number scale allows.
Restaurant and service reviews
Give users finer control when a place feels "better than 4 but not quite 5".
Internal feedback and NPS-style forms
Use fractional star scoring for more statistically useful aggregate averages.
Related: Rating Stars Input
See the Rating Stars Input for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

ratingFromEvent() reads the click's x-coordinate relative to the star's own bounding box with e.clientX - rect.left, then checks whether that position is less than half the star's width. A left-half click adds 0.5 to the star's index; a right-half click adds 1, producing the half or full rating.

Each star is two stacked ★ characters — a gray one behind, and a gold one in an absolutely positioned .hsr-fill element with overflow: hidden and a width set as a percentage. Setting that width to 50% clips the gold star glyph to reveal only its left half, giving a visually correct half-fill using plain CSS.

For each star, it computes diff = rating - starIndex, then clamps that to the 0–1 range with Math.max(0, Math.min(1, diff)) before converting to a percentage width. A rating of 3.5 gives full stars 0–2 a diff ≥ 1 (clamped to 100%), star 3 a diff of 0.5 (50% fill), and star 4 a negative diff (clamped to 0%).

Yes. The wrapper has tabindex="0" and role="slider" with aria-valuemin/max/now, and a keydown listener lets Arrow Right/Up increase and Arrow Left/Down decrease the rating in 0.5-point steps, updating both the visual fill and the announced ARIA value.

In ratingFromEvent(), replace the single width/2 comparison with a check against width/4 and width/2 and 3*width/4 to produce four possible fractional increments per star, and change the step constant in the keydown handler from 0.5 to 0.25 to match.