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

@webority/theme

v0.50.1

Published

Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.

Readme

@webority/theme

Webority's internal design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. Consumed by @webority/ui-react and Webority.Ui.Razor.

Published publicly so npm install needs no private registry. Built for Webority products; no support commitment outside them.

Bias: thin layer on Bootstrap. Shared identity + control language + hard gaps — not a second framework and not every product's marketing site.

Use

Compile the partials with your brand values, in this order — the order is load-bearing:

@import "your-brand/variables";                // your $primary, fonts, etc.
@import "@webority/theme/scss/variables";      // library defaults (yours win)
@import "bootstrap/scss/bootstrap";
@import "@webority/theme/scss/tokens";         // --wui-* tokens
@import "@webority/theme/scss/wui-aliases";    // --color-* / --radius-* aliases
@import "@webority/theme/scss/components";     // portal / control chrome
@import "@webority/theme/scss/wui-components";
// Optional — marketing sites only (heroes, section-y, pill CTAs, reveal motion):
// @import "@webority/theme/scss/marketing";
// Optional, only if the site renders the phone input or country picker (see Country flags):
// @import "@webority/theme/scss/flags";

Importing part of the core chain is a bug: skipping wui-aliases leaves every --color-* reference unresolved.

The primary as text: --wui-primary-ink

A brand primary is chosen as a fill. A light one fails WCAG AA as text: an orange such as #E8501E measures 3.75:1 on white. So the theme keeps two colours:

  • --wui-primary (--color-primary): fills, borders, focus rings, the selected-day disc.
  • --wui-primary-ink (--color-primary-ink): every link, tab label, page number, selected option, avatar initial and primary-coloured icon. It is $primary darkened in 2% steps until it clears 4.5:1 on white, the page, the muted and elevated fills and the brand's own soft tint, so it equals $primary when the primary already clears AA. Orange #E8501E gets #BA4018 (5.48:1 on white).
  • --wui-primary-text (--color-primary-text): the label ON a solid primary fill, white when white clears 4.5:1 on the primary, near-black otherwise.

All three derive from $primary at compile time, so setting $primary (or wui-brand-primary) is enough. A product can set $wui-primary-ink / $wui-primary-text before the theme's variables, or --wui-primary-ink / --wui-primary-text at runtime. A brand layer that re-points --wui-primary at runtime must set --wui-primary-ink and --wui-primary-text beside it. Dark mode uses the lifted --wui-dark-primary for the ink. check-contrast proves the derivation for an orange, an amber and a sky test brand and fails any theme rule that colours text with --color-primary.

Country flags are opt-in

The 245 .wui-flag-* rules are about 187 KB raw (38 KB Brotli) of inlined SVG, so wui-components does not include them. A site that renders AppPhoneNumberInput, <app-phone-number-input>, AppCountrySelect or <app-country-select> adds them once:

  • Your own SCSS: @import "@webority/theme/scss/flags"; after wui-components. Import it once; legacy @import does not dedupe, so a second import doubles every flag rule.
  • Precompiled CSS: link @webority/theme/css/flags (dist/webority-flags.css). The Razor package serves the same file at _content/Webority.Ui.Razor/css/webority-flags.css.
  • Per page: on a site with one phone field, link the flags stylesheet only on the pages that render it. Without it every flag is an empty grey box, and nothing reports the cause.

Purging unused CSS (PurgeCSS)

A consuming site's PurgeCSS pass reads its own pages, but Webority.Ui.Razor ships as a DLL and the React and elements packages ship as bundles, so the markup they render at runtime is invisible to it. @webority/theme therefore ships purge-safelist.json, generated by scripts/gen-purge-safelist.mjs at theme build time from the tag helpers, custom elements, React components and the compiled CSS:

  • classes: every theme class the library's own markup names.
  • patterns: regular-expression sources (^btn-, ^wui-flag-, ...) for classes the library builds from a prefix at runtime, where the tail is only known in the browser.

A Razor site already installs @webority/theme for its SCSS build, so the file is in its node_modules. Wire it into PurgeCSS (purgecss.config.cjs, run after sass and before the file is committed):

const { classes, patterns } = require("@webority/theme/purge-safelist.json");

module.exports = {
    content: ["Pages/**/*.cshtml", "wwwroot/js/**/*.js"],
    css: ["wwwroot/css/site.css"],
    safelist: {
        standard: [...classes, ...patterns.map((p) => new RegExp(p))],
        // Bootstrap state classes and attribute selectors the library toggles at runtime.
        greedy: [/^data-bs-/, /^wui-/],
    },
};

Add your own safelist entries for classes your page scripts add. PurgeCSS failures are silent, so compare every page before and after a purge.

$secondary is quiet grey (de-emphasised text/buttons), not a second brand colour — intentional shared semantics (see _variables.scss).

