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

@servicenow/eslint-plugin-aiux-style

v0.4.0

Published

ESLint rules for AIUX styling conventions — promote Tailwind/DaisyUI utilities and design tokens over inline styles and hardcoded colors. Static-analysis companion to the runtime style audit panel.

Readme

@servicenow/eslint-plugin-aiux-style

ESLint rules that keep ServiceNow scoped apps built on Karuna using DaisyUI + Tailwind correctly — promoting aiux--prefixed DaisyUI component classes, Tailwind utilities, and design tokens over inline styles, hardcoded colors, and inaccessible DaisyUI "hacks". Static-analysis companion to the runtime style audit panel (components/audit-panel).

All rules:

  • Report at the precise source location of the offending token/declaration (not the template's opening backtick).
  • See through ${…} interpolations — a class token or attribute that spans an interpolation (class="btn ${active ? 'on' : ''}") is still analyzed.

Install

pnpm add -D @servicenow/eslint-plugin-aiux-style

Usage

import aiuxStyle from '@servicenow/eslint-plugin-aiux-style';

export default [...aiuxStyle.recommended];

Opt into higher-signal, Tailwind-forward guidance with strict:

export default [...aiuxStyle.strict];

Or build your own from the rules export:

export default [
  {
    plugins: {'@servicenow/aiux-style': {rules: aiuxStyle.rules}},
    rules: {
      '@servicenow/aiux-style/no-inline-styles': 'warn',
      '@servicenow/aiux-style/no-unsupported-aiux-daisy-class': 'error'
    }
  }
];

Configs

recommended

| Rule | Severity | What it catches | | --------------------------------- | -------- | ------------------------------------------------------------------------ | | no-unsupported-aiux-daisy-class | error | aiux- DaisyUI classes Karuna doesn't safelist (won't render) | | no-daisyui-tooltip-hack | error | CSS-only data-tip tooltips (inaccessible) | | no-collapse-input-hack | error | hidden checkbox/radio collapse/accordion (inaccessible) | | require-aiux-daisy-prefix | warn | unprefixed DaisyUI component classes | | no-inline-styles | warn | style="…" attribute literals | | no-hardcoded-colors | warn | hex / rgb() / hsl() / oklch() colors and Tailwind arbitrary colors | | no-semantic-color-opacity | warn | opacity modifiers on semantic colors (bg-info/10) | | no-filter-input-hack | warn | DaisyUI aiux-filter component (inaccessible, unsafelisted) | | no-modal-div-hack | warn | <div class="aiux-modal"> instead of <dialog> / <aiux-modal> | | prefer-disabled-attr | warn | aiux-btn-disabled class without a real disabled state | | prefer-skeleton-loader | warn | content loading spinners (use aiux-skeleton) |

strict

Everything in recommended, plus heuristic "you hand-rolled a component" rules (all warn):

| Rule | What it catches | | ------------------------------ | --------------------------------------------------------- | | prefer-aiux-button | custom CSS button styling → aiux-btn | | prefer-aiux-form-control | custom input/textarea/select CSS → aiux-input/… | | prefer-aiux-card | custom card/panel/tile CSS → aiux-card | | prefer-aiux-badge | custom badge/pill/tag/chip CSS → aiux-badge/aiux-pill | | prefer-aiux-tabs | custom tablist/tab CSS → aiux-tabs/aiux-tab | | no-local-design-token-family | local/legacy token families (--sn-*, --now-*) |

The DaisyUI class contract (generated)

require-aiux-daisy-prefix and no-unsupported-aiux-daisy-class are backed by rules/daisy-safelist.generated.js — the exact set of aiux-* classes Karuna's prebuilt bundle emits, generated from components/core/src/styles/tailwind.css by scripts/generate-daisy-contract.mjs and guarded by test/daisy-safelist.invariant.test.js. Because "supported" is read straight from the safelist, the contract can never contradict ground truth.

Regenerate after changing the Karuna safelist:

pnpm --filter @servicenow/eslint-plugin-aiux-style generate

Rules

no-inline-styles (warn)

Flags style="…" literals in html`…` templates. CSS-custom-property-only values (style="--x: 12px") and fully dynamic bindings (style=${styleMap(...)}, style="${expr}") are allowed; an interpolated value with static CSS (style="color: red; ${x}") is flagged. Per-element opt-out via data-style-skip (configurable). Options: {allowPattern, optOutAttr}.

no-hardcoded-colors (warn)

