Skip to content

Taste Lint

Catch AI slop before you ship

For developers shipping UI an agent helped write. Taste Lint flags the copy, type and motion a design reviewer would send back.

Install command for
npx taste-lint@latest init

npx taste-lint@latest init --agent

Try it on your copy

Pick a sample or paste your own. These 8 copy rules load from the CLI’s own rule files and check each line as you type.

Start from a sample

One line is one string. It runs in your browser and nothing leaves the page.

6 findings from 8 rules.

  1. Success message padded with "successfully"

    copywriting-toast-successfullyLine 1

    Successfully saved your changes! 🎉

    Fix: Name the thing that changed in past tense and stop: "Project deleted", not "Successfully deleted the project."

  2. Emoji in interface copy

    copywriting-emoji-in-uiLine 1

    Successfully saved your changes! 🎉

    Fix: Remove the emoji. Headings, descriptions, buttons and labels carry their meaning in words.

  3. Verb buried in a noun

    copywriting-nominalisationsLine 2

    Please make a selection before you continue...

    Fix: Use the verb: decide, analyse, review, help, consider, conclude, assess, evaluate, select, describe.

  4. Three periods instead of an ellipsis character

    typography-ellipsisLine 2

    Please make a selection before you continue...

    Fix: Use the single ellipsis character (U+2026), for example "Saving…".

  5. Software described as thinking, wanting or knowing

    copywriting-anthropomorphismLine 3

    The app thinks your session expired, i.e. you were away too long.

    Fix: Use the precise verb: returns, sends, rejects, creates, deletes, validates, stores, triggers.

  6. Latin abbreviation in running text

    copywriting-latinismsLine 3

    The app thinks your session expired, i.e. you were away too long.

    Fix: Write "for example", "that is" and "and so on". Latin abbreviations trip screen readers and non-native readers.

210 rules. 28 can fail your build.

Taste Lint never fails a build on a model’s opinion. Only a regex or a measured value can block a run. The other 182 rules leave review notes, and the judgement calls among them go to Jev, which answers with a probability.

How a rule gets promoted
Agent instructions7 rules, 0 blocking
  • authoring-approval-every-stepBlanket approval gate blocks routine local workReview note
  • authoring-fixed-agent-quotaWorkflow imposes an unconditional agent quotaReview note
  • authoring-generic-coding-adviceInstruction contains only generic coding adviceReview note
  • authoring-generic-verificationVerification instruction names no observable checkReview note
  • authoring-plan-vague-acceptancePlan acceptance criterion is not observableReview note
  • authoring-instruction-missing-scriptInstruction invokes an undefined package scriptReview note
  • authoring-skill-frontmatterSkill frontmatter is missing or invalidReview note
