CLI tool
TheOrcDev/shadscan avatar
TheOrcDev/shadscan

A critical overflow finding that adds no points

Deterministic UI audits for shadcn apps, built for your terminal, your CI, and your AI agent.

548 stars14 forksTypeScriptMIT

At a glance

What is it?
shadscan is a static auditor for React shadcn applications that scores UI fundamentals out of 100 with evidence attached to every finding. Its rendered browser check is documented with more precision than its rules, and it can report a critical failure that deliberately does not move the score or the grade.
Who is it for?
Use it as a repeatable floor rather than a verdict on design, and read the two halves of the report separately, since a Not applicable bucket can carry as much weight as a Fixes bucket in a small app. Two things to settle first: pin an exact version in CI, because unqualified commands resolve to the latest stable release and the gate is a single number, and remember that the rendered UI suite reports critical failures without contributing to the 62-rule score.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A critical browser finding that adds no points

The rendered UI check opens an already-running local or deployed app in isolated Chromium pages at two fixed viewports, 320 by 820 for mobile and 1440 by 1000 for desktop. Horizontal overflow is its first check, and it reports a critical failure for a single CSS pixel of overflow, or if the root or body forces a horizontal scrollbar, with likely culprit selectors attached when they can be identified.

Then comes the part that decides how you use it. That check adds no rule, no score and no grade, and it leaves the 62-rule catalog and the existing `mobile-overflow-absent` advisory untouched. So a run can report a critical failure and still print 92 out of 100 with grade A.

The same section is also clear about what it will not do: shadscan does not start or build the target app. You start it yourself, or pass a URL that is already deployed.

The scan calls no model, then offers to launch one

The default scan is described as deterministic and read-only: it does not start the app, edit files, call an AI model, upload source, or require application secrets. Those are strong claims and they hold for the scan itself, which reads a repository and writes a report.

The surrounding tooling then offers to involve an agent. There is a paste-ready remediation plan behind `--prompt`, a handoff that interactive runs put on the clipboard, and `--apply`, which explicitly launches an installed coding agent with the generated plan, with codex given as the example. An interactive scan also ends with a menu offering to add a pre-commit score gate, which writes into your git hooks.

So the boundary is not the tool versus your agent, it is the audit versus the handoff. The audit is model-free by design; what you do with the plan afterwards is a separate decision the README makes easy to make.

Four buckets, and one of them is not applicable

Reports are split four ways, and the fourth bucket is the interesting one. Fixes are high-confidence defects with repository-relative evidence. Decisions are product choices that should be implemented or explicitly waived. Advisories are lower-confidence checks that need rendered or manual verification. Not applicable lists rules excluded because the relevant UI surface is absent.

