@drskillissue/ganko 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-KVZ56NZ5.js → chunk-5OEDGKHL.js} +3 -3
- package/dist/{chunk-KVZ56NZ5.js.map → chunk-5OEDGKHL.js.map} +1 -1
- package/dist/{chunk-64TMAKYI.js → chunk-XVYCT2RS.js} +36 -24
- package/dist/{chunk-64TMAKYI.js.map → chunk-XVYCT2RS.js.map} +1 -1
- package/dist/eslint-plugin.cjs +35 -23
- package/dist/eslint-plugin.cjs.map +1 -1
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.cjs +37 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/rules-manifest.cjs +2 -2
- package/dist/rules-manifest.cjs.map +1 -1
- package/dist/rules-manifest.js +1 -1
- package/package.json +1 -1
package/dist/eslint-plugin.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -31524,6 +31524,19 @@ function formatAlignmentCauses(findings) {
|
|
|
31524
31524
|
}
|
|
31525
31525
|
return out;
|
|
31526
31526
|
}
|
|
31527
|
+
function formatPrimaryFix(findings) {
|
|
31528
|
+
if (findings.length === 0) return "";
|
|
31529
|
+
let best = null;
|
|
31530
|
+
for (let i = 0; i < findings.length; i++) {
|
|
31531
|
+
const finding = findings[i];
|
|
31532
|
+
if (!finding) continue;
|
|
31533
|
+
if (best === null || finding.weight > best.weight) {
|
|
31534
|
+
best = finding;
|
|
31535
|
+
}
|
|
31536
|
+
}
|
|
31537
|
+
if (best === null) return "";
|
|
31538
|
+
return best.fix;
|
|
31539
|
+
}
|
|
31527
31540
|
function compareAscii(left, right) {
|
|
31528
31541
|
if (left < right) return -1;
|
|
31529
31542
|
if (left > right) return 1;
|
|
@@ -34858,9 +34871,6 @@ var COMPOSITION_LABELS = {
|
|
|
34858
34871
|
[4 /* BlockSegmented */]: "block-segmented",
|
|
34859
34872
|
[5 /* Unknown */]: "unknown"
|
|
34860
34873
|
};
|
|
34861
|
-
function formatCompositionClassification(classification) {
|
|
34862
|
-
return COMPOSITION_LABELS[classification];
|
|
34863
|
-
}
|
|
34864
34874
|
function formatCompositionFixSuggestion(subjectFingerprint) {
|
|
34865
34875
|
if (subjectFingerprint.classification === 2 /* MixedUnmitigated */) {
|
|
34866
34876
|
if (subjectFingerprint.hasVerticalAlignMitigation) {
|
|
@@ -38539,6 +38549,7 @@ function buildFindingsFromAtoms(atoms, input, evidence) {
|
|
|
38539
38549
|
const next = {
|
|
38540
38550
|
kind: factor.kind,
|
|
38541
38551
|
message: factor.message,
|
|
38552
|
+
fix: factor.fix,
|
|
38542
38553
|
weight
|
|
38543
38554
|
};
|
|
38544
38555
|
const existing = byKind.get(factor.kind);
|
|
@@ -38552,48 +38563,48 @@ function buildFindingsFromAtoms(atoms, input, evidence) {
|
|
|
38552
38563
|
}
|
|
38553
38564
|
return [...byKind.values()];
|
|
38554
38565
|
}
|
|
38555
|
-
function toFindingFactor(factorId, input,
|
|
38566
|
+
function toFindingFactor(factorId, input, _evidence) {
|
|
38556
38567
|
switch (factorId) {
|
|
38557
38568
|
case "offset-delta":
|
|
38558
38569
|
return {
|
|
38559
38570
|
kind: "offset-delta",
|
|
38560
|
-
message: "
|
|
38571
|
+
message: "block-axis offset differs from siblings",
|
|
38572
|
+
fix: "normalize margin/padding to match sibling cohort"
|
|
38561
38573
|
};
|
|
38562
38574
|
case "declared-offset-delta":
|
|
38563
38575
|
return {
|
|
38564
38576
|
kind: "declared-offset-delta",
|
|
38565
|
-
message: "declared block-axis offset differs from
|
|
38577
|
+
message: "declared block-axis offset differs from siblings",
|
|
38578
|
+
fix: "remove or unify the offset"
|
|
38566
38579
|
};
|
|
38567
38580
|
case "baseline-conflict":
|
|
38568
38581
|
return {
|
|
38569
38582
|
kind: "baseline-conflict",
|
|
38570
|
-
message: "baseline/line-height
|
|
38583
|
+
message: "baseline/line-height mismatch between siblings",
|
|
38584
|
+
fix: "unify line-height or add vertical-align"
|
|
38571
38585
|
};
|
|
38572
38586
|
case "context-conflict":
|
|
38573
38587
|
return {
|
|
38574
38588
|
kind: "context-conflict",
|
|
38575
|
-
message: "container and child alignment
|
|
38589
|
+
message: "container and child alignment conflict",
|
|
38590
|
+
fix: "check align-items on the parent"
|
|
38576
38591
|
};
|
|
38577
38592
|
case "replaced-control-risk":
|
|
38578
38593
|
return {
|
|
38579
38594
|
kind: "replaced-control-risk",
|
|
38580
|
-
message: "replaced
|
|
38595
|
+
message: "replaced element baseline differs from text siblings",
|
|
38596
|
+
fix: "add vertical-align: middle to the replaced element"
|
|
38581
38597
|
};
|
|
38582
38598
|
case "content-composition-conflict":
|
|
38583
38599
|
return {
|
|
38584
38600
|
kind: "content-composition-conflict",
|
|
38585
|
-
message:
|
|
38601
|
+
message: "content composition differs from siblings",
|
|
38602
|
+
fix: formatCompositionFixSuggestion(input.subjectContentComposition)
|
|
38586
38603
|
};
|
|
38587
38604
|
default:
|
|
38588
38605
|
return null;
|
|
38589
38606
|
}
|
|
38590
38607
|
}
|
|
38591
|
-
function formatContentCompositionFinding(input, evidence) {
|
|
38592
|
-
const subjectClassification = formatCompositionClassification(input.subjectContentComposition.classification);
|
|
38593
|
-
const majorityClassification = formatCompositionClassification(evidence.majorityClassification);
|
|
38594
|
-
const fixSuggestion = formatCompositionFixSuggestion(input.subjectContentComposition);
|
|
38595
|
-
return `siblings have identical CSS but different content composition (subject: ${subjectClassification}, majority: ${majorityClassification}; fix: ${fixSuggestion})`;
|
|
38596
|
-
}
|
|
38597
38608
|
function round(value2) {
|
|
38598
38609
|
return Math.round(value2 * 1e3) / 1e3;
|
|
38599
38610
|
}
|
|
@@ -39269,7 +39280,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
39269
39280
|
|
|
39270
39281
|
// src/cross-file/rules/css-layout-sibling-alignment-outlier.ts
|
|
39271
39282
|
var messages143 = {
|
|
39272
|
-
misalignedSibling: "
|
|
39283
|
+
misalignedSibling: "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}"
|
|
39273
39284
|
};
|
|
39274
39285
|
var MIN_CONFIDENCE_THRESHOLD = 0.48;
|
|
39275
39286
|
var MIN_OFFSET_PX_THRESHOLD = 2;
|
|
@@ -39294,6 +39305,7 @@ var siblingAlignmentDetector = {
|
|
|
39294
39305
|
severity: decision.evaluation.severity,
|
|
39295
39306
|
confidence: decision.evaluation.confidence,
|
|
39296
39307
|
causes: formatAlignmentCauses(decision.evaluation.signalFindings),
|
|
39308
|
+
primaryFix: formatPrimaryFix(decision.evaluation.signalFindings),
|
|
39297
39309
|
contextKind: decision.evaluation.contextKind,
|
|
39298
39310
|
contextCertainty: decision.evaluation.contextCertainty,
|
|
39299
39311
|
estimatedOffsetPx: decision.evaluation.estimatedOffsetPx,
|
|
@@ -39376,8 +39388,11 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
39376
39388
|
const confidence = formatFixed(detection.evidence.confidence);
|
|
39377
39389
|
const offset = detection.evidence.estimatedOffsetPx;
|
|
39378
39390
|
const hasOffset = offset !== null && Math.abs(offset) > 0.25;
|
|
39379
|
-
const offsetClause = hasOffset ?
|
|
39391
|
+
const offsetClause = hasOffset ? ` Estimated offset: ${formatFixed(offset)}px.` : "";
|
|
39380
39392
|
const causes = detection.evidence.causes.length === 0 ? "alignment signals indicate an outlier" : detection.evidence.causes.join("; ");
|
|
39393
|
+
const primaryFix = detection.evidence.primaryFix;
|
|
39394
|
+
const firstChar = primaryFix.length > 0 ? primaryFix[0] : void 0;
|
|
39395
|
+
const fix = firstChar !== void 0 ? ` ${firstChar.toUpperCase()}${primaryFix.slice(1)}.` : "";
|
|
39381
39396
|
if (log.enabled) {
|
|
39382
39397
|
log.debug(
|
|
39383
39398
|
`${logPrefix} EMIT: severity=${severity} confidence=${confidence} offset=${offset?.toFixed(2) ?? "null"} posterior=[${detection.evidence.posteriorLower.toFixed(3)},${detection.evidence.posteriorUpper.toFixed(3)}] evidenceMass=${detection.evidence.evidenceMass.toFixed(3)} topFactors=[${detection.evidence.topFactors.join(",")}] causes=[${causes}]`
|
|
@@ -39392,11 +39407,8 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
39392
39407
|
resolveMessage(messages143.misalignedSibling, {
|
|
39393
39408
|
subject,
|
|
39394
39409
|
parent,
|
|
39395
|
-
|
|
39396
|
-
|
|
39397
|
-
confidence,
|
|
39398
|
-
offsetClause,
|
|
39399
|
-
causes
|
|
39410
|
+
fix,
|
|
39411
|
+
offsetClause
|
|
39400
39412
|
}),
|
|
39401
39413
|
"warn"
|
|
39402
39414
|
)
|
|
@@ -41486,7 +41498,7 @@ var RULES = [
|
|
|
41486
41498
|
"category": "css-layout",
|
|
41487
41499
|
"plugin": "cross-file",
|
|
41488
41500
|
"messages": {
|
|
41489
|
-
"misalignedSibling": "
|
|
41501
|
+
"misalignedSibling": "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}"
|
|
41490
41502
|
}
|
|
41491
41503
|
},
|
|
41492
41504
|
{
|
|
@@ -42729,7 +42741,7 @@ var RULES_BY_CATEGORY = {
|
|
|
42729
42741
|
"css-animation": [{ "id": "css-no-discrete-transition", "severity": "error", "description": "Disallow transitions on discrete CSS properties.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "discreteTransition": "Property `{{property}}` is discrete and should not be transitioned." } }, { "id": "css-no-empty-keyframes", "severity": "error", "description": "Disallow empty @keyframes rules.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "emptyKeyframes": "@keyframes `{{name}}` has no effective keyframes." } }, { "id": "no-layout-property-animation", "severity": "warn", "description": "Disallow animating layout-affecting properties.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "avoidLayoutAnimation": "Avoid animating layout property `{{property}}`. Prefer transform or opacity to reduce layout thrashing." } }, { "id": "no-transition-all", "severity": "warn", "description": "Disallow transition: all and transition-property: all.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "avoidTransitionAll": "Avoid `transition: all`. Transition specific properties to reduce unnecessary style and paint work." } }, { "id": "no-unknown-animation-name", "severity": "error", "description": "Disallow animation names that do not match declared keyframes.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "unknownAnimationName": "Animation name `{{name}}` in `{{property}}` does not match any declared @keyframes." } }, { "id": "no-unused-keyframes", "severity": "warn", "description": "Disallow unused @keyframes declarations.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "unusedKeyframes": "@keyframes `{{name}}` is never referenced by animation declarations." } }],
|
|
42730
42742
|
"css-cascade": [{ "id": "declaration-no-overridden-within-rule", "severity": "warn", "description": "Disallow duplicate declarations of the same property within a single rule block.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "overriddenWithinRule": "Declaration `{{property}}` is overridden later in the same rule. Keep one final declaration per property." } }, { "id": "media-query-overlap-conflict", "severity": "warn", "description": "Disallow conflicting declarations in partially overlapping media queries.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "mediaOverlapConflict": "Overlapping media queries set different `{{property}}` values for `{{selector}}` in the same overlap range." } }, { "id": "no-descending-specificity-conflict", "severity": "warn", "description": "Disallow lower-specificity selectors after higher-specificity selectors for the same property.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "descendingSpecificity": "Lower-specificity selector `{{laterSelector}}` appears after `{{earlierSelector}}` for `{{property}}`, creating brittle cascade behavior." } }, { "id": "no-layer-order-inversion", "severity": "warn", "description": "Disallow source-order assumptions that are inverted by layer precedence.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "layerOrderInversion": "Declaration for `{{property}}` in selector `{{selector}}` appears later but is overridden by an earlier declaration due to @layer precedence." } }, { "id": "no-redundant-override-pairs", "severity": "warn", "description": "Disallow declarations that are deterministically overridden in the same selector context.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "redundantOverride": "Declaration `{{property}}` is always overridden later by the same selector in the same cascade context." } }],
|
|
42731
42743
|
"css-jsx": [{ "id": "css-no-unreferenced-component-class", "severity": "warn", "description": "Detect CSS classes that are never referenced by static JSX class attributes.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unreferencedClass": "CSS class '{{className}}' is defined but not referenced by static JSX class attributes" } }, { "id": "jsx-classlist-boolean-values", "severity": "error", "description": "Require classList values to be boolean-like expressions.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "nonBooleanValue": "classList value for `{{name}}` must be boolean." } }, { "id": "jsx-classlist-no-accessor-reference", "severity": "error", "description": "Disallow passing accessor references directly as classList values.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "accessorReference": "Signal accessor `{{name}}` must be called in classList value (use {{name}}())." } }, { "id": "jsx-classlist-no-constant-literals", "severity": "warn", "description": "Disallow classList entries with constant true/false values.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "constantEntry": "classList entry `{{name}}: {{value}}` is constant; move it to static class." } }, { "id": "jsx-classlist-static-keys", "severity": "error", "description": "Require classList keys to be static and non-computed.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "nonStaticKey": "classList key must be statically known for reliable class mapping." } }, { "id": "jsx-layout-classlist-geometry-toggle", "severity": "warn", "description": "Flag classList-driven class toggles that map to layout-affecting CSS geometry changes.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "classListGeometryToggle": "classList toggles '{{className}}', and matching CSS changes layout-affecting '{{property}}', which can cause CLS." } }, { "id": "jsx-layout-fill-image-parent-must-be-sized", "severity": "warn", "description": "Require stable parent size and positioning for fill-image component usage.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unsizedFillParent": "Fill-image component '{{component}}' is inside a parent without stable size/position; add parent sizing (height/min-height/aspect-ratio) and non-static position to avoid CLS." } }, { "id": "jsx-layout-picture-source-ratio-consistency", "severity": "warn", "description": "Require consistent intrinsic aspect ratios across <picture> sources and fallback image.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "inconsistentPictureRatio": "`<picture>` source ratio {{sourceRatio}} differs from fallback img ratio {{imgRatio}}, which can cause reserved-space mismatch and CLS." } }, { "id": "jsx-layout-unstable-style-toggle", "severity": "warn", "description": "Flag dynamic inline style values on layout-sensitive properties that can trigger CLS.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unstableLayoutStyleToggle": "Dynamic style value for '{{property}}' can toggle layout geometry at runtime and cause CLS." } }, { "id": "jsx-no-duplicate-class-token-class-classlist", "severity": "warn", "description": "Disallow duplicate class tokens between class and classList on the same JSX element.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "duplicateClassToken": "Class token `{{name}}` appears in both class and classList." } }, { "id": "jsx-no-undefined-css-class", "severity": "error", "description": "Detect undefined CSS class names in JSX", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "undefinedClass": "CSS class '{{className}}' is not defined in project CSS files" } }, { "id": "jsx-style-kebab-case-keys", "severity": "error", "description": "Require kebab-case keys in JSX style object literals.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "kebabStyleKey": "Style key `{{name}}` should be `{{kebab}}` in Solid style objects." } }, { "id": "jsx-style-no-function-values", "severity": "error", "description": "Disallow function values in JSX style objects.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "functionStyleValue": "Style value for `{{name}}` is a function; pass computed value instead." } }, { "id": "jsx-style-no-unused-custom-prop", "severity": "warn", "description": "Detect inline style custom properties that are never consumed by CSS var() references.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unusedInlineVar": "Inline custom property `{{name}}` is never read via var({{name}})." } }, { "id": "jsx-style-policy", "severity": "warn", "description": "Enforce accessibility policy thresholds on inline JSX style objects.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "fontTooSmall": "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for policy `{{policy}}`.", "lineHeightTooSmall": "Inline style `line-height: {{value}}` is below the minimum `{{min}}` for policy `{{policy}}`.", "heightTooSmall": "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for interactive elements in policy `{{policy}}`.", "letterSpacingTooSmall": "Inline style `letter-spacing: {{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Inline style `word-spacing: {{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`." } }],
|
|
42732
|
-
"css-layout": [{ "id": "css-layout-animation-layout-property", "severity": "warn", "description": "Disallow keyframe animations that mutate layout-affecting properties and can trigger CLS.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "animationLayoutProperty": "Animation '{{animation}}' mutates layout-affecting '{{property}}', which can trigger CLS. Prefer transform/opacity or reserve geometry." } }, { "id": "css-layout-box-sizing-toggle-with-chrome", "severity": "warn", "description": "Disallow conditional box-sizing mode toggles when box chrome contributes to geometry shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "boxSizingToggleWithChrome": "Conditional `box-sizing` toggle on '{{tag}}' combines with non-zero padding/border, which can shift layout and trigger CLS." } }, { "id": "css-layout-conditional-display-collapse", "severity": "warn", "description": "Disallow conditional display collapse in flow without reserved geometry.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalDisplayCollapse": "Conditional display sets '{{display}}' on '{{tag}}' without stable reserved space, which can collapse/expand layout and cause CLS." } }, { "id": "css-layout-conditional-offset-shift", "severity": "warn", "description": "Disallow conditional non-zero block-axis offsets that can trigger layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalOffsetShift": "Conditional style applies non-zero '{{property}}' offset ({{value}}), which can cause layout shifts when conditions toggle." } }, { "id": "css-layout-conditional-white-space-wrap-shift", "severity": "warn", "description": "Disallow conditional white-space wrapping mode toggles that can trigger CLS.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalWhiteSpaceShift": "Conditional white-space '{{whiteSpace}}' on '{{tag}}' can reflow text and shift siblings; keep wrapping behavior stable or reserve geometry." } }, { "id": "css-layout-content-visibility-no-intrinsic-size", "severity": "warn", "description": "Require intrinsic size reservation when using content-visibility auto to avoid late layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "missingIntrinsicSize": "`content-visibility: auto` on '{{tag}}' lacks intrinsic size reservation (`contain-intrinsic-size`/min-height/height/aspect-ratio), which can cause CLS." } }, { "id": "css-layout-dynamic-slot-no-reserved-space", "severity": "warn", "description": "Require reserved block space for dynamic content containers to avoid layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "dynamicSlotNoReservedSpace": "Dynamic content container '{{tag}}' does not reserve block space (min-height/height/aspect-ratio/contain-intrinsic-size), which can cause CLS." } }, { "id": "css-layout-font-swap-instability", "severity": "warn", "description": "Require metric overrides for swapping webfonts to reduce layout shifts during font load.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "unstableFontSwap": "`@font-face` for '{{family}}' uses `font-display: {{display}}` without metric overrides (for example `size-adjust`), which can cause CLS when the webfont swaps in." } }, { "id": "css-layout-overflow-anchor-instability", "severity": "warn", "description": "Disallow overflow-anchor none on dynamic or scrollable containers prone to visible layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "unstableOverflowAnchor": "Element '{{tag}}' sets `overflow-anchor: none` on a {{context}} container; disabling scroll anchoring can amplify visible layout shifts." } }, { "id": "css-layout-overflow-mode-toggle-instability", "severity": "warn", "description": "Disallow conditional overflow mode switches that can introduce scrollbar-induced layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "overflowModeToggle": "Conditional overflow mode changes scrolling ('{{overflow}}') on '{{tag}}' without `scrollbar-gutter: stable`, which can trigger CLS." } }, { "id": "css-layout-scrollbar-gutter-instability", "severity": "warn", "description": "Require stable scrollbar gutters for scrollable containers to reduce layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "missingScrollbarGutter": "Scrollable container '{{tag}}' uses overflow auto/scroll without `scrollbar-gutter: stable`, which can trigger CLS when scrollbars appear." } }, { "id": "css-layout-sibling-alignment-outlier", "severity": "warn", "description": "Detect vertical alignment outliers between sibling elements in shared layout containers.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "misalignedSibling": "
|
|
42744
|
+
"css-layout": [{ "id": "css-layout-animation-layout-property", "severity": "warn", "description": "Disallow keyframe animations that mutate layout-affecting properties and can trigger CLS.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "animationLayoutProperty": "Animation '{{animation}}' mutates layout-affecting '{{property}}', which can trigger CLS. Prefer transform/opacity or reserve geometry." } }, { "id": "css-layout-box-sizing-toggle-with-chrome", "severity": "warn", "description": "Disallow conditional box-sizing mode toggles when box chrome contributes to geometry shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "boxSizingToggleWithChrome": "Conditional `box-sizing` toggle on '{{tag}}' combines with non-zero padding/border, which can shift layout and trigger CLS." } }, { "id": "css-layout-conditional-display-collapse", "severity": "warn", "description": "Disallow conditional display collapse in flow without reserved geometry.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalDisplayCollapse": "Conditional display sets '{{display}}' on '{{tag}}' without stable reserved space, which can collapse/expand layout and cause CLS." } }, { "id": "css-layout-conditional-offset-shift", "severity": "warn", "description": "Disallow conditional non-zero block-axis offsets that can trigger layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalOffsetShift": "Conditional style applies non-zero '{{property}}' offset ({{value}}), which can cause layout shifts when conditions toggle." } }, { "id": "css-layout-conditional-white-space-wrap-shift", "severity": "warn", "description": "Disallow conditional white-space wrapping mode toggles that can trigger CLS.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "conditionalWhiteSpaceShift": "Conditional white-space '{{whiteSpace}}' on '{{tag}}' can reflow text and shift siblings; keep wrapping behavior stable or reserve geometry." } }, { "id": "css-layout-content-visibility-no-intrinsic-size", "severity": "warn", "description": "Require intrinsic size reservation when using content-visibility auto to avoid late layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "missingIntrinsicSize": "`content-visibility: auto` on '{{tag}}' lacks intrinsic size reservation (`contain-intrinsic-size`/min-height/height/aspect-ratio), which can cause CLS." } }, { "id": "css-layout-dynamic-slot-no-reserved-space", "severity": "warn", "description": "Require reserved block space for dynamic content containers to avoid layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "dynamicSlotNoReservedSpace": "Dynamic content container '{{tag}}' does not reserve block space (min-height/height/aspect-ratio/contain-intrinsic-size), which can cause CLS." } }, { "id": "css-layout-font-swap-instability", "severity": "warn", "description": "Require metric overrides for swapping webfonts to reduce layout shifts during font load.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "unstableFontSwap": "`@font-face` for '{{family}}' uses `font-display: {{display}}` without metric overrides (for example `size-adjust`), which can cause CLS when the webfont swaps in." } }, { "id": "css-layout-overflow-anchor-instability", "severity": "warn", "description": "Disallow overflow-anchor none on dynamic or scrollable containers prone to visible layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "unstableOverflowAnchor": "Element '{{tag}}' sets `overflow-anchor: none` on a {{context}} container; disabling scroll anchoring can amplify visible layout shifts." } }, { "id": "css-layout-overflow-mode-toggle-instability", "severity": "warn", "description": "Disallow conditional overflow mode switches that can introduce scrollbar-induced layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "overflowModeToggle": "Conditional overflow mode changes scrolling ('{{overflow}}') on '{{tag}}' without `scrollbar-gutter: stable`, which can trigger CLS." } }, { "id": "css-layout-scrollbar-gutter-instability", "severity": "warn", "description": "Require stable scrollbar gutters for scrollable containers to reduce layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "missingScrollbarGutter": "Scrollable container '{{tag}}' uses overflow auto/scroll without `scrollbar-gutter: stable`, which can trigger CLS when scrollbars appear." } }, { "id": "css-layout-sibling-alignment-outlier", "severity": "warn", "description": "Detect vertical alignment outliers between sibling elements in shared layout containers.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "misalignedSibling": "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}" } }, { "id": "css-layout-stateful-box-model-shift", "severity": "warn", "description": "Disallow stateful selector changes that alter element geometry and trigger layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "statefulBoxModelShift": "State selector '{{selector}}' changes layout-affecting '{{property}}'. Keep geometry stable across states to avoid CLS." } }, { "id": "css-layout-transition-layout-property", "severity": "warn", "description": "Disallow transitions that animate layout-affecting geometry properties.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "transitionLayoutProperty": "Transition '{{property}}' in '{{declaration}}' animates layout-affecting geometry. Prefer transform/opacity to avoid CLS." } }, { "id": "css-layout-unsized-replaced-element", "severity": "warn", "description": "Require stable reserved geometry for replaced media elements to prevent layout shifts.", "fixable": false, "category": "css-layout", "plugin": "cross-file", "messages": { "unsizedReplacedElement": "Replaced element '{{tag}}' has no stable reserved size (width/height or aspect-ratio with a dimension), which can cause CLS." } }],
|
|
42733
42745
|
"css-property": [{ "id": "css-no-custom-property-cycle", "severity": "error", "description": "Disallow cycles in custom property references.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "variableCycle": "Custom property cycle detected involving `{{name}}`." } }, { "id": "css-no-hardcoded-z-index", "severity": "warn", "description": "Disallow hardcoded positive z-index literals.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "hardcodedZ": "Use a z-index token variable instead of literal `{{value}}`." } }, { "id": "css-no-legacy-vh-100", "severity": "warn", "description": "Disallow 100vh in viewport sizing declarations.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "avoidLegacyVh": "Use 100dvh/100svh instead of `100vh` for mobile-safe viewport sizing." } }, { "id": "css-z-index-requires-positioned-context", "severity": "warn", "description": "Require positioned context when using z-index.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "zIndexNoContext": "`z-index` has no guaranteed effect without a positioned context." } }, { "id": "no-important", "severity": "warn", "description": "Disallow !important declarations.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "avoidImportant": "Avoid `!important` on `{{property}}`. It increases override cost and usually signals specificity debt." } }, { "id": "no-unresolved-custom-properties", "severity": "error", "description": "Disallow unresolved custom property references.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "unresolvedCustomProperty": "Custom property reference `{{name}}` is unresolved in `{{property}}`. Define it or provide a fallback value." } }, { "id": "no-unused-custom-properties", "severity": "warn", "description": "Disallow unused CSS custom properties.", "fixable": false, "category": "css-property", "plugin": "css", "messages": { "unusedCustomProperty": "Custom property `{{name}}` is never referenced within the project CSS." } }],
|
|
42734
42746
|
"css-selector": [{ "id": "no-complex-selectors", "severity": "warn", "description": "Disallow deep selectors that are expensive to match.", "fixable": false, "category": "css-selector", "plugin": "css", "messages": { "selectorTooDeep": "Selector `{{selector}}` has depth {{depth}}. Deep selectors increase style recalculation cost and are fragile across component rerenders." } }, { "id": "no-duplicate-selectors", "severity": "warn", "description": "Disallow duplicate selector blocks.", "fixable": false, "category": "css-selector", "plugin": "css", "messages": { "duplicateSelector": "Selector `{{selector}}` is duplicated {{count}} times. Merge declarations to avoid cascade ambiguity." } }, { "id": "no-id-selectors", "severity": "warn", "description": "Disallow ID selectors.", "fixable": false, "category": "css-selector", "plugin": "css", "messages": { "avoidId": "Avoid ID selector in `{{selector}}`. IDs raise specificity and make component-level styling harder to maintain." } }, { "id": "selector-max-attribute-and-universal", "severity": "off", "description": "Disallow selectors with attribute or universal selectors beyond configured limits.", "fixable": false, "category": "css-selector", "plugin": "css", "messages": { "tooManyAttributes": "Selector `{{selector}}` uses {{count}} attribute selector(s). Maximum allowed is {{max}}.", "tooManyUniversals": "Selector `{{selector}}` uses {{count}} universal selector(s). Maximum allowed is {{max}}." } }, { "id": "selector-max-specificity", "severity": "warn", "description": "Disallow selectors that exceed a specificity threshold.", "fixable": false, "category": "css-selector", "plugin": "css", "messages": { "maxSpecificity": "Selector `{{selector}}` specificity {{specificity}} exceeds max {{max}}. Reduce selector weight to keep the cascade predictable." } }],
|
|
42735
42747
|
"css-structure": [{ "id": "css-no-empty-rule", "severity": "warn", "description": "Disallow empty CSS rules.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "emptyRule": "Empty rule `{{selector}}` should be removed." } }, { "id": "css-no-unknown-container-name", "severity": "error", "description": "Disallow unknown named containers in @container queries.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "unknownContainer": "Unknown container name `{{name}}` in @container query." } }, { "id": "css-no-unused-container-name", "severity": "warn", "description": "Disallow unused named containers.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "unusedContainer": "Container name `{{name}}` is declared but never queried." } }, { "id": "layer-requirement-for-component-rules", "severity": "warn", "description": "Require style rules to be inside @layer when the file defines layers.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "missingLayer": "Rule `{{selector}}` is not inside any @layer block while this file uses @layer. Place component rules inside an explicit layer." } }],
|