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

@oleksandr-94/aura-css

v0.1.0

Published

Aura — a framework-agnostic, themeable, skinnable CSS component library (SCSS source + optional JS behaviour layer).

Readme

Aura

A framework-agnostic, themeable, skinnable CSS component library authored in SCSS. Works in any project — plain HTML, React/Vue/Svelte, or alongside Tailwind — via a single stylesheet. Theming and surface style ("skin") are decoupled from markup, so the same components can be glass in one app and flat in another, on different palettes.

Status: early foundation. Complete theming/skin/token system + 2 components (button, card). More components are being added on top of this base.


Quick start

# from aura/
npm install     # sass + postcss + esbuild toolchain (dev only)
npm run build   # → dist/ : CSS (expanded + min, per skin) + JS bundles + types

Include the stylesheet and set a theme on the root element:

<html data-theme="aura-dark">
  <head>
    <link rel="stylesheet" href="dist/aura.css" />
  </head>
  <body>
    <button class="btn">Primary</button>
    <div class="card"><h3 class="card__title">Hello</h3></div>
  </body>
</html>

No build in your consuming project? Just ship the compiled dist/aura.css.


The three config levers

Everything adapts through src/_config.scss. Override with Sass configuration:

@use "aura/src/index" with (
  $prefix: 'au-',     // namespace all classes → .au-btn (default: none)
  $skin:   flat,      // surface treatment: glass | flat | neu
  $themes: ( ... )    // your colour palettes (see Theming)
);

| Lever | What it controls | Default | |-------|------------------|---------| | $prefix | Class-name namespace (avoid collisions with other libs) | '' | | $skin | Surface look — glass / flat / neu | glass | | $themes | Colour palettes, swapped at runtime via data-theme | dark + light |


Theming

Colours follow Material 3 color roles: every fill colour has a paired on-* content colour for guaranteed contrast, and a subtle *-container tint for badges/alerts.

You only define base colours — each on-* (text-on-colour) is derived automatically by luminance, so "white-on-white" is structurally impossible.

Token contract

Components reference only these CSS variables (never hardcoded colours):

Accents & status — each has --<role>, --on-<role>, --<role>-container, --on-<role>-container: primary, secondary, accent, success, warning, error, info

Surfaces & lines | Token | Use | |-------|-----| | --surface | Page background | | --surface-1 / --surface-2 | Card / elevated (modal, popover) | | --on-surface / --on-surface-muted | Body text / secondary text | | --outline / --outline-strong | Dividers / control borders | | --glass-film / --glass-film-2 | Frosted film (glass skin only) | | --shadow-1 / --shadow-2 | Elevation |