Flags hardcoded colors in color-bearing properties: hex / rgb() / hsl() / oklch() / etc. in inline styles and css`…` blocks, plus Tailwind arbitrary-value color classes (text-[#abc], bg-[rgb(0,0,0)]). css blocks are parsed at declaration boundaries, so snippets are clean and never spill across a rule. Allows currentColor/transparent/var(--token). Option: {optOutAttr} (default data-color-skip).

require-aiux-daisy-prefix (warn)

Karuna configures DaisyUI with prefix: 'aiux-', so DaisyUI component classes must use the aiux- form. Tailwind utilities and custom classes stay unprefixed. A token the component styles itself (a .<token> rule in its own css block) is treated as a custom class and suppressed.

// ✗  html`<button class="btn btn-primary">Save</button>`
// ✓  html`<button class="aiux-btn aiux-btn-primary">Save</button>`
// ✓  html`<div class="flex gap-2 pod-card">…</div>`            // utilities + custom
// ✓  css`.card { … }` + html`<div class="card">…</div>`        // self-styled custom class

no-unsupported-aiux-daisy-class (error)

Flags aiux- classes whose root is a real DaisyUI component but which Karuna does not safelist — they silently won't render. Legacy/renamed classes get a targeted fix-it message.

// ✗  html`<input class="aiux-input aiux-input-bordered" />`  // v5: bordered by default
// ✗  html`<button class="aiux-btn-glass">Save</button>`      // removed in v5
// ✗  html`<nav class="aiux-navbar">…</nav>`                  // not safelisted in Karuna
// ✓  html`<table class="aiux-table aiux-table-zebra">…</table>`  // safelisted → fine

no-daisyui-tooltip-hack (error)

Flags the CSS-only data-tip tooltip (no keyboard/focus/SR support), in static, interpolated (data-tip=${label}), and templated forms. Use the tooltip directive/controller. Bare boolean data-tip (which DaisyUI never renders) is not flagged. Skipped in *.stories.js.

no-collapse-input-hack (error)

Flags hidden <input type="checkbox|radio"> inside an aiux-collapse element (announced as "checkbox"/"radio" instead of a disclosure). Detection scopes to the collapse element's actual content, so intervening elements and either ordering are caught. Use native <details>/<summary> (or <aiux-accordion-group>).

no-filter-input-hack (warn)

Flags the DaisyUI filter component (aiux-filter), whose hidden radio/checkbox children are inaccessible (and which Karuna doesn't safelist). The class is the filter root wherever it appears (typically the wrapping <form>). Use <button class="aiux-pill" aria-pressed>.

no-modal-div-hack (warn)

Flags <div class="aiux-modal"> (no focus trapping / Escape / aria-modal). Use <aiux-modal> or native <dialog class="aiux-modal …">. Skipped in *.stories.js. (Class-toggled modals via classMap on a <div> are not detected.)

no-semantic-color-opacity (warn)

Flags opacity modifiers on DaisyUI semantic colors (bg-info/10, text-success/50). Matches whole class tokens, so custom classes like my-bg-primary/50 are not false-flagged; non-semantic utilities (bg-black/50) are allowed.

prefer-disabled-attr (error-type, warn)

Flags aiux-btn-disabled on a <button>/<input> without a real disabled state. Recognizes native disabled, Lit ?disabled=${x} (boolean attr) and .disabled=${x} (property), and aria-disabled="true" for non-interactive display elements.

prefer-skeleton-loader (warn)

Flags content loading spinners (aiux-loading-spinner, …-dots, …) in favor of aiux-skeleton. A spinner inside a button (the spinner's own element or a true ancestor carries aiux-btn) is allowed; a content spinner that merely follows a sibling button is still flagged. Skipped in *.stories.js.

prefer-aiux-button / -form-control / -card / -badge / -tabs (strict, warn)

Heuristic rules that flag high-confidence hand-rolled component CSS in css`…` blocks (a buttonish/inputish/cardish/badgeish/tabish selector carrying the matching cluster of layout/color/shape declarations) and recommend the corresponding Karuna primitive plus Tailwind utilities.

// ✗ static styles = css`.btn-home { background: …; color: …; border-radius: 8px; padding: …; cursor: pointer; }`
// ✓ html`<button class="aiux-btn aiux-btn-primary">Home</button>`

no-local-design-token-family (strict, warn)

Flags local/legacy design-token families (--sn-*, --now-*) at both their declaration and use sites, in css blocks and inline style. Prefer Karuna/Horizon tokens or Tailwind/DaisyUI utilities. Option: {prefixes}.

Pairing with the runtime audit panel

The audit panel (components/audit-panel/src/audit-panel.js) walks the live DOM at runtime for the same issues. This plugin catches them earlier — in the editor and on CI — so they never reach a render.