npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fr0st/ui-starrating

v4.0.0

Published

Customizable star rating component for Frost UI with fractional values, keyboard, pointer, and touch interaction.

Readme

Frost UI StarRating

CI codecov npm version npm downloads JS gzip size CSS gzip size license

Accessible star-rating control for Frost UI with fractional values, configurable ranges and star counts, tooltips, keyboard navigation, mouse and touch input, and RTL support.

Highlights

  • Native number input remains the form control and source of truth
  • Integer, fractional, exponent, and unrestricted step="any" values
  • Configurable minimum, maximum, star count, rating text, and five sizes
  • Keyboard, mouse, touch drag, and optional hover-preview interaction
  • Direction-aware filling, pointer values, and horizontal keys in RTL
  • Accessible slider state, native labels, required and disabled state, and display-only mode
  • Frost UI v4 light, dark, system, reduced-motion, focus, and forced-colors presentation
  • Native StarRating class and starrating fQuery plugin
  • Existing-instance reuse with frozen resolved options
  • Reversible disposal that restores the input's original visibility and tabindex
  • Prebuilt ESM and UMD bundles plus expanded and minified CSS, all with source maps
  • JSDoc-powered IntelliSense

Installation

Browser projects / bundlers

Install StarRating with its Frost UI v4 and fQuery v5 peers:

npm i @fr0st/ui-starrating @fr0st/ui @fr0st/query

The package root resolves to the compiled ESM bundle. Import both required stylesheets and the default component export:

import '@fr0st/ui/dist/frost-ui.min.css';
import '@fr0st/ui-starrating/dist/frost-ui-starrating.min.css';
import StarRating from '@fr0st/ui-starrating';

const rating = StarRating.init(
    document.querySelector('#product-rating'),
    {
        stars: 5,
        step: .5,
    },
);

@fr0st/ui and @fr0st/query are peer dependencies so the component shares the application's UI and fQuery instances. The package root, dist/*, and src/* are available through package exports.

StarRating requires a browser DOM or a compatible DOM environment configured through fQuery. Server-rendered applications should load the component on the client.

Browser (ESM)

The ESM bundle imports @fr0st/ui and @fr0st/query. Frost UI and fQuery also require @fr0st/core, so map all three dependencies when loading the bundle directly in a browser:

<link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
<link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/@fr0st/ui-starrating@latest/dist/frost-ui-starrating.min.css">

<script type="importmap">
{
    "imports": {
        "@fr0st/core": "https://cdn.jsdelivr.net/npm/@fr0st/core@latest/dist/frost-core.esm.min.js",
        "@fr0st/query": "https://cdn.jsdelivr.net/npm/@fr0st/query@latest/dist/fquery.esm.min.js",
        "@fr0st/ui": "https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.esm.min.js"
    }
}
</script>
<script type="module">
    import StarRating from 'https://cdn.jsdelivr.net/npm/@fr0st/ui-starrating@latest/dist/frost-ui-starrating.esm.min.js';

    StarRating.init(document.querySelector('#product-rating'));
</script>

Browser (UMD)

Load Frost UI's all-in-one bundle before StarRating. The UI bundle supplies both the UI and fQuery globals expected by the component:

<link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui.min.css">
<link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/@fr0st/ui-starrating@latest/dist/frost-ui-starrating.min.css">

<script src="https://cdn.jsdelivr.net/npm/@fr0st/ui@latest/dist/frost-ui-bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fr0st/ui-starrating@latest/dist/frost-ui-starrating.min.js"></script>
<script>
    const rating = UI.StarRating.init(
        document.querySelector('#product-rating'),
    );
</script>

The UMD bundle adds StarRating to the existing globalThis.UI object. It expects globalThis.UI and globalThis.fQuery to exist before it loads. If the non-bundled Frost UI build is used instead, load fQuery, Frost UI, and StarRating in that order.

Do not load the separate fQuery script when using frost-ui-bundle.js or frost-ui-bundle.min.js.

Usage

Start with a normal number input and an explicit or wrapping label. StarRating inserts the visible slider immediately before the input and visually hides the original control while keeping its value synchronized for forms:

<label for="product-rating">Product rating</label>
<input
    id="product-rating"
    name="rating"
    type="number"
    min="1"
    max="5"
    step="0.5"
    value="3.5"
    required>
import StarRating from '@fr0st/ui-starrating';

const rating = StarRating.init(
    document.querySelector('#product-rating'),
    {
        ratingText(value) {
            return `${value} out of 5 stars`;
        },
    },
);

console.log(rating.getValue()); // 3.5

Calling StarRating.init() again for the same input returns its existing instance. Dispose the current instance before reinitializing the input with different options.

Options

Component options are resolved in this order:

  1. StarRating.defaults
  2. The input's data-ui-* attributes
  3. Options passed to StarRating.init()

