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

@mukundakatta/designlint

v0.2.0

Published

UI/UX design linter for HTML/CSS: accessibility, contrast, spacing, touch targets, and responsive design.

Readme

DesignLint

npm CI License: MIT

An accessibility and design linter for HTML/CSS. Catches low-contrast text, tiny touch targets, skipped heading levels, unlabeled form controls, missing alt text, and more — with autofixes for the mechanical ones.

Runs as a CLI, a GitHub Action (PR annotations + SARIF for Code Scanning), or a programmatic API.

$ designlint index.html
index.html
  line 12  error   color-contrast
    Contrast ratio 1.61:1 is below WCAG AA minimum of 4.5:1.
    in: <p style="color: #ccc; background-color: #fff">
    tip: Darken the foreground or lighten the background.
  line 18  error   image-alt-text
    <img src="hero.png"> is missing alt attribute.
    fix: Mark image as decorative (alt="" role="presentation")
  line 21  warning link-rel-noopener
    <a href="..." target="_blank"> is missing rel="noopener".
    fix: Add rel="noopener noreferrer" to target="_blank" link

  3 issues (2 errors, 1 warning). Score 79/100.

Install

npm install -g designlint

Or one-off without installing:

npx designlint path/to/page.html

Usage

CLI

designlint '<inputs...>' [options]

| Flag | Description | |---|---| | --format text\|json\|sarif\|github | Output format (default text) | | --fix | Apply autofixes in place | | --rule <id...> | Run only the listed rule IDs | | --config <path> | JSON config file | | --fail-on error\|warning\|info\|never | Exit nonzero threshold (default error) | | --output <path> | Write output to a file | | -v, --version | Print version |

Inputs can be file paths, globs, or URLs:

designlint index.html
designlint 'src/**/*.html'
designlint https://example.com --format sarif > results.sarif
designlint index.html --fix                       # apply autofixes
designlint index.html --rule color-contrast       # single rule

GitHub Action

# .github/workflows/designlint.yml
name: DesignLint
on: [pull_request]
permissions:
  contents: read
  pull-requests: write
  security-events: write
jobs:
  designlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: MukundaKatta/[email protected]
        with:
          paths: 'src/**/*.html'
          fail-on: error
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: designlint.sarif

The action:

  1. Emits inline GitHub annotations on the PR diff (the ::error/::warning style you see under "Files changed").
  2. Writes SARIF 2.1.0 so you can upload it to Code Scanning and get persistent findings in the Security tab.
  3. Fails the job based on --fail-on when issues meet the threshold.

Programmatic

import { DesignLinter, applyFixes } from "designlint";

const linter = new DesignLinter({
  rules: {
    colorContrast: { minRatio: 4.5 },
    buttonSize: { minWidthPx: 44, minHeightPx: 44 },
    spacingConsistency: { baseUnit: 8 },
  },
});

const html = await fetch("https://example.com").then((r) => r.text());
const report = linter.lint(html);

console.log(report.summary);
console.log(report.issues);
console.log(report.score);

const { output, appliedCount } = applyFixes(html, report.issues);

Rules

| ID | Checks | Severity | Autofix | |---|---|---|---| | color-contrast | WCAG AA contrast ratio (4.5:1 normal, 3:1 large text). Handles rgba alpha composition. | error | no | | font-size-minimum | Font sizes below 12px are hard to read. | warning | no | | spacing-consistency | Margin/padding/gap should snap to your base unit (4 or 8). | info | no | | button-size | Touch targets must be at least 44x44 (WCAG 2.5.5). | error | no | | heading-hierarchy | Headings should not skip levels (h1 -> h3). | warning | no | | image-alt-text | Every <img> needs meaningful alt, or explicit decorative marking. | error | yes | | viewport-meta | Full documents should include a responsive <meta name="viewport">. | warning | yes | | link-rel-noopener | target="_blank" links need rel="noopener". | warning | yes | | form-label | Every <input>/<select>/<textarea> needs a label, aria-label, or wrapping <label>. | error | no | | duplicate-id | id attributes must be unique per document. | error | no | | responsive-images | Flags <img> missing srcset or loading="lazy" hints. | info | no |

All rules read both inline style="..." attributes and declarations from <style> blocks inside the same document. Simple class/id/tag selectors are supported; descendant combinators and pseudo-classes are ignored.

Configuration

Pass a JSON file with --config:

{
  "rules": {
    "colorContrast": { "enabled": true, "severity": "error", "minRatio": 4.5, "minRatioLarge": 3.0 },
    "spacingConsistency": { "enabled": true, "severity": "info", "baseUnit": 8 },
    "responsiveImages": { "enabled": false, "severity": "off" }
  }
}

Every rule accepts enabled (boolean) and severity (error | warning | info | off). Rule-specific knobs are listed in the rules table above.

Output formats

  • text (default) — human-readable, colorized for TTY, clustered per file.
  • json — structured results for piping into other tools.
  • sarif — SARIF 2.1.0; upload to GitHub Code Scanning for inline PR annotations and Security tab surface.
  • github::error/::warning/::notice workflow commands. GitHub Actions turns these into inline PR file annotations.

Architecture

HTML input
  -> parse5 (with source locations)     -> DOM
  -> css-tree (<style> + inline)        -> property maps
    for each element:
      effective style = <style>-block decls + inline style (inline wins)
      each rule walks elements + reports LintIssue[] (with optional Fix offsets)
  -> scorer + formatter

parse5 gives us byte offsets for every open tag, which is what makes autofixes safe: we splice replacements at known-good positions, never regex-rewrite.

Development

npm install
npm run build
npm test
npm run dev       # tsc --watch

Run the CLI from source without building:

npx tsx src/cli.ts examples/dirty.html

License

MIT