dist/webority-theme.css is a precompiled default-brand portal build (no marketing partial). Prefer the SCSS chain above for brand-exact output.

Signal brand layer (opt-in)

dist/webority-brand-signal.css (export @webority/theme/css/brand-signal, source scss/_brand-signal.scss) re-points the theme to the Webority company palette: violet #4A0FBF primary, ink #16093A headings, tint #F1EBFF, pink #B517C8 for unread counts only, muted text #6A6A6A, a dark set on pure black #000 with neutral grey surfaces and violet #A98BFF kept for accents, and the Webority Serif 600 face for page, card, modal and empty-state titles. It is never imported by the default theme (check-theme-exports asserts the default dist has no #4A0FBF).

Load it once, after the theme: React import "@webority/theme/css/brand-signal"; Razor _content/Webority.Ui.Razor/css/webority-brand-signal.css; SCSS @import "@webority/theme/scss/brand-signal" after the components. Dark mode is emitted under [data-theme=dark], [data-bs-theme=dark] and the data-theme=auto media query. Contrast of every brand text and surface pair is held at 4.5:1 by check-contrast. _brand-signal.scss owns brand values only; no component CSS.

Dark mode

The theme ships with two built-in dark-mode support patterns:

Manual toggle (production)

Set data-theme="dark" and data-bs-theme="dark" on <html> to flip both WUI and Bootstrap native components together. The dark token remap maps all --color-* aliases onto dark hexes, and Bootstrap's utility classes (.text-body, .bg-secondary) and components respond via the [data-bs-theme="dark"] selector that Bootstrap provides. Pair the attributes—setting one without the other leaves part of the system on the wrong palette.

Use AppThemeToggle (React) or <app-theme-toggle> (Razor) rather than wiring the attributes by hand: it offers System, Light and Dark, stores the choice in localStorage, writes both attributes, and announces wui:theme:change. Nothing stored means System (data-theme="auto"), so a page follows the device until someone picks a theme. Put the before-paint script in <head> on every page, before the stylesheets, so the stored theme lands before first paint: <app-theme-script storage-key="theme" /> on Razor, the inlined output of themeBootScript("theme") from @webority/ui-react on React.

Automatic detection

Set data-theme="auto" on <html> to detect the system preference via @media (prefers-color-scheme: dark) and apply the dark remap automatically. The toggle's System choice is this mode; it also writes the resolved theme to data-bs-theme and keeps it in step when the device changes.

Implementation

Both patterns use the same 60-line dark-token SCSS mixin (defined in _wui-aliases.scss), so the compiled dark CSS is byte-identical whether toggled manually or via system preference.

Token remap includes:

  • Surfaces & text — --color-bg, --color-border, --color-text*
  • Bootstrap RGB triplets — --bs-body-bg-rgb, --bs-body-color-rgb, etc., so .bg-body / .text-body utilities flip
  • Brand & status colors — primary, success, danger, warning, info (mapped to readable values on dark surfaces)
  • Scrim — overlay dimming adjusted for dark backgrounds
  • Component shadows — slider thumbs, badge accents, etc. re-mapped for visibility

Marketing sites ship light-only (the scss/_marketing.scss partial is opt-in on portals and omitted on marketing); dark mode is a portal feature, not a marketing requirement.

Public CSS class vocabulary

Theme-owned class names follow one rule going forward. Enforced by scripts/check-scss-class-prefix.mjs (frozen legacy allowlist + fail on new bare names).

| Class kind | Allowed for new chrome? | Notes | |---|---|---| | Bootstrap public classes (.btn, .form-control, .card, …) | Yes | Restyle Layer A only — do not fork a second kit | | .wui-* | Yes — default | All new library chrome | | .app-* | Legacy only | Shell / host wrappers already shipped; prefer .wui-* for anything new | | Bare generics (.input, .search-select, .datepicker, .tbl-*, …) | Legacy only | Frozen allowlist; no new top-level bare selectors | | Marketing bare (.section-y, .btn-pill, …) | Opt-in _marketing.scss only | Documented marketing vocabulary; portals omit the import |

Renames of legacy bare names are BREAKING: remove the old class, list the new name in CHANGELOG.md, no dual-class shim. Consumers update markup on the bump.

Library vs product ownership

Three layers. Do not collapse product art direction into the shared package.

| Layer | Owner | What | |---|---|---| | A — Bootstrap | Bootstrap | Grid, utilities, reboot, structure of .btn / .form-control / .modal / offcanvas | | B — Shared UI system | this package + App* | Brandable tokens, one field/selection skin, portal shell/table/modal, hard interactive, React↔Razor parity | | C — Product / marketing | Each product | Brand hexes, page layouts, heroes/motion, domain components, router/auth/content |

