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

maillint

v0.1.0

Published

Lint HTML email for client compatibility — locally, no render farm. Flags CSS/HTML that breaks in Outlook (Word engine), Gmail clipping at 102KB, missing image alt/dimensions and more, checked against a bundled caniemail-derived support matrix. Determinis

Readme

📬 maillint

Catch email rendering bugs before you hit send — Outlook breakage & Gmail clipping, linted locally.

npm version bundle size CI types license

You hand-code (or template) an HTML email, send a test to yourself, it looks fine — and then it ships broken in Outlook (which renders with Word's engine and ignores flex, position, background-image, border-radius…) or clipped in Gmail (which truncates anything past ~102 KB, hiding your CTA and unsubscribe link). The only ways to know are a paid render farm (Litmus, Email on Acid) or manually cross-checking every CSS property on caniemail.com.

maillint checks your email HTML against a bundled, caniemail-derived support matrix — locally, deterministically, in one command. No render farm, no API key, no sending.

npx maillint scan newsletter.html
newsletter.html  0/100 (F) · 1.3 KB
  ✗ L9   display: flex            breaks in: Gmail, Outlook (Windows), Outlook.com
  ✗ L11  CSS background-image     breaks in: Outlook (Windows, Word engine)
  ✗ L13  border-radius            breaks in: Outlook (Windows, Word engine)
  ✗ L44  <svg> graphics           breaks in: Gmail, Outlook, Yahoo!, Samsung…
  ✗ L46  <form> interactivity     breaks in: Gmail, Outlook, Outlook.com, Yahoo!
  ⚠ L35  Image without alt text   → add alt, or alt="" for decorative

Why maillint?

  • 🎯 Per-client truth, not guesses. Each finding names the exact clients that break and how to fix it, from a curated matrix modelled on caniemail.com — the same data the pros check by hand, baked in.
  • 🔒 Local & deterministic. No render farm, no upload, no API key. Same input → same output. Runs offline and in CI on every template change.
  • 📏 Gmail clipping is just math. maillint measures the real UTF-8 byte size and warns before you cross the ~102 KB line that hides your call to action.
  • a11y built in. Missing image alt, width-less images (Outlook blows them up), layout tables without role="presentation", missing charset/doctype/lang.
  • 🪶 Zero-dependency core. The library imports nothing at runtime; the CLI adds only cac + picocolors.

Why not just ask an LLM "will this render in Outlook"? Client support is exact, shifting data — a chatbot hallucinates it, can't run in CI, and can't byte-count your template on every commit. maillint is a lookup table, not a vibe.

Install

# run it now, no install
npx maillint scan email.html

# or add it
npm install -g maillint      # global CLI
npm install -D maillint      # CI dependency

Node ≥ 18. Ships ESM + CJS + TypeScript types. Works on raw HTML, or the HTML your MJML/Handlebars/React-Email build emits.

Quick start

maillint scan newsletter.html              # lint one file
cat email.html | maillint scan             # or pipe it
maillint scan ./emails --min-score 80      # CI gate over a folder
maillint scan email.html --clients gmail,outlook-windows   # only the clients you target
maillint scan email.html --md report.md    # Markdown report for a PR
maillint init                              # write maillint.config.json

See examples/sample-report.md for a full report, and examples/clean.html for an email that scores 100/100.

What it checks

| Category | Examples | | -------- | -------- | | Client compatibility | flex/grid, position, max-width, border-radius, box-shadow, background-image, transform/animation, web fonts (@font-face), @import, @media (Outlook), padding on <a>, <form>/<input>/<button>, <svg>, <video> — each mapped to the clients that break | | Gmail clipping | real UTF-8 byte size vs Gmail's ~102 KB clip limit (error over, warning approaching) | | Images | missing alt (clients block images by default), missing explicit width (Outlook scaling) | | Structure & a11y | <!DOCTYPE>, <meta charset>, <html lang>, <title>, role="presentation" on layout tables |

Each finding is a weighted error / warning / info; files roll up to a 0–100 score and an A–F grade you can gate in CI.

Real scenarios

1. CI gate on your email templates. A PR that adds a display:flex hero or a web font fails the build before it reaches a real inbox:

# .github/workflows/email.yml
- run: npx maillint scan ./emails --min-score 85 --md email-report.md

2. Lint your ESP/MJML output. Point maillint at the compiled HTML from React Email, MJML or your ESP to catch what the framework (or a hand-tweak) let through — clipping, a missing alt, an unsupported gradient.

3. Target only the clients you support. A B2B product whose audience lives in Outlook? --clients outlook-windows,outlook-com focuses the report on what actually matters to your recipients.

Configuration

maillint init writes maillint.config.json:

{
  "clients": ["apple-mail-ios", "gmail", "outlook-windows", "..."],
  "ignore": [],            // rule ids, e.g. ["compat.border-radius"]
  "partialSeverity": "info",
  "clipBytes": 102400,      // Gmail clipping threshold
  "clipWarnRatio": 0.9,
  "minScore": 0             // CI gate
}

Library API

import { lintEmail, DEFAULT_CONFIG } from "maillint";

const { findings, bytes } = lintEmail(html, DEFAULT_CONFIG);
for (const f of findings) {
  console.log(f.severity, f.rule, f.unsupported); // e.g. ["gmail","outlook-windows"]
}

Also exported: lintFile, buildReport, parseEmail, the support matrix (CSS_FEATURES, HTML_FEATURES, AT_RULE_FEATURES), CLIENTS, and all types. The core is dependency-free and browser-safe (great for a live playground).

Roadmap

  • 🤖 Optional --ai layer (bring-your-own key) to suggest rewrites for flagged code (e.g. a VML fallback for a CSS background). The core stays 100% offline and deterministic — AI is enhancement only.
  • More matrix coverage (dark-mode prefers-color-scheme, mso- props, AMP4email).
  • Inline <!-- maillint-disable --> comments and per-rule severity overrides.
  • Auto-inlining hints (which <style> rules to inline for stripping clients).
  • A web playground — paste HTML, see the report, nothing uploaded.

💖 Sponsor

maillint is free and MIT-licensed, built and maintained in spare time. If it saved you a broken send (or a render-farm subscription), please consider supporting it:

  • Star this repo — the simplest free way to help others find it.
  • 🍋 Sponsor via Lemon Squeezy — one-time or recurring.

The support matrix is modelled on the excellent community data at caniemail.com. maillint bundles a curated snapshot; it isn't affiliated with caniemail.

License

MIT © maillint contributors