Skip to content

feat(mint): add @property type-check analysis to CSS audit - #104

Merged
nujovich merged 4 commits into
mainfrom
hermes/build/mint-css-property-type-validator
Jul 29, 2026
Merged

feat(mint): add @property type-check analysis to CSS audit#104
nujovich merged 4 commits into
mainfrom
hermes/build/mint-css-property-type-validator

Conversation

@nujovich

@nujovich nujovich commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Card: https://github.com/nujovich/mint-radar/issues/8

What

Extends the CSS audit with @property type-check analysis. A new STEP 13 in lib/prompts.mjs detects CSS @property registrations, validates them and their var() usages against the declared syntax descriptor, and reports violations as propertyTypeIssues. It builds on Mint's existing LLM-based audit pipeline — no new runtime parser needed.

Three rules are reported, most severe first:

Rule Severity What it catches
invalid-initial-value warning An initial-value that does not parse as the declared syntax, or a missing initial-value on a non-universal syntax. The browser rejects the whole registration, silently dropping both the type contract and the ability to transition.
fallback-type-mismatch warning var(--my-color, 14px) — a fallback whose type contradicts the registration, so it can never apply.
property-type-mismatch suggestion A registered property assigned where its declared syntax cannot apply (a <length> used in color). Reported only when unambiguous from the stylesheet alone.

Registrations declaring the universal syntax (*) are skipped — they are not type-checkable.

Why

@property adoption is growing (2.67% per Project Wallace 2026, driven by Tailwind v4). Mint audits colors, fonts, spacing, line-height, motion, layout accessibility, modern practices and overflow safety, but had no awareness of registered custom properties or their type contracts. Mismatches between a declared syntax and actual usage are a class of bug that neither ESLint CSS nor Stylelint targets natively, which reinforces Mint's positioning as a semantic auditor.

Milestones

  • Milestone 1 — STEP 13 in lib/prompts.mjs: detect registrations, validate against the declared syntax, report violations
  • Milestone 2 — PropertyTypeIssue interface + propertyTypeIssues on AuditReport in lib/types.ts
  • Milestone 3 — surface results in the CLI summary and the playground
  • Milestone 4 — lib/__fixtures__/at-property.css fixture covering one case per rule, plus tests
  • Docs — the three rules in the README's CSS layout linting table, plus a subsection on @property type safety

Review fixes (bfa9a22)

The first commit shipped the prompt step alone, emitting an output shape of its own (propertyName / declaredSyntax / location / issue). Neither shared consumer can read that shape, so findings were produced by the model and then silently discarded: an empty severity badge, as the anchor and a blank reason in the playground, and no entry at all in the CLI summary.

  • align propertyTypeIssues with the contract every other lint category follows: selector, property, rule, severity, reason (plus propertyName and declaredSyntax as extras)
  • split STEP 13 into the three named rules above, adding the invalid-initial-value check — the highest-value deterministic check, and the one previously missing
  • drop the ad-hoc >> location separator in favour of discrete selector + property, mirroring layoutA11yIssues
  • narrow the foreign-context rule to unambiguous cases to limit false positives
  • register propertyTypeIssues in LINT_CATEGORIES (lib/audit-summary.mjs) — the single registry feeding both formatLintSummary in the CLI and collectLintGroups in AuditView, so one entry lights up both surfaces
  • add a lint-contract test that parses the prompt's own <example> block and asserts severity / reason / anchor across all five lint categories
  • remove the BUILD-PLAN-issue-8.md scratch file

Documentation (dbf0727)

The README's CSS layout linting section tables every category and its rules; @property was missing from it. Added the three rules to that table, a @property type safety subsection explaining why invalid-initial-value is the one to fix first and that universal-syntax registrations are skipped, and updated the audit step and section intro. The subsection's CSS sample is wrapped in prettier-ignore so the aligned trailing comments survive formatting, matching the existing convention in the Frankenstein example.

Verification

vitest        592 passed (25 files)
typecheck     clean
eslint        no warnings or errors
format:check  clean
check:pack    28 files reachable from bin

The CLI summary line renders 1 overflow · 2 property types, and the shared renderer receives severity=warning, anchor=@property --spacing-unit, reason=Registered as <length> but initial-value… where it previously got undefined for all three fields.

Note for #105

#105 also adds a step numbered STEP 13, at the same hunk in lib/prompts.mjs. Both PRs report MERGEABLE because each is diffed against main in isolation. Whichever merges second must renumber to STEP 14 to avoid a duplicated step number in the audit prompt.

How to test

npm test

node -e "import('./lib/prompts.mjs').then(m => console.log('STEP 13 present:', m.buildAuditPrompt('').content.includes('@PROPERTY TYPE CHECKING')))"

@nujovich

Copy link
Copy Markdown
Owner Author

All milestones complete. Ready for review when you are.

2 similar comments
@nujovich

Copy link
Copy Markdown
Owner Author

All milestones complete. Ready for review when you are.

@nujovich

Copy link
Copy Markdown
Owner Author

All milestones complete. Ready for review when you are.

@nujovich
nujovich marked this pull request as ready for review July 23, 2026 14:02
@nujovich
nujovich marked this pull request as draft July 23, 2026 14:03
@nujovich

Copy link
Copy Markdown
Owner Author

All milestones complete. Ready for review when you are.

nujovich added 2 commits July 27, 2026 12:29
STEP 13 emitted its own field shape (propertyName / declaredSyntax /
location / issue), which neither the shared lint renderer nor the summary
registry can read. Findings were produced by the model and then silently
dropped: an empty severity badge, "—" as the anchor and a blank reason in
the playground, and no entry at all in the CLI summary.

- align propertyTypeIssues with the contract every other lint category
  follows: selector, property, rule, severity, reason (plus propertyName
  and declaredSyntax as extras)
- split STEP 13 into three named rules: invalid-initial-value,
  fallback-type-mismatch, property-type-mismatch
- detect invalid initial-value, where a syntax/initial-value mismatch (or
  a missing initial-value on a non-universal syntax) makes the browser
  reject the whole registration
- drop the ad-hoc ">>" location separator in favour of selector+property
- narrow the foreign-context rule to unambiguous cases to limit false
  positives
- register propertyTypeIssues in LINT_CATEGORIES so both the CLI summary
  and AuditView surface it
- add PropertyTypeIssue to AuditReport
- assert the shared severity/reason/anchor fields across all five lint
  categories by parsing the prompt's own output example
- add lib/__fixtures__/at-property.css covering one case per rule
- remove the BUILD-PLAN-issue-8.md scratch file
The CSS layout linting section tables every lint category and its rules;
@Property type checking was missing from it.

- add the three @Property rules to the linting table
- add a subsection explaining why invalid-initial-value matters (the
  browser rejects the whole registration) and that universal syntax is
  skipped
- mention @Property type contracts in the audit step and the section intro
- note that @Property findings do not feed the chaos score
@nujovich
nujovich marked this pull request as ready for review July 29, 2026 08:37
@nujovich
nujovich merged commit 7735100 into main Jul 29, 2026
3 checks passed
@nujovich
nujovich deleted the hermes/build/mint-css-property-type-validator branch July 29, 2026 08:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant