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

postcss-bettera11y

v0.1.1

Published

PostCSS plugin that runs BetterA11y accessibility audits on your CSS.

Readme

postcss-bettera11y

PostCSS plugin that runs BetterA11y on your CSS so you can catch accessibility issues (for example low contrast) in the stylesheet that your build actually emits.

Install

npm install postcss-bettera11y bettera11y postcss

Peer dependencies: bettera11y and postcss.

Usage

Register bettera11yPostcss in your PostCSS config. Place it after any plugins that expand or transform CSS (framework pipelines, @import resolution, etc.) so the audit sees the final declarations.

postcss.config.mjs

import bettera11y from "postcss-bettera11y";

export default {
    plugins: [bettera11y()]
};

With options:

import bettera11y from "postcss-bettera11y";

export default {
    plugins: [
        bettera11y({
            failOnError: false,
            logLevel: "warn"
        })
    ]
};

Using with Tailwind CSS

If you use Tailwind’s PostCSS plugin, list postcss-bettera11y after it so utilities are expanded before the audit:

import tailwindcss from "@tailwindcss/postcss";
import bettera11y from "postcss-bettera11y";

export default {
    plugins: [tailwindcss(), bettera11y()]
};

Tailwind remains your dependency; this package does not ship or require it.

Options

The plugin is a standard PostCSS plugin factory: bettera11yPostcss(options?).

| Option | Type | Default | Description | | ------------- | ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | | failOnError | boolean | false | When true, throws if any diagnostic uses BetterA11y severity "error". (Built-in contrast rule is "warn".) | | logLevel | "error" \| "warn" \| "info" | "warn" | Filters which severities are forwarded to PostCSS warnings. "info" surfaces all diagnostics and a “no issues” line when applicable. | | cache | boolean | true | Skip re-auditing when the CSS string for a given from path is unchanged. | | rules | RuleDefinition[] | [color-contrast] | Override BetterA11y rules (defaults to the color-contrast rule from defaultRules). | | ruleOptions | object | — | Per-rule options passed to audit(). | | normalizers | object | — | Custom normalizers passed to audit(). |

Presets from BetterA11y are re-exported: recommendedPreset, strictPreset, wcagAaBaselinePreset.

Scope and limitations

BetterA11y’s CSS contrast check only considers declaration blocks where both color and background / background-color appear in the same rule. That matches plain CSS and many layered or composed styles, but not the typical atomic-utility pattern where foreground and background utilities compile to different selectors on the same element.

For markup and component-level checks, use vite-plugin-bettera11y (or run audit() on your HTML/TSX sources) alongside this plugin.

Output from utility frameworks often uses nested at-rules; BetterA11y’s flat CSS parser does not walk every nested block. Treat results as a best-effort signal, not a full substitute for manual review or browser contrast tooling.

API

import bettera11yPostcss, { hashContent, normalizePostcssOptions, runCssAudit } from "postcss-bettera11y";
  • Default export — same as bettera11yPostcss (PostCSS PluginCreator).
  • runCssAudit — runs the same CSS audit the plugin uses (returns BetterA11y AuditResult plus error/warning counts).
  • normalizePostcssOptions — merges partial plugin options with the same defaults as the PostCSS plugin (including default color-contrast rules).
  • hashContent — stable SHA-256 hash of a CSS string (used by the plugin’s cache; useful for custom tooling).

Types: BetterA11yPostcssOptions, BetterA11yPostcssNormalizedOptions, BetterA11yPostcssLogLevel.

Stricter contrast thresholds (ruleOptions)

Per-rule options are passed through to BetterA11y’s audit() call. The built-in CSS contrast rule accepts thresholds such as minContrastNormal and minContrastLarge (see that rule’s optionsSchema in the core package).

import bettera11y from "postcss-bettera11y";

export default {
    plugins: [
        bettera11y({
            ruleOptions: {
                "color-contrast": {
                    minContrastNormal: 7,
                    minContrastLarge: 4.5
                }
            }
        })
    ]
};

Custom rules (rules)

By default the plugin audits with BetterA11y’s color-contrast rule only (the same subset runCssAudit uses). Override rules with any RuleDefinition[] from bettera11y or your own definitions.

For raw CSS (format: "css" inside the engine), rules that rely on a DOM document typically emit nothing; contrast (and your own CSS-aware rules) are the natural fit. For authoring rules in general, see the BetterA11y rule authoring guide.

Example: combine the stock contrast rule with a small custom rule that inspects the stylesheet text:

import bettera11y from "postcss-bettera11y";
import { defaultRules } from "bettera11y";

const colorContrast = defaultRules.filter((rule) => rule.meta.id === "color-contrast");

/** @type {import("bettera11y").RuleDefinition} */
const warnOnOutlineNone = {
    meta: {
        id: "demo-outline-none",
        description: "Example: warn when outline is removed in plain CSS.",
        category: "style",
        defaultSeverity: "warn",
        tags: ["custom"],
        wcagAlignment: "heuristic",
        wcagNotes: "Illustrative static check; a real rule would verify compensating :focus-visible styles."
    },
    check({ input }) {
        if (input.format !== "css") {
            return [];
        }
        if (!/outline\s*:\s*none/i.test(input.content)) {
            return [];
        }
        return [
            {
                ruleId: "demo-outline-none",
                severity: "warn",
                category: "style",
                message: "Stylesheet contains outline: none; confirm focus is still visible for keyboard users.",
                remediation: "Pair removals with a visible :focus-visible ring or equivalent."
            }
        ];
    }
};

export default {
    plugins: [
        bettera11y({
            rules: [...colorContrast, warnOnOutlineNone]
        })
    ]
};

Presets (recommendedPreset, strictPreset, wcagAaBaselinePreset)

These are re-exported from bettera11y for convenience. You can pass them as rules, but on CSS-only input most preset rules are markup-oriented and return no diagnostics while still being evaluated. For PostCSS pipelines, the default contrast-only set—or [...colorContrast, …custom] as above—is usually clearer than dropping in a full preset unchanged.

import bettera11y, { recommendedPreset } from "postcss-bettera11y";

export default {
    plugins: [bettera11y({ rules: recommendedPreset })]
};

Programmatic audit without PostCSS

Use runCssAudit when you already have a CSS string (for example from a design-token pipeline) and want the same logic as the plugin:

import { normalizePostcssOptions, runCssAudit } from "postcss-bettera11y";

const css = `.bad { color: #777777; background-color: #888888; }`;

const { result, summary } = await runCssAudit(
    css,
    "virtual/tokens.css",
    normalizePostcssOptions({
        ruleOptions: {
            "color-contrast": { minContrastNormal: 4.5 }
        }
    })
);

console.log(summary, result.diagnostics);

Content fingerprint (hashContent)

import { hashContent } from "postcss-bettera11y";

const a = hashContent(cssString);

The PostCSS plugin uses this (with the input path) when cache: true to skip re-auditing unchanged CSS.

Examples

See examples/README.md. From the repo root, run npm run example:build to build the plugin and exercise the postcss-cli demo (contrast warnings in the terminal).

License

MIT