Copy74 rules, 10 blocking
  • copywriting-anthropomorphismSoftware described as thinking, wanting or knowingBlocks a run
  • copywriting-antithesis-contrastDenies a thing nobody claimed to sharpen a pointReview note
  • copywriting-antithesis-frameSets up a claim by denying one nobody madeBlocks a run
  • copywriting-artificial-balanceForces symmetrical both-sides framingReview note
  • copywriting-bare-confirm-labelGeneric confirmation label obscures a consequential actionReview note
  • copywriting-boilerplate-offerEnds with a generic offer to help or continueReview note
  • copywriting-canned-phrasingUses canned phrases common in assistant responsesReview note
  • copywriting-claim-without-evidenceQuality claimed, nothing the reader could checkReview note
  • copywriting-click-here-linksLink text that names nothingBlocks a run
  • copywriting-conditions-firstAn instruction hides its prerequisite after the actionReview note
  • copywriting-confirmshamingDecline label shames the personReview note
  • copywriting-copula-avoidanceDresses up "is" or "has"Review note
  • copywriting-cta-names-outcomeButton that names an action, not what the reader getsReview note
  • copywriting-dead-linkLink with nowhere to goReview note
  • copywriting-dead-weight-hedgesHedge or throat-clearing phrase the sentence does not needReview note
  • copywriting-em-dash-in-uiSpaced em dash in interface copyReview note
  • copywriting-emoji-in-uiEmoji in interface copyBlocks a run
  • copywriting-empty-state-no-actionEmpty state that names the void but not the first moveReview note
  • copywriting-error-descriptionsAn error explanation provides no recovery guidanceReview note
  • copywriting-excessive-hedgingHedges claims more than the subject requiresReview note
  • copywriting-external-arrow-internal-linkExternal-link arrow on a link that stays on the siteReview note
  • copywriting-fabricated-specificitySuspiciously specific details without grounding or attributionReview note
  • copywriting-filler-wordsIntensifiers and filler that add nothingReview note
  • copywriting-flat-descriptionInformation with no reason for the reader to careReview note
  • copywriting-friction-ctaCall to action that names a commitment without reducing the riskReview note
  • copywriting-generic-audienceBenefit promised to everyone, so it lands on no oneReview note
  • copywriting-generic-framingRelies on generic framing instead of concrete contextReview note
  • copywriting-generic-languageRelies on generic or interchangeable languageReview note
  • copywriting-generic-validationUses vague praise or generic validationReview note
  • copywriting-latinismsLatin abbreviation in running textReview note
  • copywriting-leaked-error-messageLeaked raw error message in UIReview note
  • copywriting-live-region-feedbackAnnounce Status Changes with Live RegionsReview note
  • copywriting-machine-proseSentence shapes that give away generated writingReview note
  • copywriting-mechanism-over-outcomeDescribes the mechanism where the reader needed the outcomeReview note
  • copywriting-meta-commentaryComments on how it is presenting or organising the answerReview note
  • copywriting-missing-reasoning-linkA conclusion with no link to what came beforeReview note
  • copywriting-nominalisationsVerb buried in a nounBlocks a run
  • copywriting-over-explanationExplains straightforward points more than necessaryReview note
  • copywriting-participle-tailSentence ends in a participle tailBlocks a run
  • copywriting-passive-voicePassive voice where the actor is knownReview note
  • copywriting-permission-benefitPermission request states no user benefitReview note
  • copywriting-promotional-docs-voiceMarketing voice inside documentationReview note
  • copywriting-prompt-restatementReads like a restatement of an implied prompt or assignmentReview note
  • copywriting-punctuation-ticsOveruses dashes, colons or parenthetical asides in a patterned wayReview note
  • copywriting-reader-firstHeadline that leads with what the product isReview note
  • copywriting-register-shiftFormal or slangy register breaking the product voiceReview note
  • copywriting-repetitive-transitionsRepeatedly uses formal or formulaic transitionsReview note
  • copywriting-requirements-languageA requirement presented as a recommendationReview note
  • copywriting-searchable-headingsA documentation heading names no topic or actionReview note
  • copywriting-semantic-linksUse Semantic Links for NavigationReview note
  • copywriting-slogan-headingsHeadings built from stacked slogan fragmentsReview note
  • copywriting-structural-symmetryExcessive structural symmetry or formulaic organisationReview note
  • copywriting-success-state-specificSuccess message does not name the completed operationReview note
  • copywriting-tee-upTees up a point instead of stating itBlocks a run
  • copywriting-temporal-wordsWording anchored to the time of writingReview note
  • copywriting-toast-successfullySuccess message padded with "successfully"Blocks a run
  • copywriting-unexplained-actionNames an action that cannot be picturedReview note
  • copywriting-unnatural-polishPolished in a way that suppresses personal textureReview note
  • copywriting-unverifiable-proofUnverifiable proof standing in for real evidenceReview note
  • copywriting-vague-errorError message with no cause and no next stepReview note
  • copywriting-visual-direction-instructionUI instruction depends on visual positionReview note
  • copywriting-long-sentenceSentence over 25 words joins several clausesBlocks a run
  • copywriting-readme-scaffoldREADME still contains scaffold instructionsReview note
  • copywriting-readme-descriptionREADME has no introductory descriptionReview note
  • copywriting-readme-quickstart-elisionQuickstart example contains an unfinished placeholderReview note
  • copywriting-document-empty-headingA section has no contentReview note
  • copywriting-document-heading-orderHeading skips a structural levelReview note
  • copywriting-document-code-fence-languageCode fence has no language tagReview note
  • copywriting-document-broken-local-linkDocument links to a missing local fileBlocks a run
  • copywriting-readme-license-linkREADME license link points to a missing fileReview note
  • copywriting-readme-install-nameREADME installs an old or unrelated package nameReview note
  • copywriting-fact-paddingThe same few facts restated to fill the pageReview note
  • copywriting-idea-repetitionRepeats the same idea without adding new evidenceReview note
  • copywriting-invented-capabilityStates a capability the brief never gaveReview note