Static scales (theme-independent) --radius-sm|md|lg|pill · --space-1|2|3|4|6|8 · --font · --fs-xs|sm|md|lg|xl · --blur · --z-dropdown|sticky|modal|toast · --gradient (general brand gradient, auto-built from the theme's accents)

Add a theme

Quickest: open docs/theme-generator.html — pick accent colours + a base mode, get a live preview and a ready-to-paste [data-theme] block (with on-* and containers derived).

Or add an entry to $themes — only base colours required:

$themes: (
  ocean: (
    primary: #0EA5E9, secondary: #6366F1, accent: #22D3EE,
    success: #16A34A, warning: #D97706, error: #DC2626, info: #3B82F6,
    surface: #0b1220, surface-1: #131c2e, surface-2: #1b2740,
    on-surface: #e6edf7, on-surface-muted: rgba(230,237,247,.62),
    outline: rgba(255,255,255,.14), outline-strong: rgba(255,255,255,.24),
    glass-film: rgba(255,255,255,.06), glass-film-2: rgba(255,255,255,.10),
    shadow-1: (0 8px 32px rgba(0,0,0,.4)), shadow-2: (0 16px 44px rgba(0,0,0,.5)),
  ),
);

Rebuild, then use it: <html data-theme="ocean">. Themes can also be layered per subtree — nest data-theme on any element.


Skins

The skin is the surface treatment, independent of colour. Components request their chrome via three mixins — surface() (panels/cards), field() (input wells) and control() (filled buttons) — and the active skin renders each. So buttons, inputs and cards all adapt together.

| Skin | Card (surface) | Button (control) | Input (field) | |------|------------------|--------------------|-----------------| | glass | Frosted film + blur | Solid + soft glow + gloss | Translucent well + blur | | flat | Solid + border | Solid fill | Solid + border | | neu | Extruded shadows | Extruded, inset on press | Inset well |

Switch at build time with $skin. Each skin lives in its own file under src/skins/; adding a new one = new file + one branch in src/_surface.scss.

Prebuilt skin outputs: dist/aura.css (glass, default), dist/aura-flat.css, dist/aura-neu.css.


Components

Button — .btn

Base class renders the primary button. Add modifiers to change colour, style, size.

| Class | Effect | |-------|--------| | .btn | Primary (default) | | .btn--secondary / .btn--accent | Accent colours | | .btn--success / .btn--warning / .btn--error / .btn--info | Status colours | | .btn--ghost | Transparent, bordered | | .btn--outline | Outlined; combine with a colour modifier | | .btn--gradient | Gradient fill — follows the colour; combine with a colour modifier (.btn--gradient.btn--success) | | .btn--sm / .btn--lg | Sizes | | .btn--block | Full width | | .btn--disabled / [disabled] | Disabled state |

States handled automatically: :hover, :active, :focus-visible, :disabled.

<button class="btn">Primary</button>
<button class="btn btn--secondary">Secondary</button>
<button class="btn btn--error btn--outline">Delete</button>
<button class="btn btn--lg btn--block">Continue</button>
<button class="btn" disabled>Disabled</button>

Card — .card

Surface container rendered through the active skin (glass/flat/neu).

| Class | Element / effect | |-------|------------------| | .card | Container (padded, uses surface()) | | .card__header / .card__title / .card__body / .card__actions | Header row / heading / text / footer | | .card--success / --warning / --error / --info | Coloured status accent (left bar) | | .card--gradient | Gradient fill; combine with a colour modifier |

<div class="card card--success">
  <div class="card__header">
    <h3 class="card__title">Acme Inc.</h3>
    <span class="badge badge--success">Active</span>
  </div>
  <p class="card__body">Enterprise plan · renews 12 Aug.</p>
</div>

<div class="card card--gradient card--error"> … </div>

Input — .input / .select / .textarea

Text controls whose "well" is rendered through the field() mixin, so they adopt the active skin (glass / flat / neu). Wrap with .field to attach a label and hint.

| Class | Effect | |-------|--------| | .input / .select / .textarea | Base controls | | .input--sm / .input--lg | Sizes | | .input--error (also --select/--textarea) | Error state (red border) | | .field | Labelled wrapper (label + control + hint) | | .field__label | Label | | .field__hint / .field__hint--error | Helper / error text |

States handled automatically: :focus-visible (primary ring), :disabled.

<div class="field">
  <label class="field__label" for="email">Email</label>
  <input id="email" class="input" type="email" placeholder="[email protected]">
</div>

<div class="field">
  <label class="field__label" for="pw">Password</label>
  <input id="pw" class="input input--error" type="password">
  <span class="field__hint field__hint--error">At least 8 characters.</span>
</div>

Badge — .badge

Small status label. Colour variants use soft *-container tints with a guaranteed-contrast content colour.

| Class | Effect | |-------|--------| | .badge | Base (neutral) | | .badge--primary / --secondary / --accent | Accent tints | | .badge--success / --warning / --error / --info | Status tints | | .badge--outline | Transparent, bordered | | .badge--sm / .badge--lg | Sizes | | .badge__dot | Leading status dot |

<span class="badge badge--success"><i class="badge__dot"></i> Active</span>
<span class="badge badge--error badge--outline">Failed</span>

Alert — .alert

A message panel with an accent bar and icon. Colour variants carry status meaning.

| Class | Effect | |-------|--------| | .alert | Base (primary) panel | | .alert--success / --warning / --error / --info | Status colours | | .alert__icon | Leading icon | | .alert__title / .alert__body | Bold heading / muted description |

<div class="alert alert--error">
  <span class="alert__icon">✕</span>
  <div>
    <div class="alert__title">Payment failed</div>
    <div class="alert__body">Your card was declined.</div>
  </div>
</div>

Switch — .switch

A toggle for boolean settings. The track is a skin-aware well; when checked it fills with the accent colour and shows a white thumb.

| Class | Effect | |-------|--------| | .switch | Toggle (wraps a checkbox) | | .switch__track / .switch__thumb | Track / knob | | .switch--success / --warning / --error | Checked colour | | .switch--sm / .switch--lg | Sizes |

<label class="switch">
  <input type="checkbox" checked>
  <span class="switch__track"></span>
  <span class="switch__thumb"></span>
</label>

Checkbox — .checkbox · Radio — .radio

Custom controls set directly on the native input. The box/circle is a skin-aware well; checked fills with the accent colour + white mark. :indeterminate (checkbox) shows a dash.

| Class | Effect | |-------|--------| | .checkbox / .radio | On the native input | | .checkbox--success / --warning / --error | Checked colour | | .radio--success / --warning / --error | Checked colour |

<input type="checkbox" class="checkbox" checked>
<input type="radio" name="plan" class="radio radio--success" checked>

Select — .select

Native <select> with a custom chevron. Same sizes (--sm/--lg) and validation (--error/--success) as text inputs.

Range — .range · File — .file · Rating — .rating

| Class | Effect | |-------|--------| | .range | Themed slider · --success/--warning/--error · --sm/--lg | | .file | Styled file input · .file--ghost | | .rating | Star rating on radios (CSS-only, submittable) · --sm/--lg |

<input class="range range--success" type="range" value="80">
<input class="file" type="file">
<div class="rating">
  <input type="radio" name="r" value="5"><label>★</label>
  <input type="radio" name="r" value="4" checked><label>★</label>
  …
</div>

Validation — add --error / --success to any control; use .field__label--required (adds *) and .field__hint--error / --success for messages.

Tabs — .tabs

A tab bar with switchable panels. Point each .tab at a panel with data-tab="#id"; the JS activates it and toggles panel visibility. Frameworks render active tab + panel from state.

| Class | Effect | |-------|--------| | .tabs | Container: pill (default) · --underline / --lift · --sm / --lg · --block | | .tab / .tab--active | Tab item / selected | | .tab-panel | Content panel (toggled via hidden) | | .tab-panels | Panel container (plain by default; card after --lift tabs) | | .tab-card | Wraps .tabs + .tab-panels into one connected card (no gap) |

<div class="tabs" role="tablist">
  <button class="tab tab--active" data-tab="#p1">Overview</button>
  <button class="tab" data-tab="#p2">Activity</button>
</div>
<div id="p1" class="tab-panel">Overview…</div>
<div id="p2" class="tab-panel" hidden>Activity…</div>

Group — .group (alias .btn-group)

Joins adjacent controls — buttons and form fields — into one segmented unit (square inner corners, single shared border). Fields grow, buttons stay natural. .group--block for full width.

<!-- button group -->
<div class="group">
  <button class="btn btn--ghost">Day</button>
  <button class="btn btn--ghost">Week</button>
</div>

<!-- input group -->
<div class="group">
  <input class="input" placeholder="Search…">
  <button class="btn">Search</button>
</div>

Radio segmented — for single-select, .segmented uses native radios (CSS-only, keyboard-accessible, form-submittable); the checked pill is skin-aware.

<div class="segmented" role="radiogroup">
  <label class="segmented__option"><input type="radio" name="v" checked><span>Day</span></label>
  <label class="segmented__option"><input type="radio" name="v"><span>Week</span></label>
</div>

Table — .table

Transparent data table (place in a .card). .table--zebra for stripes, .table--compact for tighter rows.

<table class="table table--zebra"> … </table>

Progress — .progress · Spinner — .spinner

| Class | Effect | |-------|--------| | .progress / .progress__bar | Track / fill (set width inline) | | .progress--success / --warning / --error | Fill colour | | .spinner | Loading spinner · --sm / --lg |

<div class="progress"><span class="progress__bar" style="width:72%"></span></div>
<span class="spinner"></span>

Tooltip — .tooltip

Pure-CSS tooltip on hover/focus, inverted vs. the theme. .tooltip--bottom to flip placement.

<span class="tooltip">
  <button class="btn">Hover me</button>
  <span class="tooltip__text">Helpful hint</span>
</span>

Interactive components (Modal, Dropdown)

Stateful components keep their state in the DOM as data-state="open|closed"; CSS renders it. Nothing in the contract is tied to the library name.

  • Vanilla — load the optional behaviour layer; it auto-wires triggers and adds scroll-lock, focus-trap, Esc and outside/backdrop close:
    <script type="module" src="src/js/interactions.auto.js"></script>
    Triggers: data-open="#id", data-close, data-toggle. (Prefix them via configure({ prefix: 'ui' })data-ui-open.)
  • React / Vue / anything — no library JS needed. Bind data-state to your own state and handle clicks your way. Optionally import pure helpers: import { open, close, trapFocus, lockScroll } from './src/js/interactions.js'.

Modal — .modal-overlay + .modal

Mark the overlay data-overlay so the JS applies scroll-lock + focus-trap + backdrop-close.

<button class="btn" data-open="#dlg">Open</button>
<div class="modal-overlay" id="dlg" data-state="closed" data-overlay>
  <div class="modal" role="dialog" aria-modal="true">
    <h3 class="modal__title">Title</h3>
    <p class="modal__body">Body…</p>
    <div class="modal__actions"><button class="btn btn--ghost" data-close>Close</button></div>
  </div>
</div>

Dropdown — .dropdown

<div class="dropdown" data-state="closed">
  <button class="btn" data-toggle>Menu <span class="dropdown__caret"></span></button>
  <div class="dropdown__menu">
    <a class="dropdown__item" href="#">Profile</a>
    <div class="dropdown__divider"></div>
    <a class="dropdown__item" href="#">Sign out</a>
  </div>
</div>

Avatar — .avatar

Image or initials, with --sm/--lg/--square, an .avatar__status dot, and an overlapping .avatar-group.

Accordion — .accordion

Built on native <details> — zero JS. Add name="…" to items to make them exclusive.

<div class="accordion">
  <details class="accordion__item" name="faq" open>
    <summary class="accordion__head">Question</summary>
    <div class="accordion__body">Answer…</div>
  </details>
</div>

Toast — .toast

Show imperatively with the JS helper (auto-creates the container, auto-dismisses):

import { toast } from './dist/interactions.mjs';
toast('Changes saved.', { type: 'success', timeout: 4000 });

Pagination — .pagination

.pagination__item for pages/arrows; --active (skin-aware) / --disabled. Variants: .pagination--joined (button-group style, shared borders) · .pagination--sm. Use .pagination__ellipsis for gaps in long ranges.


Distribution

npm run build produces everything into dist/ — Sass compiles the CSS (expanded + minified per skin), PostCSS/autoprefixer adds vendor prefixes (targets from the browserslist field), and esbuild bundles the JS.

CSS — one file per skin, expanded and minified:

| Skin | Expanded | Minified | |------|----------|----------| | glass (default) | dist/aura.css | dist/aura.min.css | | flat | dist/aura-flat.css | dist/aura-flat.min.css | | neu | dist/aura-neu.css | dist/aura-neu.min.css |

JS (optional behaviour layer):

| File | Use | |------|-----| | dist/interactions.mjs / .min.mjs | ESM — import { open, toast } from '@oleksandr-94/aura-css' (frameworks, <script type=module>) | | dist/interactions.global.min.js | Minified IIFE — <script src> exposes window.Aura + auto-wires triggers | | dist/interactions.d.ts | TypeScript types |

The global name is esbuild's --global-name flag — rename freely; the DOM contract is unaffected.

Package entry points (exports):

import { open, toast } from '@oleksandr-94/aura-css';        // JS (ESM) + types
import '@oleksandr-94/aura-css/css';                          // glass CSS  (also: @oleksandr-94/aura-css/css/min)
import '@oleksandr-94/aura-css/flat';                         // flat skin  (also: @oleksandr-94/aura-css/flat/min)
import '@oleksandr-94/aura-css/neu';                          // neu skin
// SCSS source for custom builds:  @use '@oleksandr-94/aura-css/scss' with ($skin: flat, $prefix: 'ui');

Accessibility

  • Reduced motion — Aura component transitions/animations are neutralised under prefers-reduced-motion: reduce (in the last cascade layer, so it never touches the host app's own motion).
  • Keyboard — Modal: focus-trap, Esc, focus return. Dropdown: //Home/End over items, Esc, aria-expanded, focus return. Tabs: / roving focus.
  • Screen readers — Toasts announce via an aria-live="polite" region (errors use role="alert"); a .sr-only helper is provided for visually-hidden labels.
  • Focus visible — every interactive component has a :focus-visible ring.

Browser support

Aura targets an evergreen baseline (Chrome/Edge 111+, Firefox 113+, Safari 16.4+), which is required for:

| Feature | Used for | If missing | |---------|----------|------------| | color-mix() | tints, hovers, neu shadows | no practical polyfill — hard requirement | | Cascade layers (@layer) | override control | hard requirement | | backdrop-filter | glass skin only | glass loses blur (film stays); use flat/neu |

These features are the same generation, so there is no meaningful fallback to add — the baseline is documented rather than polyfilled. Non-glass skins (flat, neu) have the lightest requirements.


Architecture

aura/
├── src/                  # library source
│   ├── _config.scss      # the 3 levers: $prefix, $skin, $themes
│   ├── _functions.scss   # auto on-colour (contrast) derivation
│   ├── _tokens.scss      # emits static scales + generates every theme
│   ├── _surface.scss     # skin dispatcher: surface() / field()
│   ├── skins/            # _glass · _flat · _neu
│   ├── base/_reset.scss  # minimal reset in @layer base
│   ├── components/       # _button · _card
│   ├── _layers.scss      # cascade-layer order
│   └── index.scss        # entry (default = glass)
├── builds/               # build presets (recipes): flat.scss …
├── dist/                 # compiled CSS output
├── demo/                 # live demo page
└── package.json
  • Cascade layers — all Aura CSS lives in @layer base, components; your app's own unlayered styles always win, so overriding is easy.
  • Coexistence — set $prefix to avoid class clashes with other libraries. With Tailwind v4 you can also map Aura's role variables into @theme to share one palette.

Testing

Unit tests cover the JS behaviour layer (interactions.js) with Vitest + happy-dom:

npm test          # run once
npm run test:watch

Covered: open/close/toggle, mount() trigger delegation, activateTab, ref-counted scroll-lock, toast() lifecycle, and prefix config.

Not yet (roadmap)

  • Components: breadcrumbs, menu/sidebar, steps, skeleton, stat, drawer, popover.
  • More prebuilt themes (the token system supports them cheaply).
  • Visual-regression tests (Playwright over the docs, per skin × theme) + a11y audit (axe).
  • RTL audit · prefers-color-scheme auto-theme · per-component CSS imports (tree-shaking).