text
Your shadscan score: [###############-] 92/100 (Grade A)

Categories:
  Foundation:           20/20 (100%)
  Interaction:        12.2/20  (61%)
  States:               20/20 (100%)
  Accessibility:        20/20 (100%)
  Forms and Data Entry: 10/10 (100%)
  Production Polish:    10/10 (100%)

Six categories make up the 100 points, and the fractional 12.2 in Interaction shows partial credit inside a category rather than a whole-point total. A Not applicable result is not a pass and not a failure, which is what keeps a small app from being punished for rules that describe a surface it does not have.

CI gating rests on one flag and one pinned version

The gating example is a single line, and every part of it is load-bearing.

bash
pnpm dlx @shadscan/[email protected] --fail-under 80 --no-interactive --no-roast

The version is pinned because unqualified commands resolve to the latest stable release, which means an unpinned gate can change its verdict when a new version ships rather than when your code changes. The threshold flag fails CI when the complete assessed score falls below the floor, and the wording distinguishes an assessed score from the headline number. `--no-interactive` suppresses the menus and prompts.

Other commands narrow the run rather than widen it: `--category accessibility` audits one category while you investigate, and `--l` lists the workspace packages that were found without scanning anything.

Progress goes to stderr, and one flag is left unexplained

Output discipline is specified closely. Progress checklists are written to stderr, never stdout, and they are suppressed for JSON output, CI, `TERM=dumb`, non-TTY stderr, `--no-interactive`, and prompt output. Redirecting stdout therefore keeps the report clean while progress continues on an eligible terminal.

The interactive finish is a short menu driven by arrow keys and Enter, offering the agent handoff, printing it, launching an installed agent, and adding a pre-commit score gate. The handoff is highlighted first, so one Enter copies it, and Esc keeps only the score.

Two loose ends sit in that section. The CI example passes `--no-roast`, and nothing in the visible documentation describes what roasting is or which output contains it, so the default behaviour of the tool is not fully specified by its own docs. And `--l` is a single letter among flags that are otherwise spelled out.

Redirect handling is the most carefully bounded part

Where shadscan follows a URL is spelled out to the point of naming what it refuses. The target URL is always checked, and `--route` can be repeated to add up to ten paths, each of which must begin with a slash and cannot carry a query string or fragment. When the initial request returns a narrowly validated server-side canonical redirect, shadscan can follow an apex-to-www or www-to-apex host change and an HTTP-to-HTTPS upgrade, then pins the resulting origin for every additional route. For multi-label public suffixes you pass the canonical origin directly.

Everything else stays blocked: other cross-origin redirects, HTTPS downgrades, and client-side cross-origin navigations. Reports use the resolved origin as their target, and human output also names the origin that was originally requested when it changed.

That is a deliberately narrow redirect budget, and it is the clearest statement in the documentation about where the boundary between inspecting and browsing sits.

The website is a Next.js app that depends on its own CLI

The repository is not only a CLI. It contains an app, components, hooks, a lib directory, drizzle and a drizzle config, next.config.ts, vercel.json, playwright.config.ts and vitest.config.ts, and the root package manifest depends on `@shadscan/cli` through the workspace protocol. That is how the hosted scan page runs the same scanner you run locally.

The site's own stack is modern and slightly crowded: Next 16.3.3 with React 19.2.8, drizzle-orm against the Neon serverless driver, Vercel analytics and queue, both radix-ui and @base-ui/react as primitive libraries, cmdk for command menus, marked for markdown, and six @visx packages pinned to a 4.0.1-alpha.0 prerelease. Linting is split three ways, with biome, oxlint and an ultracite config alongside prettier.

Licensing is per package as well: the root ships LICENSE.md while the badge for the CLI points at packages/cli/LICENSE. There is also an action.yml, so the scanner can run as a GitHub Action.

Editorial conclusion

Use it as a repeatable floor rather than a verdict on design, and read the two halves of the report separately, since a Not applicable bucket can carry as much weight as a Fixes bucket in a small app. Two things to settle first: pin an exact version in CI, because unqualified commands resolve to the latest stable release and the gate is a single number, and remember that the rendered UI suite reports critical failures without contributing to the 62-rule score.

Frequently asked questions

What does shadscan check in a shadcn app?

It scores UI fundamentals from 0 to 100 across a catalog of 62 rules grouped into categories such as Foundation, Interaction, States, Accessibility, Forms and Data Entry, and Production Polish, and shows evidence behind every finding.

Does shadscan upload source or call an AI model?

The default scan says it does not start the app, edit files, call an AI model, upload source or require application secrets. The rendered check does open an already-running app in isolated Chromium pages.

How do I gate a pipeline on shadscan?

Pin an exact package version, as in `@shadscan/[email protected] --fail-under 80 --no-interactive --no-roast`, since unqualified commands resolve to the latest stable release. JSON and non-interactive runs stay quiet so stdout stays clean.

What does the rendered UI check report?

Horizontal overflow at fixed viewports of 320 by 820 for mobile and 1440 by 1000 for desktop. It reports a critical failure for a single CSS pixel of overflow or a forced horizontal scrollbar on root or body, and accepts up to ten routes.

How do I get a machine-readable report from shadscan?

Pass --json for a versioned report, or --prompt for a paste-ready remediation plan. --format human, --format json and --format prompt make the choice explicit, and --category audits a single category such as accessibility.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. TheOrcDev/shadscan on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/theorcdev-shadscan.svg)](https://hysenlabs.com/projects/theorcdev-shadscan)