Visual craft21 rules, 0 blocking
  • craft-atmosphere-gradientPurple or cyan gradient used as atmosphereReview note
  • craft-decoration-no-roleStacked decorative layers with no roleReview note
  • craft-decorative-ordinalsOrdinal numbers on items that are not a sequenceReview note
  • craft-default-indigoIndigo accent that looks like a template defaultReview note
  • craft-emoji-in-copyEmoji in interface copyReview note
  • craft-faux-product-chromeFaux product chrome wrapping nothingReview note
  • craft-hover-only-affordanceCritical actions hidden behind hover-only affordancesReview note
  • craft-i18n-rtl-untestedLayout uses physical (left/right) instead of logical (start/end) propertiesReview note
  • craft-i18n-untestedHardcoded light-mode token with no dark variant on the lineReview note
  • craft-inline-stylesInline style object or style element in a componentReview note
  • craft-lazy-load-offscreenHero or above-the-fold image marked loading lazyReview note
  • craft-left-accent-borderA coloured left border on cards that are not calloutsReview note
  • craft-token-driftArbitrary values drifting off the themeReview note
  • craft-viewport-scalingViewport explicitly disables user zoomReview note
  • craft-near-duplicate-scaleArbitrary font size within 1px of a declared theme stepReview note
  • craft-raw-colour-classPalette colour with a numeric shade instead of a theme tokenReview note
  • craft-arbitrary-value-classLiteral visual styling worth reviewing for a design tokenReview note
  • craft-interpolated-class-stringClass name built from a template expressionReview note
  • craft-image-dimensions-and-priorityImage has no declared dimensions or styleReview note
  • craft-eyebrow-overuseAn eyebrow over nearly every headingReview note
  • craft-opacity-ladderA ladder of text opacitiesReview note
Engineering36 rules, 0 blocking
  • engineering-catch-rethrowCatch block that only rethrowsReview note
  • engineering-ci-cancels-default-branchConcurrency cancels runs on the default branchReview note
  • engineering-ci-coverage-on-prCoverage collected on every pull requestReview note
  • engineering-ci-full-cloneCheckout fetches full history without needing itReview note
  • engineering-ci-no-concurrencyPull request runs are never cancelledReview note
  • engineering-ci-uncached-installDependencies installed with no cacheReview note
  • engineering-dead-branchBranch that can never runReview note
  • engineering-debug-log-in-uiconsole.log left in a componentReview note
  • engineering-empty-catchError caught and droppedReview note
  • engineering-hardcoded-secretSecret committed in sourceReview note
  • engineering-heavy-dependencyImports a heavy library with a native replacementReview note
  • engineering-insecure-random-tokenMath.random used for a token or secretReview note
  • engineering-non-atomic-writesRelated writes without a transactionReview note
  • engineering-query-in-loopOne query per item inside a loop (N+1)Review note
  • engineering-request-waterfallIndependent awaits run one after anotherReview note
  • engineering-restating-commentComments restate the code under themReview note
  • engineering-sensitive-data-loggedCredential or personal data written to logsReview note
  • engineering-silent-fallbackFailure turned into a default with no recordReview note
  • engineering-stale-feature-flagFeature flag hard-coded to one valueReview note
  • engineering-sync-io-in-handlerBlocking sync call inside a request handlerReview note
  • engineering-template-residuePlaceholder or debug residueReview note
  • engineering-test-focusedFocused test left in (.only)Review note
  • engineering-test-mock-echoTest only checks that a mock was calledReview note
  • engineering-test-render-onlyComponent test renders and checks presence onlyReview note
  • engineering-test-self-comparisonTest compares a value with itselfReview note
  • engineering-test-skippedSkipped or placeholder testReview note
  • engineering-token-in-local-storageAuth token stored in localStorageReview note
  • engineering-unbounded-readList read with no limit or paginationReview note
  • engineering-test-no-assertionTest asserts nothingReview note
  • engineering-test-snapshot-onlyTest asserts only a snapshotReview note
  • engineering-untested-moduleLogic the tests barely runReview note
  • engineering-oversized-fileFile is over 1000 linesReview note
  • engineering-type-bypassType check bypassedReview note
  • engineering-duplicated-blockThe same block appears twice in one fileReview note
  • engineering-commented-out-codeCommented-out codeReview note
  • engineering-unused-dependencyDependency nothing in the package importsReview note