Resolved instance.options are frozen. Native input state is applied during initialization without modifying that object: min, max, and step attributes override their matching resolved options; native readonly enables display-only behavior; and the input's current value supplies the initial rating. The initial disabled and required state, accessible label attributes, associated labels, and direction also come from the input and its DOM context.

Dispose and reinitialize to apply changes to the range, step, read-only mode, labels, or direction; use disable() and enable() to change disabled state.

| Option | Type | Default | Description | | --- | --- | --- | --- | | animate | boolean | true | Animate committed rating changes using Frost UI transition tokens. | | displayOnly | boolean | false | Present a focusable, read-only rating without editing handlers. Native readonly also enables this mode. | | hover | boolean | true | Preview the pointer rating without committing it. | | max | number \| null | null | Set the effective maximum. null uses the rendered star count. | | min | number | 0 | Set the effective minimum. | | ratingText | (rating: number) => string | Singular/plural star text | Create tooltip content and aria-valuetext. | | size | 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Select the component font size. | | stars | number | 5 | Set the number of rendered stars. Values are normalized to a positive integer. | | step | number \| 'any' | 1 | Set the rating increment. 'any' allows unrestricted finite values. | | tooltip | boolean | true | Show rating text during focus, hover, and drag interaction. |

const rating = StarRating.init(node, {
    animate: true,
    displayOnly: false,
    hover: true,
    max: 8,
    min: 2,
    ratingText: (value) => `Score ${value} of 8`,
    size: 'lg',
    stars: 10,
    step: .25,
    tooltip: true,
});

The effective range is clamped between zero and the rendered star count. Finite values are clamped to that range. Numeric steps are anchored at the effective minimum, and intermediate values advance to the next valid step; the exact minimum and maximum remain selectable. Decimal and exponent steps are supported within JavaScript numeric precision. When a step is too small for reliable snapping, the clamped value is retained instead of introducing rounding drift or an invalid value. Invalid or non-finite programmatic values are ignored, and invalid steps safely behave like step="any".

Data attributes

Options other than ratingText can be supplied through data-ui-* attributes:

| Attribute | Example | | --- | --- | | data-ui-animate | data-ui-animate="false" | | data-ui-display-only | data-ui-display-only="true" | | data-ui-hover | data-ui-hover="false" | | data-ui-max | data-ui-max="8" | | data-ui-min | data-ui-min="2" | | data-ui-size | data-ui-size="lg" | | data-ui-stars | data-ui-stars="10" | | data-ui-step | data-ui-step="0.25" | | data-ui-tooltip | data-ui-tooltip="false" |

<input
    id="product-rating"
    type="number"
    data-ui-toggle="starrating"
    data-ui-size="lg"
    data-ui-stars="10"
    min="2"
    max="8"
    step="0.25"
    value="6.5">

The component still needs to be initialized through the class or fQuery plugin. The demo uses data-ui-toggle="starrating" as a shared initialization selector:

$('[data-ui-toggle="starrating"]').starrating();

The data-ui-toggle attribute does not initialize StarRating by itself. Use native min, max, step, and readonly when those constraints are part of the input's form semantics.

Methods

| Method | Returns | Description | | --- | --- | --- | | StarRating.init(node, options?) | StarRating | Return the existing instance for an input or create one. | | disable() | void | Disable the number input, cancel dragging and hover previews, restore the committed rating, and remove the slider from the tab order. | | dispose() | void | Remove generated markup and events, unregister component state, and restore the original input. | | enable() | void | Enable the number input and restore slider interaction. | | getValue() | number \| null | Return the current finite rating, or null when the input is empty or invalid. | | setValue(value) | void | Normalize, clamp, snap, display, and commit a finite rating. |

rating.setValue(4.5);
console.log(rating.getValue()); // 4.5

rating.disable();
rating.enable();
rating.dispose();

An instance exposes its original input as instance.node and its frozen resolved configuration as instance.options. Both become null after disposal.

dispose() restores the input's original visually-hidden state, aria-hidden, and tabindex, preserves its current value and disabled state and unrelated classes, and removes generated label IDs only if the application has not changed them. The input can then be initialized again with new options. Removing the original input through fQuery also disposes the component automatically.

Events

StarRating emits one namespaced fQuery event from the original number input when a component-driven action commits a distinct normalized rating:

| Event | Description | | --- | --- | | change.ui.starrating | The value changed through keyboard, mouse, touch, or setValue(). |

import $ from '@fr0st/query';

$.addEvent(
    '#product-rating',
    'change.ui.starrating',
    (event) => {
        console.log(event.currentTarget.value);
    },
);

The underlying event type is change; fQuery exposes event.namespace as ui.starrating. Setting or selecting the current normalized value again does not emit another event. Hover preview changes only the visible fill and tooltip; it does not commit a value or change the slider's accessible value. A change event dispatched on the input refreshes the rendered control from the native value.