Products own (do not re-implement in the library)

  • Brand values — $primary / --wui-primary* (and related) via pre-bootstrap variables or runtime CSS; use wui-brand-primary when setting primary once
  • Marketing art direction — heroes, section rhythm, marquee, scroll reveals, page entry fades, marketing-only button shapes (pill / ghost-dark), wide marketing canvas
  • Screens and domain UI — feature layouts, status→label maps, RoleBadge-style domain components
  • App infrastructure: routing, auth, i18n copy, rich text, API (charts are the library's AppChart)

Products must not own (deviation)

  • Forked AppButton / AppSelect / theme hex tables
  • A second select/date/phone kit (Radix, react-day-picker, etc.)
  • A second styling system (Tailwind, MUI, hand-rolled .btn kit)
  • Per-product copies of shell/table/modal chrome

Ship rule for new PRs into this package

| Proposed change | Put it here if… | Put it in the product if… | |---|---|---| | Bootstrap $ or class restyle | Fixes shared a11y, alignment, or rebrand | Looks cool on one landing page | | New CSS in theme | Both React and Razor portals need the same look | Marketing-only motion or section art | | New App* / <app-*> | ≥2 products need it, or Bootstrap fails, or parity requires it | One screen / one domain | | New token | Shared semantic meaning (status, surface, control height) | One brand's one-off hex |

What stays in the library (do not move to products)

Anti-over-correction checklist. When thinning the package (e.g. marketing extract), keep:

Tokens & system (scss/_variables, _tokens, _wui-aliases)

  • Theme colors + wui-brand-primary; body/text/surface/border ramps (incl. AA tertiary)
  • Radius ladder that differs from Bootstrap; spacer map extensions (6+)
  • Status ramps: fills and -text / danger-strong for AA
  • Control heights (--wui-control-height*); flat in-page elevation; overlay shadows only
  • Z-index scale (--wui-z-*); dark-mode --bs-* / RGB bridges
  • Focus: no input glow ($input-focus-box-shadow: none); brand border as focus cue in post-CSS

One skin on Bootstrap classes (not a second kit)

  • Fields: .form-control / .form-select chrome; .input / .select / .textarea are aliases for size/shell hooks
  • Selection: .form-check-input glyph; .wui-check / .wui-radio are aliases (table select compact under .wui-select-cell only)
  • Buttons: tokenized primary / danger / outline / ghost / disabled; shared control height on default size
  • Dropdown, card surface/radius, table cell padding via SCSS vars where specificity requires it

Hard interactive & portal patterns (packages outside this folder, same monorepo)

  • @webority/ui-elements + App* wrappers: select, multiselect, autocomplete, phone, date, daterange, OTP, command palette
  • Portal: AppShell, AppDataTable / ledger, AppModal / AppConfirmDialog / AppCommandPalette, toast contract, AppSidebarMenu, AppThemeToggle
  • React↔Razor parity gates and unit tests for catalogue components

Marketing (opt-in — not default)

Editorial helpers live in scss/_marketing.scss, imported as @webority/theme/scss/marketing after the core chain. Includes section-y, eyebrow, h-display, hero/CTA gradients, btn-pill / btn-ghost-dark, link-wipe, container-cloves, hero-wash, page/stagger/reveal motion, marquee, section content-visibility. Portals omit this import. Marketing sites that used these classes must add the import.

Catalogue tiers

Keep shipping all of these; classify for when to use, not for removal:

| Tier | Components | Library? | Notes | |---|---|---|---| | Portal core | Shell, DataTable, fields/selects, Modal, Confirm, Tabs, Card, Alert, Badge, StatusBadge, Stepper, File*, Date*, Phone, OTP, Toast, Empty/Skeleton/Spinner | Yes — never product-fork | Daily product UI | | Portal display | AppKpiTile, AppInfoList, AppTimeline, AppSplash, AppBanner, AppBreadcrumbs, AppProgress, AppMeter, AppChart | Yes | Shared across portals | | Commercial / plan UI | AppPricingCard, AppCompareTable, AppResourceCard | Yes — keep | Pricing pages and in-app plan/billing. Editorial shape, not a second design system. Moving them to products would break React↔Razor parity. | | CSS marketing only | section-y, btn-pill, reveal, marquee, … | Opt-in partial | Not App*; see marketing import above |

Do not remove PricingCard / CompareTable / ResourceCard without a multi-product usage audit outside this monorepo. Inventory conclusion: keep in library, document tier.

Squircle policy

corner-shape: squircle is applied on many controls (buttons, fields, chips, menus, shell) as progressive enhancement:

  • Supporting browsers get slightly smoother corners; others keep plain border-radius.
  • Not a cross-browser design contract; not a WCAG requirement.
  • Missing squircle is not a bug.

Policy: keep on shared control surfaces for consistent geometry; do not strip globally. Optional .rounded-squircle remains an opt-in utility. Products that want pure arcs only override locally — do not fork App*.