Interaction29 rules, 1 blocking
  • interaction-a11y-color-only-meaningAvoid Color-Only MeaningReview note
  • interaction-a11y-data-table-semanticsMark Up Data Tables With Real Table SemanticsReview note
  • interaction-a11y-disabled-control-tooltipDo not put tooltips on disabled controlsReview note
  • interaction-a11y-icon-controls-labeledLabel Icon-Only ControlsReview note
  • interaction-a11y-media-captionsCaption Video and Transcribe AudioReview note
  • interaction-a11y-semantic-html-firstPrefer Native Semantics Before ARIAReview note
  • interaction-a11y-tooltip-no-interactiveHover tooltips must not contain interactive contentReview note
  • interaction-broken-focus-trapModal without working focus trapReview note
  • interaction-dont-block-paste-imeDo Not Block Paste or IME InputReview note
  • interaction-error-associationAssociate and Announce Form ErrorsReview note
  • interaction-focus-visiblePreserve Visible Focus StatesReview note
  • interaction-inline-errors-first-focusShow Inline Errors and Focus the First Invalid FieldReview note
  • interaction-input-affix-hit-targetOverlay input affixes on the fieldReview note
  • interaction-keyboard-operableEnsure Full Keyboard OperabilityReview note
  • interaction-labels-and-autocompleteLabel Inputs and Set Autocomplete MetadataReview note
  • interaction-lost-data-on-errorForm data lost on validation errorReview note
  • interaction-mobile-input-font-sizeKeep Mobile Input Text at Readable SizeReview note
  • interaction-no-disable-while-submittingSubmit button not disabled while pendingReview note
  • interaction-no-empty-stateEmpty state has no call to actionReview note
  • interaction-no-error-boundaryClient async tree without a local error boundaryReview note
  • interaction-no-error-stateAsync UI failure has no local recoveryReview note
  • interaction-no-suspense-boundaryAsync server component without Suspense boundaryReview note
  • interaction-not-restoredFocus not restored after a modal, sheet or popover closesReview note
  • interaction-on-dynamic-contentRoute change without focus management or a live regionReview note
  • interaction-optimistic-without-rollbackOptimistic update without rollback on server rejectReview note
  • interaction-out-of-order-responsesInput handler fetches with no cancellationReview note
  • interaction-target-sizeButton sized below the touch target floorReview note
  • interaction-use-form-status-misuseuseFormStatus called in same component as form (silent bug)Review note
  • interaction-a11y-image-alt-textImage is missing an alt attributeBlocks a run
Motion12 rules, 7 blocking
  • motion-pulse-dotA pulsing dot with nothing live behind itReview note
  • motion-will-change-misusewill-change promotes a property the compositor cannot animateReview note
  • motion-ease-in-on-transitionTransition eases in, so it starts slow while the user is watchingBlocks a run
  • motion-linear-easing-on-transitionLinear easing on motion that is not a spinner or marqueeBlocks a run
  • motion-duration-over-300msTransition longer than 300ms on product UIBlocks a run
  • motion-transition-alltransition-all animates every property, layout includedBlocks a run
  • motion-scale-from-zeroEntrance from scale zeroBlocks a run
  • motion-animate-without-reduced-motionAnimation has no local reduced-motion guardReview note
  • motion-reduced-motion-animationContinuous animation explicitly targets reduced-motion usersReview note
  • motion-layout-property-transitionTransition explicitly animates layout propertiesReview note
  • motion-css-transition-alltransition: all animates every property, layout includedBlocks a run
  • motion-framer-motion-importImports framer-motion instead of motion/reactBlocks a run