Native form resets cancel active dragging and refresh the visible rating, accessible value, and tooltip after the browser restores the input, without emitting a change event. This also works for read-only ratings and inputs associated with an external form through the form attribute. Canceled resets leave the current interaction intact.

Calling disable() stops an active drag, clears any hover preview, and restores the latest committed rating and tooltip text without emitting a change event. If a focus or change handler disables or disposes the component during drag startup, startup stops.

fQuery API

Importing StarRating registers starrating on fQuery.QuerySet:

import $ from '@fr0st/query';
import '@fr0st/ui-starrating';

const rating = $('#product-rating').starrating({
    size: 'lg',
    step: .5,
});

$('#product-rating').starrating('setValue', 4.5);

const value = $('#product-rating').starrating('getValue');

$('#product-rating').starrating('disable');
$('#product-rating').starrating('enable');
$('#product-rating').starrating('dispose');

Pass an options object to initialize every matched input, or pass a public method name followed by its arguments. The first component or method result is returned.

Accessibility and keyboard behavior

  • The rendered control uses role="slider" with aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext, aria-required, aria-readonly, and aria-disabled.
  • Explicit labels, wrapping labels, multiple labels, and existing aria-labelledby references contribute to the rendered slider's accessible name.
  • An input aria-label is copied when no label references are available.
  • Labels without IDs receive temporary generated IDs while the component is active.
  • The rendered slider enters the tab order while the original input becomes visually hidden and receives aria-hidden="true" and tabindex="-1", leaving one control exposed to assistive technology. Focus on the original input is forwarded to the slider, including when the input is already focused during initialization.
  • Arrow Up and Arrow Down increase and decrease by one step. Arrow Left and Arrow Right are direction-aware. Page Up and Page Down use the larger of one rating point or one step, subject to step snapping and range limits. Home and End select the effective minimum and maximum.
  • Handled slider keys prevent page scrolling.
  • Disabled ratings leave the tab order and ignore keyboard and pointer interaction.
  • Display-only and native read-only ratings expose aria-readonly="true", remain focusable, and do not install editing handlers.
  • The original input remains the submitted form field and preserves native value, required, disabled, min, max, and step semantics.

Applications remain responsible for a meaningful label, instructions, validation feedback, and sufficient contrast when overriding the component color. ratingText should describe the rating in the surrounding language and scale.

Customization

The public static defaults, classes, icons, and lang objects can be customized before initialization:

StarRating.defaults.tooltip = false;
StarRating.lang.star = 'point';
StarRating.lang.stars = 'points';
StarRating.icons.filled = '<svg aria-hidden="true" ...></svg>';
StarRating.icons.outline = '<svg aria-hidden="true" ...></svg>';

StarRating.classes exposes the generated class names for animation, the slider container, disabled state, filled and outline layers, and input hiding. Custom icons should remain decorative because the slider's accessible value comes from ARIA and ratingText.

The component stylesheet exposes these CSS custom properties:

| Property | Purpose | | --- | --- | | --ui-starrating-font-size | Star size for the current variant. | | --ui-starrating-color | Outline and fill color. | | --ui-starrating-focus-box-shadow | Focus-visible ring. | | --ui-starrating-disabled-opacity | Disabled opacity. |

.product-score .starrating {
    --ui-starrating-color: var(--ui-success);
}

Themes and RTL

StarRating combines its component stylesheet with Frost UI v4 transition, focus-ring, disabled, and reduced-motion tokens. Frost UI follows the user's preferred color scheme by default. Set data-ui-theme="light" or data-ui-theme="dark" on the document or an ancestor to select a theme explicitly:

<section data-ui-theme="dark">
    <label for="dark-rating">Dark theme rating</label>
    <input id="dark-rating" type="number" value="4">
</section>

Normal document and ancestor direction is respected. A dir attribute placed directly on the original input is copied to the rendered slider:

<input id="rtl-rating" type="number" value="4" dir="rtl">

In RTL layouts, fill begins at the inline start, pointer values are mirrored, and Arrow Left increases while Arrow Right decreases. Vertical keys, Home, End, and Page keys retain their normal meaning. Forced-colors mode replaces the star and focus colors with system colors.

Development

Use Node.js matching ^20.19.0 || ^22.13.0 || >=24. Install dependencies with npm ci, then install Playwright browsers with npx playwright install --with-deps.

npm test
npm run lint
npm run build

npm test rebuilds JavaScript and CSS, then runs the Playwright suite in Chromium, Firefox, and WebKit. npm run test:browser runs the suite against the existing bundles, so rebuild after changing source files.

After building, npm run test:coverage runs Chromium tests and writes coverage reports to coverage/.

npm run test:headed and npm run test:ui also use the existing bundles and open headed browsers or the Playwright UI.

npm run lint:sass:unused checks for unused Sass variables.

License

Frost UI StarRating is released under the MIT License.