Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions BUILD-PLAN-issue-21.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# BUILD-PLAN-issue-21.md — CSS Cascade Layers (@layer) audit

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

**Decision:** PLAN defined 4 milestones (static detection, no LLM step needed).

## Milestones

- [ ] Milestone 1 — Add `CascadeLayerAudit` type system with detection of layer names, declaration order, and rules outside any layer
- [ ] Milestone 2 — Implement @layer parser that groups rules by layer and detects anti-patterns (post-layer specificity, !important inside layers)
- [ ] Milestone 3 — Add layer hierarchy visualization to the CLI output, with warnings for implicit vs explicit order
- [ ] Milestone 4 — Add test fixture with real-world multi-layer projects and tests
40 changes: 39 additions & 1 deletion bin/mint-ds.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ async function cmdLint(argv) {
)

const result = lintCss(css)
const { findings } = result
const { findings, cascadeLayers } = result

if (findings.length === 0) {
log(styles.green('✓') + ' No lint issues found.')
Expand All @@ -547,6 +547,44 @@ async function cmdLint(argv) {
}
}

// Cascade Layers: hierarchy visualization + implicit/explicit order warnings.
if (cascadeLayers && cascadeLayers.layerCount > 0) {
log('')
log(styles.bold('Cascade Layers'))
log('')
if (cascadeLayers.orderExplicit) {
log(styles.dim(' Order: explicit (@layer order statement)'))
} else {
log(styles.dim(' Order: implicit (first-appearance order)'))
log(
' ' +
styles.yellow('WARN') +
' Layer order is implicit. Add an explicit order statement (e.g. @layer reset, base, theme;) to pin cascade precedence.'
)
}
log('')
log(styles.dim(' Layer hierarchy (lowest priority first):'))
for (const entry of cascadeLayers.hierarchy) {
const tag = entry.order === 'explicit' ? 'explicit' : 'implicit'
log(
' ' +
(entry.rank + 1) +
'. ' +
entry.name +
' ' +
styles.dim('(' + tag + ', ' + entry.rulesCount + ' rule(s))')
)
}
log(
styles.dim(
' - unlayered styles (highest priority; override all layers, ' +
cascadeLayers.rulesOutsideLayers +
' rule(s))'
)
)
log('')
}

// Modern CSS Opportunities: adoption report for gap decorations.
const { adoption } = lintGapDecorationAdoption(css, {
stylesheetCount: files.length,
Expand Down
77 changes: 77 additions & 0 deletions lib/__fixtures__/cascade-layers.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* Fixture for the Cascade Layers audit — a real-world multi-layer project.
Mirrors a design-system stylesheet: an explicit layer order, four named
@layer blocks (reset, base, components, utilities), legacy unlayered
overrides, a high-specificity unlayered rule declared after the layer
order, and an !important inside the reset layer. */

@layer reset, base, components, utilities;

@layer reset {
* {
margin: 0 !important;
box-sizing: border-box;
}
img {
display: block;
max-width: 100%;
}
}

@layer base {
:root {
--color-text: #111111;
--color-bg: #ffffff;
}
body {
font-family: system-ui, sans-serif;
color: var(--color-text);
background: var(--color-bg);
}
h1,
h2,
h3 {
line-height: 1.2;
}
}

@layer components {
.card {
display: grid;
gap: 1rem;
padding: 1.5rem;
border-radius: 8px;
}
.button {
border: none;
padding: 0.5rem 1rem;
}
.modal {
position: fixed;
inset: 0;
}
}

@layer utilities {
.mt-0 {
margin-top: 0;
}
.text-center {
text-align: center;
}
}

/* Legacy overrides declared outside any layer — they outrank every layer. */
.legacy-clearfix::after {
content: '';
display: table;
clear: both;
}

.footer {
margin-top: 2rem;
}

/* High-specificity unlayered rule declared after the layer order. */
#hero {
font-size: 3rem !important;
}
Loading
Loading