Product2 rules, 0 blocking
  • product-nested-modalsA modal that opens inside another modalReview note
  • product-select-for-few-optionsA select with two or three static optionsReview note
Search6 rules, 0 blocking
  • seo-canonical-wrong-hostCanonical points at a dev or preview hostReview note
  • seo-sitemap-build-time-lastmodSitemap lastmod is the build timeReview note
  • seo-unconditional-noindexPage set to noindex in sourceReview note
  • seo-page-missing-metadataPage inherits its parent's title and descriptionReview note
  • seo-missing-metadata-baseRoot metadata has URLs but no metadataBaseReview note
  • seo-missing-crawl-filesApp has no sitemap or robots fileReview note
Typography23 rules, 10 blocking
  • typography-dashesDouble hyphen or spaced hyphen standing in for a dashBlocks a run
  • typography-ellipsisThree periods instead of an ellipsis characterBlocks a run
  • typography-hover-weight-shiftDo not change font-weight on hover or selectedReview note
  • typography-midpoint-separatorsPipe or bullet used as an inline separatorBlocks a run
  • typography-multiplication-signThe letter x used as a multiplication signBlocks a run
  • typography-nbsp-value-unitBreaking space between a value and its unitBlocks a run
  • typography-readable-scaleSet a Readable Type ScaleReview note
  • typography-straight-quotesStraight quotes in rendered copyBlocks a run
  • typography-title-case-headingHeading, button or label set in Title CaseReview note
  • typography-trending-display-serifInstrument Serif as the default display faceReview note
  • typography-body-below-15pxRunning text set below 15pxReview note
  • typography-letterspaced-bodyPositive letter-spacing on lowercase body textReview note
  • typography-light-weight-small-bodyThin or light weight on text-sized copyBlocks a run
  • typography-line-height-out-of-bandLine height outside the comfortable band for its sizeBlocks a run
  • typography-hierarchy-size-onlyTwo roles told apart by a small size step aloneReview note
  • typography-numeric-cell-proportional-figuresNumbers in a table cell without tabular figuresReview note
  • typography-stacked-emphasisBold, caps, colour and size stacked on one elementBlocks a run
  • typography-uppercase-without-trackingUppercase text with no added letter-spacingBlocks a run
  • typography-justify-without-hyphensJustified text explicitly disables hyphenationReview note
  • typography-disabled-kerningText explicitly disables font kerningReview note
  • typography-stretched-glyphsText glyphs are horizontally stretchedReview note
  • typography-italic-accent-wordOne italic word in every headingReview note
  • typography-mono-overuseMonospace on everything that is not codeReview note

Live from npm and GitHub

Questions

What does Taste Lint check?

Copy, typography, interaction and motion in JSX, TSX and CSS. Add --profile writing for Markdown, MDX and READMEs, or --profile instructions for AGENTS.md, skills and plans. Each finding names the rule, quotes the source and suggests a fix.

Do I need an API key?

No. Every mechanical check runs without a key and makes no model calls. Add a Vercel AI Gateway key only if you want Jev review notes on judgement calls. Those calls bill your own account, and repeat runs reuse cached answers.

Will it fail my CI build?

Only on the 28 active rules, and each of those is mechanical: a regex or a measured value decides. The other 182 leave review notes that never fail a run. A Jev rule can block only after tune --write proves its precision on a labelled holdout.

Is Taste Lint free?

Yes. Taste Lint is MIT licensed and the mechanical checks cost nothing to run. The one optional cost is uncached Jev checks, billed to your Vercel AI Gateway key. Run with --dry-run first to see the requests and the estimated cost.

What do I need to run it?

Node.js 24.11 or later. Run npx taste-lint@latest init in your project folder. It installs Taste Lint locally and adds a taste script, so npm run taste lints the repo from then on.

Where do the rules come from?

From Agent Skills and Taste Training, both by Matthew Blode. Each rule file names the source file and line it came from, and port-rules --check confirms every ported rule still matches that source.

Lint your next pull request

Mechanical checks need no key and make no model calls. Add --dry-run to preview Jev requests and their cost first.

Install command for
npx taste-lint@latest init

npx taste-lint@latest init --agent