@drskillissue/ganko 0.2.71 → 0.2.72

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/index.cjs CHANGED
@@ -29835,148 +29835,8 @@ function checkParagraphSpacing(graph, emit, policy, name) {
29835
29835
  }
29836
29836
  }
29837
29837
 
29838
- // src/css/rules/a11y/css-policy-touch-target.ts
29839
- var messages125 = {
29840
- heightTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
29841
- widthTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
29842
- paddingTooSmall: "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
29843
- };
29844
- function classifyRule(rule) {
29845
- if (rule.elementKinds.has("button")) return "button";
29846
- if (rule.elementKinds.has("input")) return "input";
29847
- return null;
29848
- }
29849
- var HEIGHT_PROPERTIES = /* @__PURE__ */ new Set(["height", "min-height"]);
29850
- var WIDTH_PROPERTIES = /* @__PURE__ */ new Set(["width", "min-width"]);
29851
- var HPADDING_PROPERTIES = /* @__PURE__ */ new Set(["padding-left", "padding-right", "padding-inline", "padding-inline-start", "padding-inline-end"]);
29852
- var cssPolicyTouchTarget = defineCSSRule({
29853
- id: "css-policy-touch-target",
29854
- severity: "warn",
29855
- messages: messages125,
29856
- meta: {
29857
- description: "Enforce minimum interactive element sizes per accessibility policy.",
29858
- fixable: false,
29859
- category: "css-a11y"
29860
- },
29861
- options: {},
29862
- check(graph, emit) {
29863
- const policy = getActivePolicy();
29864
- if (policy === null) return;
29865
- const name = getActivePolicyName() ?? "";
29866
- const decls = graph.declarationsForProperties(
29867
- "height",
29868
- "min-height",
29869
- "width",
29870
- "min-width",
29871
- "padding-left",
29872
- "padding-right",
29873
- "padding-inline",
29874
- "padding-inline-start",
29875
- "padding-inline-end"
29876
- );
29877
- for (let i = 0; i < decls.length; i++) {
29878
- const d = decls[i];
29879
- if (!d) continue;
29880
- if (!d.rule) continue;
29881
- const prop = d.property.toLowerCase();
29882
- const kind = classifyRule(d.rule);
29883
- if (!kind) continue;
29884
- if (isVisuallyHiddenInput(d.rule)) continue;
29885
- const px = parsePxValue(d.value);
29886
- if (px === null) continue;
29887
- if (HEIGHT_PROPERTIES.has(prop)) {
29888
- const min = kind === "button" ? policy.minButtonHeight : policy.minInputHeight;
29889
- if (px >= min) continue;
29890
- emitCSSDiagnostic(
29891
- emit,
29892
- d.file.path,
29893
- d.startLine,
29894
- d.startColumn,
29895
- cssPolicyTouchTarget,
29896
- "heightTooSmall",
29897
- resolveMessage(messages125.heightTooSmall, {
29898
- property: d.property,
29899
- value: d.value.trim(),
29900
- resolved: String(Math.round(px * 100) / 100),
29901
- min: String(min),
29902
- element: kind,
29903
- policy: name
29904
- })
29905
- );
29906
- continue;
29907
- }
29908
- if (WIDTH_PROPERTIES.has(prop)) {
29909
- const min = kind === "button" ? policy.minButtonWidth : policy.minTouchTarget;
29910
- if (px >= min) continue;
29911
- emitCSSDiagnostic(
29912
- emit,
29913
- d.file.path,
29914
- d.startLine,
29915
- d.startColumn,
29916
- cssPolicyTouchTarget,
29917
- "widthTooSmall",
29918
- resolveMessage(messages125.widthTooSmall, {
29919
- property: d.property,
29920
- value: d.value.trim(),
29921
- resolved: String(Math.round(px * 100) / 100),
29922
- min: String(min),
29923
- element: kind,
29924
- policy: name
29925
- })
29926
- );
29927
- continue;
29928
- }
29929
- if (kind === "button" && HPADDING_PROPERTIES.has(prop)) {
29930
- if (px >= policy.minButtonHorizontalPadding) continue;
29931
- emitCSSDiagnostic(
29932
- emit,
29933
- d.file.path,
29934
- d.startLine,
29935
- d.startColumn,
29936
- cssPolicyTouchTarget,
29937
- "paddingTooSmall",
29938
- resolveMessage(messages125.paddingTooSmall, {
29939
- value: d.value.trim(),
29940
- resolved: String(Math.round(px * 100) / 100),
29941
- min: String(policy.minButtonHorizontalPadding),
29942
- element: kind,
29943
- policy: name
29944
- })
29945
- );
29946
- }
29947
- }
29948
- }
29949
- });
29950
- function isVisuallyHiddenInput(rule) {
29951
- if (!hasPositionAbsoluteOrFixed(rule)) return false;
29952
- if (!hasOpacityZero(rule)) return false;
29953
- return true;
29954
- }
29955
- function hasPositionAbsoluteOrFixed(rule) {
29956
- const positionDecls = rule.declarationIndex.get("position");
29957
- if (!positionDecls || positionDecls.length === 0) return false;
29958
- for (let i = 0; i < positionDecls.length; i++) {
29959
- const decl = positionDecls[i];
29960
- if (!decl) continue;
29961
- const v = decl.value.trim().toLowerCase();
29962
- if (v === "absolute" || v === "fixed") return true;
29963
- }
29964
- return false;
29965
- }
29966
- function hasOpacityZero(rule) {
29967
- const opacityDecls = rule.declarationIndex.get("opacity");
29968
- if (!opacityDecls || opacityDecls.length === 0) return false;
29969
- for (let i = 0; i < opacityDecls.length; i++) {
29970
- const decl = opacityDecls[i];
29971
- if (!decl) continue;
29972
- const v = decl.value.trim();
29973
- if (v === "0") return true;
29974
- }
29975
- return false;
29976
- }
29977
-
29978
29838
  // src/css/rules/a11y/css-policy-typography.ts
29979
- var messages126 = {
29839
+ var messages125 = {
29980
29840
  fontTooSmall: "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.",
29981
29841
  lineHeightTooSmall: "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`."
29982
29842
  };
@@ -30012,7 +29872,7 @@ function resolveLineHeightContext(d, p) {
30012
29872
  var cssPolicyTypography = defineCSSRule({
30013
29873
  id: "css-policy-typography",
30014
29874
  severity: "warn",
30015
- messages: messages126,
29875
+ messages: messages125,
30016
29876
  meta: {
30017
29877
  description: "Enforce minimum font sizes and line heights per accessibility policy.",
30018
29878
  fixable: false,
@@ -30039,7 +29899,7 @@ var cssPolicyTypography = defineCSSRule({
30039
29899
  d.startColumn,
30040
29900
  cssPolicyTypography,
30041
29901
  "fontTooSmall",
30042
- resolveMessage(messages126.fontTooSmall, {
29902
+ resolveMessage(messages125.fontTooSmall, {
30043
29903
  value: d.value.trim(),
30044
29904
  resolved: String(Math.round(px * 100) / 100),
30045
29905
  context,
@@ -30066,7 +29926,7 @@ var cssPolicyTypography = defineCSSRule({
30066
29926
  d.startColumn,
30067
29927
  cssPolicyTypography,
30068
29928
  "lineHeightTooSmall",
30069
- resolveMessage(messages126.lineHeightTooSmall, {
29929
+ resolveMessage(messages125.lineHeightTooSmall, {
30070
29930
  value: d.value.trim(),
30071
29931
  context,
30072
29932
  min: String(min),
@@ -30084,7 +29944,7 @@ var ZERO_S = /(^|\s|,)0s($|\s|,)/;
30084
29944
  var TIME_VALUE_G = /([0-9]*\.?[0-9]+)(ms|s)/g;
30085
29945
  var AMPERSAND_G = /&/g;
30086
29946
  var WHITESPACE_G = /\s+/g;
30087
- var messages127 = {
29947
+ var messages126 = {
30088
29948
  missingReducedMotion: "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
30089
29949
  };
30090
29950
  function isAnimationDecl(p) {
@@ -30188,7 +30048,7 @@ function normalizeSelector2(s) {
30188
30048
  var cssRequireReducedMotionOverride = defineCSSRule({
30189
30049
  id: "css-require-reduced-motion-override",
30190
30050
  severity: "warn",
30191
- messages: messages127,
30051
+ messages: messages126,
30192
30052
  meta: {
30193
30053
  description: "Require reduced-motion override for animated selectors.",
30194
30054
  fixable: false,
@@ -30251,20 +30111,20 @@ var cssRequireReducedMotionOverride = defineCSSRule({
30251
30111
  d.startColumn,
30252
30112
  cssRequireReducedMotionOverride,
30253
30113
  "missingReducedMotion",
30254
- resolveMessage(messages127.missingReducedMotion, { selector: r.selectorText })
30114
+ resolveMessage(messages126.missingReducedMotion, { selector: r.selectorText })
30255
30115
  );
30256
30116
  }
30257
30117
  }
30258
30118
  });
30259
30119
 
30260
30120
  // src/css/rules/structure/css-no-empty-rule.ts
30261
- var messages128 = {
30121
+ var messages127 = {
30262
30122
  emptyRule: "Empty rule `{{selector}}` should be removed."
30263
30123
  };
30264
30124
  var cssNoEmptyRule = defineCSSRule({
30265
30125
  id: "css-no-empty-rule",
30266
30126
  severity: "warn",
30267
- messages: messages128,
30127
+ messages: messages127,
30268
30128
  meta: {
30269
30129
  description: "Disallow empty CSS rules.",
30270
30130
  fixable: false,
@@ -30282,20 +30142,20 @@ var cssNoEmptyRule = defineCSSRule({
30282
30142
  rule.startColumn,
30283
30143
  cssNoEmptyRule,
30284
30144
  "emptyRule",
30285
- resolveMessage(messages128.emptyRule, { selector: rule.selectorText })
30145
+ resolveMessage(messages127.emptyRule, { selector: rule.selectorText })
30286
30146
  );
30287
30147
  }
30288
30148
  }
30289
30149
  });
30290
30150
 
30291
30151
  // src/css/rules/structure/css-no-unknown-container-name.ts
30292
- var messages129 = {
30152
+ var messages128 = {
30293
30153
  unknownContainer: "Unknown container name `{{name}}` in @container query."
30294
30154
  };
30295
30155
  var cssNoUnknownContainerName = defineCSSRule({
30296
30156
  id: "css-no-unknown-container-name",
30297
30157
  severity: "error",
30298
- messages: messages129,
30158
+ messages: messages128,
30299
30159
  meta: {
30300
30160
  description: "Disallow unknown named containers in @container queries.",
30301
30161
  fixable: false,
@@ -30315,20 +30175,20 @@ var cssNoUnknownContainerName = defineCSSRule({
30315
30175
  1,
30316
30176
  cssNoUnknownContainerName,
30317
30177
  "unknownContainer",
30318
- resolveMessage(messages129.unknownContainer, { name })
30178
+ resolveMessage(messages128.unknownContainer, { name })
30319
30179
  );
30320
30180
  }
30321
30181
  }
30322
30182
  });
30323
30183
 
30324
30184
  // src/css/rules/structure/css-no-unused-container-name.ts
30325
- var messages130 = {
30185
+ var messages129 = {
30326
30186
  unusedContainer: "Container name `{{name}}` is declared but never queried."
30327
30187
  };
30328
30188
  var cssNoUnusedContainerName = defineCSSRule({
30329
30189
  id: "css-no-unused-container-name",
30330
30190
  severity: "warn",
30331
- messages: messages130,
30191
+ messages: messages129,
30332
30192
  meta: {
30333
30193
  description: "Disallow unused named containers.",
30334
30194
  fixable: false,
@@ -30351,7 +30211,7 @@ var cssNoUnusedContainerName = defineCSSRule({
30351
30211
  d.startColumn,
30352
30212
  cssNoUnusedContainerName,
30353
30213
  "unusedContainer",
30354
- resolveMessage(messages130.unusedContainer, { name })
30214
+ resolveMessage(messages129.unusedContainer, { name })
30355
30215
  );
30356
30216
  }
30357
30217
  }
@@ -30359,13 +30219,13 @@ var cssNoUnusedContainerName = defineCSSRule({
30359
30219
  });
30360
30220
 
30361
30221
  // src/css/rules/structure/layer-requirement-for-component-rules.ts
30362
- var messages131 = {
30222
+ var messages130 = {
30363
30223
  missingLayer: "Rule `{{selector}}` is not inside any @layer block while this file uses @layer. Place component rules inside an explicit layer."
30364
30224
  };
30365
30225
  var layerRequirementForComponentRules = defineCSSRule({
30366
30226
  id: "layer-requirement-for-component-rules",
30367
30227
  severity: "warn",
30368
- messages: messages131,
30228
+ messages: messages130,
30369
30229
  meta: {
30370
30230
  description: "Require style rules to be inside @layer when the file defines layers.",
30371
30231
  fixable: false,
@@ -30384,7 +30244,7 @@ var layerRequirementForComponentRules = defineCSSRule({
30384
30244
  rule.startColumn,
30385
30245
  layerRequirementForComponentRules,
30386
30246
  "missingLayer",
30387
- resolveMessage(messages131.missingLayer, {
30247
+ resolveMessage(messages130.missingLayer, {
30388
30248
  selector: rule.selectorText
30389
30249
  })
30390
30250
  );
@@ -30424,7 +30284,6 @@ var rules2 = [
30424
30284
  selectorMaxAttributeAndUniversal,
30425
30285
  selectorMaxSpecificity,
30426
30286
  cssPolicyTypography,
30427
- cssPolicyTouchTarget,
30428
30287
  cssPolicySpacing,
30429
30288
  cssPolicyContrast
30430
30289
  ];
@@ -30761,21 +30620,34 @@ function detectTailwindEntry(files) {
30761
30620
  }
30762
30621
  return themeOnlyFallback;
30763
30622
  }
30764
- async function resolveTailwindValidator(files) {
30623
+ async function resolveTailwindValidator(files, logger) {
30765
30624
  const entry = detectTailwindEntry(files);
30766
- if (!entry) return null;
30625
+ if (!entry) {
30626
+ logger?.info('tailwind: no entry file detected (no @import "tailwindcss" or @theme block found in CSS files)');
30627
+ return null;
30628
+ }
30629
+ logger?.info(`tailwind: entry file detected: ${entry.path}`);
30767
30630
  try {
30768
30631
  const base = (0, import_node_path2.dirname)(entry.path);
30769
30632
  const resolved = resolveTailwindNodePath(base);
30770
- if (resolved === null) return null;
30633
+ if (resolved === null) {
30634
+ logger?.warning(`tailwind: @tailwindcss/node not resolvable walking up from ${base}`);
30635
+ return null;
30636
+ }
30637
+ logger?.info(`tailwind: @tailwindcss/node resolved to ${resolved}`);
30771
30638
  const mod = await import(resolved);
30772
- if (typeof mod.__unstable__loadDesignSystem !== "function") return null;
30639
+ if (typeof mod.__unstable__loadDesignSystem !== "function") {
30640
+ logger?.warning("tailwind: @tailwindcss/node module missing __unstable__loadDesignSystem export");
30641
+ return null;
30642
+ }
30773
30643
  const design = await mod.__unstable__loadDesignSystem(
30774
30644
  entry.content,
30775
30645
  { base }
30776
30646
  );
30647
+ logger?.info("tailwind: design system loaded successfully");
30777
30648
  return createLiveValidator(design);
30778
- } catch {
30649
+ } catch (err) {
30650
+ logger?.warning(`tailwind: failed to load design system: ${err instanceof Error ? err.message : String(err)}`);
30779
30651
  return null;
30780
30652
  }
30781
30653
  }
@@ -39580,13 +39452,13 @@ function emitLayoutDiagnostic(layout, node, emit, ruleId, messageId, template, s
39580
39452
  }
39581
39453
 
39582
39454
  // src/cross-file/rules/undefined-css-class.ts
39583
- var messages132 = {
39455
+ var messages131 = {
39584
39456
  undefinedClass: "CSS class '{{className}}' is not defined in project CSS files"
39585
39457
  };
39586
39458
  var jsxNoUndefinedCssClass = defineCrossRule({
39587
39459
  id: "jsx-no-undefined-css-class",
39588
39460
  severity: "error",
39589
- messages: messages132,
39461
+ messages: messages131,
39590
39462
  meta: {
39591
39463
  description: "Detect undefined CSS class names in JSX",
39592
39464
  fixable: false,
@@ -39605,7 +39477,7 @@ var jsxNoUndefinedCssClass = defineCrossRule({
39605
39477
  ref.solid.sourceFile,
39606
39478
  jsxNoUndefinedCssClass.id,
39607
39479
  "undefinedClass",
39608
- resolveMessage(messages132.undefinedClass, { className: item.className }),
39480
+ resolveMessage(messages131.undefinedClass, { className: item.className }),
39609
39481
  "error"
39610
39482
  ));
39611
39483
  }
@@ -39613,13 +39485,13 @@ var jsxNoUndefinedCssClass = defineCrossRule({
39613
39485
  });
39614
39486
 
39615
39487
  // src/cross-file/rules/unreferenced-css-class.ts
39616
- var messages133 = {
39488
+ var messages132 = {
39617
39489
  unreferencedClass: "CSS class '{{className}}' is defined but not referenced by static JSX class attributes"
39618
39490
  };
39619
39491
  var cssNoUnreferencedComponentClass = defineCrossRule({
39620
39492
  id: "css-no-unreferenced-component-class",
39621
39493
  severity: "warn",
39622
- messages: messages133,
39494
+ messages: messages132,
39623
39495
  meta: {
39624
39496
  description: "Detect CSS classes that are never referenced by static JSX class attributes.",
39625
39497
  fixable: false,
@@ -39643,7 +39515,7 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
39643
39515
  },
39644
39516
  cssNoUnreferencedComponentClass.id,
39645
39517
  "unreferencedClass",
39646
- resolveMessage(messages133.unreferencedClass, { className }),
39518
+ resolveMessage(messages132.unreferencedClass, { className }),
39647
39519
  "warn"
39648
39520
  )
39649
39521
  );
@@ -39652,13 +39524,13 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
39652
39524
  });
39653
39525
 
39654
39526
  // src/cross-file/rules/jsx-no-duplicate-class-token-class-classlist.ts
39655
- var messages134 = {
39527
+ var messages133 = {
39656
39528
  duplicateClassToken: "Class token `{{name}}` appears in both class and classList."
39657
39529
  };
39658
39530
  var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
39659
39531
  id: "jsx-no-duplicate-class-token-class-classlist",
39660
39532
  severity: "warn",
39661
- messages: messages134,
39533
+ messages: messages133,
39662
39534
  meta: {
39663
39535
  description: "Disallow duplicate class tokens between class and classList on the same JSX element.",
39664
39536
  fixable: false,
@@ -39700,7 +39572,7 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
39700
39572
  solid.sourceFile,
39701
39573
  jsxNoDuplicateClassTokenClassClasslist.id,
39702
39574
  "duplicateClassToken",
39703
- resolveMessage(messages134.duplicateClassToken, { name: token }),
39575
+ resolveMessage(messages133.duplicateClassToken, { name: token }),
39704
39576
  "warn"
39705
39577
  ));
39706
39578
  }
@@ -39711,13 +39583,13 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
39711
39583
 
39712
39584
  // src/cross-file/rules/jsx-classlist-static-keys.ts
39713
39585
  var import_typescript128 = __toESM(require("typescript"), 1);
39714
- var messages135 = {
39586
+ var messages134 = {
39715
39587
  nonStaticKey: "classList key must be statically known for reliable class mapping."
39716
39588
  };
39717
39589
  var jsxClasslistStaticKeys = defineCrossRule({
39718
39590
  id: "jsx-classlist-static-keys",
39719
39591
  severity: "error",
39720
- messages: messages135,
39592
+ messages: messages134,
39721
39593
  meta: {
39722
39594
  description: "Require classList keys to be static and non-computed.",
39723
39595
  fixable: false,
@@ -39728,26 +39600,26 @@ var jsxClasslistStaticKeys = defineCrossRule({
39728
39600
  forEachClassListPropertyAcross(solids, (solid, p) => {
39729
39601
  if (import_typescript128.default.isSpreadAssignment(p)) return;
39730
39602
  if (!import_typescript128.default.isPropertyAssignment(p)) {
39731
- emit(createDiagnostic(solid.file, p, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages135.nonStaticKey), "error"));
39603
+ emit(createDiagnostic(solid.file, p, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages134.nonStaticKey), "error"));
39732
39604
  return;
39733
39605
  }
39734
39606
  if (import_typescript128.default.isComputedPropertyName(p.name)) return;
39735
39607
  if (import_typescript128.default.isIdentifier(p.name)) return;
39736
39608
  if (import_typescript128.default.isStringLiteral(p.name)) return;
39737
- emit(createDiagnostic(solid.file, p.name, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages135.nonStaticKey), "error"));
39609
+ emit(createDiagnostic(solid.file, p.name, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages134.nonStaticKey), "error"));
39738
39610
  });
39739
39611
  }
39740
39612
  });
39741
39613
 
39742
39614
  // src/cross-file/rules/jsx-classlist-no-constant-literals.ts
39743
39615
  var import_typescript129 = __toESM(require("typescript"), 1);
39744
- var messages136 = {
39616
+ var messages135 = {
39745
39617
  constantEntry: "classList entry `{{name}}: {{value}}` is constant; move it to static class."
39746
39618
  };
39747
39619
  var jsxClasslistNoConstantLiterals = defineCrossRule({
39748
39620
  id: "jsx-classlist-no-constant-literals",
39749
39621
  severity: "warn",
39750
- messages: messages136,
39622
+ messages: messages135,
39751
39623
  meta: {
39752
39624
  description: "Disallow classList entries with constant true/false values.",
39753
39625
  fixable: false,
@@ -39767,7 +39639,7 @@ var jsxClasslistNoConstantLiterals = defineCrossRule({
39767
39639
  solid.sourceFile,
39768
39640
  jsxClasslistNoConstantLiterals.id,
39769
39641
  "constantEntry",
39770
- resolveMessage(messages136.constantEntry, { name: n, value: val.kind === import_typescript129.default.SyntaxKind.TrueKeyword ? "true" : "false" }),
39642
+ resolveMessage(messages135.constantEntry, { name: n, value: val.kind === import_typescript129.default.SyntaxKind.TrueKeyword ? "true" : "false" }),
39771
39643
  "warn"
39772
39644
  ));
39773
39645
  });
@@ -39802,13 +39674,13 @@ function isDefinitelyNonBooleanType(solid, node) {
39802
39674
  }
39803
39675
 
39804
39676
  // src/cross-file/rules/jsx-classlist-boolean-values.ts
39805
- var messages137 = {
39677
+ var messages136 = {
39806
39678
  nonBooleanValue: "classList value for `{{name}}` must be boolean."
39807
39679
  };
39808
39680
  var jsxClasslistBooleanValues = defineCrossRule({
39809
39681
  id: "jsx-classlist-boolean-values",
39810
39682
  severity: "error",
39811
- messages: messages137,
39683
+ messages: messages136,
39812
39684
  meta: {
39813
39685
  description: "Require classList values to be boolean-like expressions.",
39814
39686
  fixable: false,
@@ -39822,25 +39694,25 @@ var jsxClasslistBooleanValues = defineCrossRule({
39822
39694
  if (!n) return;
39823
39695
  if (isBooleanish(p.initializer)) return;
39824
39696
  if (isDefinitelyNonBoolean(p.initializer)) {
39825
- emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages137.nonBooleanValue, { name: n }), "error"));
39697
+ emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages136.nonBooleanValue, { name: n }), "error"));
39826
39698
  return;
39827
39699
  }
39828
39700
  if (isBooleanType(solid, p.initializer)) return;
39829
39701
  if (!isDefinitelyNonBooleanType(solid, p.initializer)) return;
39830
- emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages137.nonBooleanValue, { name: n }), "error"));
39702
+ emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages136.nonBooleanValue, { name: n }), "error"));
39831
39703
  });
39832
39704
  }
39833
39705
  });
39834
39706
 
39835
39707
  // src/cross-file/rules/jsx-classlist-no-accessor-reference.ts
39836
39708
  var import_typescript131 = __toESM(require("typescript"), 1);
39837
- var messages138 = {
39709
+ var messages137 = {
39838
39710
  accessorReference: "Signal accessor `{{name}}` must be called in classList value (use {{name}}())."
39839
39711
  };
39840
39712
  var jsxClasslistNoAccessorReference = defineCrossRule({
39841
39713
  id: "jsx-classlist-no-accessor-reference",
39842
39714
  severity: "error",
39843
- messages: messages138,
39715
+ messages: messages137,
39844
39716
  meta: {
39845
39717
  description: "Disallow passing accessor references directly as classList values.",
39846
39718
  fixable: false,
@@ -39872,7 +39744,7 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
39872
39744
  solid.sourceFile,
39873
39745
  jsxClasslistNoAccessorReference.id,
39874
39746
  "accessorReference",
39875
- resolveMessage(messages138.accessorReference, { name: v.text }),
39747
+ resolveMessage(messages137.accessorReference, { name: v.text }),
39876
39748
  "error"
39877
39749
  ));
39878
39750
  });
@@ -39881,13 +39753,13 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
39881
39753
 
39882
39754
  // src/cross-file/rules/jsx-style-kebab-case-keys.ts
39883
39755
  var import_typescript132 = __toESM(require("typescript"), 1);
39884
- var messages139 = {
39756
+ var messages138 = {
39885
39757
  kebabStyleKey: "Style key `{{name}}` should be `{{kebab}}` in Solid style objects."
39886
39758
  };
39887
39759
  var jsxStyleKebabCaseKeys = defineCrossRule({
39888
39760
  id: "jsx-style-kebab-case-keys",
39889
39761
  severity: "error",
39890
- messages: messages139,
39762
+ messages: messages138,
39891
39763
  meta: {
39892
39764
  description: "Require kebab-case keys in JSX style object literals.",
39893
39765
  fixable: false,
@@ -39908,7 +39780,7 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
39908
39780
  solid.sourceFile,
39909
39781
  jsxStyleKebabCaseKeys.id,
39910
39782
  "kebabStyleKey",
39911
- resolveMessage(messages139.kebabStyleKey, { name: n, kebab }),
39783
+ resolveMessage(messages138.kebabStyleKey, { name: n, kebab }),
39912
39784
  "error"
39913
39785
  ));
39914
39786
  });
@@ -39917,13 +39789,13 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
39917
39789
 
39918
39790
  // src/cross-file/rules/jsx-style-no-function-values.ts
39919
39791
  var import_typescript133 = __toESM(require("typescript"), 1);
39920
- var messages140 = {
39792
+ var messages139 = {
39921
39793
  functionStyleValue: "Style value for `{{name}}` is a function; pass computed value instead."
39922
39794
  };
39923
39795
  var jsxStyleNoFunctionValues = defineCrossRule({
39924
39796
  id: "jsx-style-no-function-values",
39925
39797
  severity: "error",
39926
- messages: messages140,
39798
+ messages: messages139,
39927
39799
  meta: {
39928
39800
  description: "Disallow function values in JSX style objects.",
39929
39801
  fixable: false,
@@ -39937,11 +39809,11 @@ var jsxStyleNoFunctionValues = defineCrossRule({
39937
39809
  if (!n) return;
39938
39810
  const v = p.initializer;
39939
39811
  if (import_typescript133.default.isArrowFunction(v) || import_typescript133.default.isFunctionExpression(v)) {
39940
- emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages140.functionStyleValue, { name: n }), "error"));
39812
+ emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages139.functionStyleValue, { name: n }), "error"));
39941
39813
  return;
39942
39814
  }
39943
39815
  if (import_typescript133.default.isIdentifier(v) && solid.typeResolver.isCallableType(v)) {
39944
- emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages140.functionStyleValue, { name: n }), "error"));
39816
+ emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages139.functionStyleValue, { name: n }), "error"));
39945
39817
  }
39946
39818
  });
39947
39819
  }
@@ -39949,13 +39821,13 @@ var jsxStyleNoFunctionValues = defineCrossRule({
39949
39821
 
39950
39822
  // src/cross-file/rules/jsx-style-no-unused-custom-prop.ts
39951
39823
  var import_typescript134 = __toESM(require("typescript"), 1);
39952
- var messages141 = {
39824
+ var messages140 = {
39953
39825
  unusedInlineVar: "Inline custom property `{{name}}` is never read via var({{name}})."
39954
39826
  };
39955
39827
  var jsxStyleNoUnusedCustomProp = defineCrossRule({
39956
39828
  id: "jsx-style-no-unused-custom-prop",
39957
39829
  severity: "warn",
39958
- messages: messages141,
39830
+ messages: messages140,
39959
39831
  meta: {
39960
39832
  description: "Detect inline style custom properties that are never consumed by CSS var() references.",
39961
39833
  fixable: false,
@@ -39996,7 +39868,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
39996
39868
  solid.sourceFile,
39997
39869
  jsxStyleNoUnusedCustomProp.id,
39998
39870
  "unusedInlineVar",
39999
- resolveMessage(messages141.unusedInlineVar, { name: n }),
39871
+ resolveMessage(messages140.unusedInlineVar, { name: n }),
40000
39872
  "warn"
40001
39873
  ));
40002
39874
  }
@@ -40006,7 +39878,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
40006
39878
 
40007
39879
  // src/cross-file/rules/jsx-style-policy.ts
40008
39880
  var import_typescript135 = __toESM(require("typescript"), 1);
40009
- var messages142 = {
39881
+ var messages141 = {
40010
39882
  fontTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for policy `{{policy}}`.",
40011
39883
  lineHeightTooSmall: "Inline style `line-height: {{value}}` is below the minimum `{{min}}` for policy `{{policy}}`.",
40012
39884
  heightTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for interactive elements in policy `{{policy}}`.",
@@ -40053,7 +39925,7 @@ function readNodeHostElementRef(layout, solid, element) {
40053
39925
  var jsxStylePolicy = defineCrossRule({
40054
39926
  id: "jsx-style-policy",
40055
39927
  severity: "warn",
40056
- messages: messages142,
39928
+ messages: messages141,
40057
39929
  meta: {
40058
39930
  description: "Enforce accessibility policy thresholds on inline JSX style objects.",
40059
39931
  fixable: false,
@@ -40080,7 +39952,7 @@ var jsxStylePolicy = defineCrossRule({
40080
39952
  solid.sourceFile,
40081
39953
  jsxStylePolicy.id,
40082
39954
  "fontTooSmall",
40083
- resolveMessage(messages142.fontTooSmall, {
39955
+ resolveMessage(messages141.fontTooSmall, {
40084
39956
  prop: key,
40085
39957
  value: strVal,
40086
39958
  resolved: formatRounded(px),
@@ -40102,7 +39974,7 @@ var jsxStylePolicy = defineCrossRule({
40102
39974
  solid.sourceFile,
40103
39975
  jsxStylePolicy.id,
40104
39976
  "lineHeightTooSmall",
40105
- resolveMessage(messages142.lineHeightTooSmall, {
39977
+ resolveMessage(messages141.lineHeightTooSmall, {
40106
39978
  value: String(lh),
40107
39979
  min: String(policy.minLineHeight),
40108
39980
  policy: name
@@ -40126,7 +39998,7 @@ var jsxStylePolicy = defineCrossRule({
40126
39998
  solid.sourceFile,
40127
39999
  jsxStylePolicy.id,
40128
40000
  "heightTooSmall",
40129
- resolveMessage(messages142.heightTooSmall, {
40001
+ resolveMessage(messages141.heightTooSmall, {
40130
40002
  prop: key,
40131
40003
  value: strVal,
40132
40004
  resolved: formatRounded(px),
@@ -40148,7 +40020,7 @@ var jsxStylePolicy = defineCrossRule({
40148
40020
  solid.sourceFile,
40149
40021
  jsxStylePolicy.id,
40150
40022
  "letterSpacingTooSmall",
40151
- resolveMessage(messages142.letterSpacingTooSmall, {
40023
+ resolveMessage(messages141.letterSpacingTooSmall, {
40152
40024
  value: strVal,
40153
40025
  resolved: String(em),
40154
40026
  min: String(policy.minLetterSpacing),
@@ -40169,7 +40041,7 @@ var jsxStylePolicy = defineCrossRule({
40169
40041
  solid.sourceFile,
40170
40042
  jsxStylePolicy.id,
40171
40043
  "wordSpacingTooSmall",
40172
- resolveMessage(messages142.wordSpacingTooSmall, {
40044
+ resolveMessage(messages141.wordSpacingTooSmall, {
40173
40045
  value: strVal,
40174
40046
  resolved: String(em),
40175
40047
  min: String(policy.minWordSpacing),
@@ -40183,7 +40055,7 @@ var jsxStylePolicy = defineCrossRule({
40183
40055
  });
40184
40056
 
40185
40057
  // src/cross-file/rules/css-layout-sibling-alignment-outlier.ts
40186
- var messages143 = {
40058
+ var messages142 = {
40187
40059
  misalignedSibling: "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}"
40188
40060
  };
40189
40061
  var MIN_CONFIDENCE_THRESHOLD = 0.48;
@@ -40231,7 +40103,7 @@ var siblingAlignmentDetector = {
40231
40103
  var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
40232
40104
  id: "css-layout-sibling-alignment-outlier",
40233
40105
  severity: "warn",
40234
- messages: messages143,
40106
+ messages: messages142,
40235
40107
  meta: {
40236
40108
  description: "Detect vertical alignment outliers between sibling elements in shared layout containers.",
40237
40109
  fixable: false,
@@ -40315,7 +40187,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
40315
40187
  subjectRef.solid.sourceFile,
40316
40188
  cssLayoutSiblingAlignmentOutlier.id,
40317
40189
  "misalignedSibling",
40318
- resolveMessage(messages143.misalignedSibling, {
40190
+ resolveMessage(messages142.misalignedSibling, {
40319
40191
  subject,
40320
40192
  parent,
40321
40193
  fix,
@@ -40399,13 +40271,13 @@ function hasNonOffsetPrimaryEvidence(topFactors) {
40399
40271
  }
40400
40272
 
40401
40273
  // src/cross-file/rules/css-layout-transition-layout-property.ts
40402
- var messages144 = {
40274
+ var messages143 = {
40403
40275
  transitionLayoutProperty: "Transition '{{property}}' in '{{declaration}}' animates layout-affecting geometry. Prefer transform/opacity to avoid CLS."
40404
40276
  };
40405
40277
  var cssLayoutTransitionLayoutProperty = defineCrossRule({
40406
40278
  id: "css-layout-transition-layout-property",
40407
40279
  severity: "warn",
40408
- messages: messages144,
40280
+ messages: messages143,
40409
40281
  meta: {
40410
40282
  description: "Disallow transitions that animate layout-affecting geometry properties.",
40411
40283
  fixable: false,
@@ -40429,7 +40301,7 @@ var cssLayoutTransitionLayoutProperty = defineCrossRule({
40429
40301
  },
40430
40302
  cssLayoutTransitionLayoutProperty.id,
40431
40303
  "transitionLayoutProperty",
40432
- resolveMessage(messages144.transitionLayoutProperty, {
40304
+ resolveMessage(messages143.transitionLayoutProperty, {
40433
40305
  property: target,
40434
40306
  declaration: declaration.property
40435
40307
  }),
@@ -40444,13 +40316,13 @@ function findLayoutTransitionTarget(raw) {
40444
40316
  }
40445
40317
 
40446
40318
  // src/cross-file/rules/css-layout-animation-layout-property.ts
40447
- var messages145 = {
40319
+ var messages144 = {
40448
40320
  animationLayoutProperty: "Animation '{{animation}}' mutates layout-affecting '{{property}}', which can trigger CLS. Prefer transform/opacity or reserve geometry."
40449
40321
  };
40450
40322
  var cssLayoutAnimationLayoutProperty = defineCrossRule({
40451
40323
  id: "css-layout-animation-layout-property",
40452
40324
  severity: "warn",
40453
- messages: messages145,
40325
+ messages: messages144,
40454
40326
  meta: {
40455
40327
  description: "Disallow keyframe animations that mutate layout-affecting properties and can trigger CLS.",
40456
40328
  fixable: false,
@@ -40478,7 +40350,7 @@ var cssLayoutAnimationLayoutProperty = defineCrossRule({
40478
40350
  },
40479
40351
  cssLayoutAnimationLayoutProperty.id,
40480
40352
  "animationLayoutProperty",
40481
- resolveMessage(messages145.animationLayoutProperty, {
40353
+ resolveMessage(messages144.animationLayoutProperty, {
40482
40354
  animation: match.name,
40483
40355
  property: match.property
40484
40356
  }),
@@ -40548,13 +40420,13 @@ function firstRiskyAnimationName(names, riskyKeyframes) {
40548
40420
  }
40549
40421
 
40550
40422
  // src/cross-file/rules/css-layout-stateful-box-model-shift.ts
40551
- var messages146 = {
40423
+ var messages145 = {
40552
40424
  statefulBoxModelShift: "State selector '{{selector}}' changes layout-affecting '{{property}}'. Keep geometry stable across states to avoid CLS."
40553
40425
  };
40554
40426
  var cssLayoutStatefulBoxModelShift = defineCrossRule({
40555
40427
  id: "css-layout-stateful-box-model-shift",
40556
40428
  severity: "warn",
40557
- messages: messages146,
40429
+ messages: messages145,
40558
40430
  meta: {
40559
40431
  description: "Disallow stateful selector changes that alter element geometry and trigger layout shifts.",
40560
40432
  fixable: false,
@@ -40595,7 +40467,7 @@ var cssLayoutStatefulBoxModelShift = defineCrossRule({
40595
40467
  },
40596
40468
  cssLayoutStatefulBoxModelShift.id,
40597
40469
  "statefulBoxModelShift",
40598
- resolveMessage(messages146.statefulBoxModelShift, {
40470
+ resolveMessage(messages145.statefulBoxModelShift, {
40599
40471
  selector: match.raw,
40600
40472
  property: declaration.property
40601
40473
  }),
@@ -40689,14 +40561,14 @@ function lookupBaseByProperty(baseValueIndex, selectorKeys) {
40689
40561
 
40690
40562
  // src/cross-file/rules/css-layout-unsized-replaced-element.ts
40691
40563
  var import_typescript136 = __toESM(require("typescript"), 1);
40692
- var messages147 = {
40564
+ var messages146 = {
40693
40565
  unsizedReplacedElement: "Replaced element '{{tag}}' has no stable reserved size (width/height or aspect-ratio with a dimension), which can cause CLS."
40694
40566
  };
40695
40567
  var REPLACED_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "iframe", "canvas", "svg"]);
40696
40568
  var cssLayoutUnsizedReplacedElement = defineCrossRule({
40697
40569
  id: "css-layout-unsized-replaced-element",
40698
40570
  severity: "warn",
40699
- messages: messages147,
40571
+ messages: messages146,
40700
40572
  meta: {
40701
40573
  description: "Require stable reserved geometry for replaced media elements to prevent layout shifts.",
40702
40574
  fixable: false,
@@ -40721,7 +40593,7 @@ var cssLayoutUnsizedReplacedElement = defineCrossRule({
40721
40593
  ref.solid.sourceFile,
40722
40594
  cssLayoutUnsizedReplacedElement.id,
40723
40595
  "unsizedReplacedElement",
40724
- resolveMessage(messages147.unsizedReplacedElement, { tag }),
40596
+ resolveMessage(messages146.unsizedReplacedElement, { tag }),
40725
40597
  "warn"
40726
40598
  )
40727
40599
  );
@@ -40774,7 +40646,7 @@ function readPositiveJsxAttribute(solid, element, name) {
40774
40646
  }
40775
40647
 
40776
40648
  // src/cross-file/rules/css-layout-dynamic-slot-no-reserved-space.ts
40777
- var messages148 = {
40649
+ var messages147 = {
40778
40650
  dynamicSlotNoReservedSpace: "Dynamic content container '{{tag}}' does not reserve block space (min-height/height/aspect-ratio/contain-intrinsic-size), which can cause CLS."
40779
40651
  };
40780
40652
  var INLINE_DISPLAYS = /* @__PURE__ */ new Set(["inline", "contents"]);
@@ -40796,7 +40668,7 @@ function hasOutOfFlowAncestor(layout, node) {
40796
40668
  var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
40797
40669
  id: "css-layout-dynamic-slot-no-reserved-space",
40798
40670
  severity: "warn",
40799
- messages: messages148,
40671
+ messages: messages147,
40800
40672
  meta: {
40801
40673
  description: "Require reserved block space for dynamic content containers to avoid layout shifts.",
40802
40674
  fixable: false,
@@ -40819,19 +40691,19 @@ var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
40819
40691
  const reservedSpace = readReservedSpaceFact(context.layout, node);
40820
40692
  if (reservedSpace.hasReservedSpace) continue;
40821
40693
  if (hasBlockAxisPadding(snapshot)) continue;
40822
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace", messages148.dynamicSlotNoReservedSpace, cssLayoutDynamicSlotNoReservedSpace.severity)) continue;
40694
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace", messages147.dynamicSlotNoReservedSpace, cssLayoutDynamicSlotNoReservedSpace.severity)) continue;
40823
40695
  }
40824
40696
  }
40825
40697
  });
40826
40698
 
40827
40699
  // src/cross-file/rules/css-layout-scrollbar-gutter-instability.ts
40828
- var messages149 = {
40700
+ var messages148 = {
40829
40701
  missingScrollbarGutter: "Scrollable container '{{tag}}' uses overflow auto/scroll without `scrollbar-gutter: stable`, which can trigger CLS when scrollbars appear."
40830
40702
  };
40831
40703
  var cssLayoutScrollbarGutterInstability = defineCrossRule({
40832
40704
  id: "css-layout-scrollbar-gutter-instability",
40833
40705
  severity: "warn",
40834
- messages: messages149,
40706
+ messages: messages148,
40835
40707
  meta: {
40836
40708
  description: "Require stable scrollbar gutters for scrollable containers to reduce layout shifts.",
40837
40709
  fixable: false,
@@ -40850,19 +40722,19 @@ var cssLayoutScrollbarGutterInstability = defineCrossRule({
40850
40722
  if (scrollbarWidth === "none") continue;
40851
40723
  const gutter = readKnownNormalized(snapshot, "scrollbar-gutter");
40852
40724
  if (gutter !== null && gutter.startsWith("stable")) continue;
40853
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter", messages149.missingScrollbarGutter, cssLayoutScrollbarGutterInstability.severity)) continue;
40725
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter", messages148.missingScrollbarGutter, cssLayoutScrollbarGutterInstability.severity)) continue;
40854
40726
  }
40855
40727
  }
40856
40728
  });
40857
40729
 
40858
40730
  // src/cross-file/rules/css-layout-overflow-anchor-instability.ts
40859
- var messages150 = {
40731
+ var messages149 = {
40860
40732
  unstableOverflowAnchor: "Element '{{tag}}' sets `overflow-anchor: none` on a {{context}} container; disabling scroll anchoring can amplify visible layout shifts."
40861
40733
  };
40862
40734
  var cssLayoutOverflowAnchorInstability = defineCrossRule({
40863
40735
  id: "css-layout-overflow-anchor-instability",
40864
40736
  severity: "warn",
40865
- messages: messages150,
40737
+ messages: messages149,
40866
40738
  meta: {
40867
40739
  description: "Disallow overflow-anchor none on dynamic or scrollable containers prone to visible layout shifts.",
40868
40740
  fixable: false,
@@ -40880,20 +40752,20 @@ var cssLayoutOverflowAnchorInstability = defineCrossRule({
40880
40752
  const isDynamicContainer = isDynamicContainerLike(node);
40881
40753
  if (!isScrollable && !isDynamicContainer) continue;
40882
40754
  const containerContext = isScrollable ? "scrollable" : "dynamic";
40883
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowAnchorInstability.id, "unstableOverflowAnchor", messages150.unstableOverflowAnchor, cssLayoutOverflowAnchorInstability.severity, { context: containerContext })) continue;
40755
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowAnchorInstability.id, "unstableOverflowAnchor", messages149.unstableOverflowAnchor, cssLayoutOverflowAnchorInstability.severity, { context: containerContext })) continue;
40884
40756
  }
40885
40757
  }
40886
40758
  });
40887
40759
 
40888
40760
  // src/cross-file/rules/css-layout-font-swap-instability.ts
40889
- var messages151 = {
40761
+ var messages150 = {
40890
40762
  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."
40891
40763
  };
40892
40764
  var SWAP_DISPLAYS = /* @__PURE__ */ new Set(["swap", "fallback"]);
40893
40765
  var cssLayoutFontSwapInstability = defineCrossRule({
40894
40766
  id: "css-layout-font-swap-instability",
40895
40767
  severity: "warn",
40896
- messages: messages151,
40768
+ messages: messages150,
40897
40769
  meta: {
40898
40770
  description: "Require metric overrides for swapping webfonts to reduce layout shifts during font load.",
40899
40771
  fixable: false,
@@ -40940,7 +40812,7 @@ var cssLayoutFontSwapInstability = defineCrossRule({
40940
40812
  },
40941
40813
  cssLayoutFontSwapInstability.id,
40942
40814
  "unstableFontSwap",
40943
- resolveMessage(messages151.unstableFontSwap, {
40815
+ resolveMessage(messages150.unstableFontSwap, {
40944
40816
  family,
40945
40817
  display: report.display
40946
40818
  }),
@@ -40953,14 +40825,14 @@ var cssLayoutFontSwapInstability = defineCrossRule({
40953
40825
  });
40954
40826
 
40955
40827
  // src/cross-file/rules/css-layout-conditional-display-collapse.ts
40956
- var messages152 = {
40828
+ var messages151 = {
40957
40829
  conditionalDisplayCollapse: "Conditional display sets '{{display}}' on '{{tag}}' without stable reserved space, which can collapse/expand layout and cause CLS."
40958
40830
  };
40959
40831
  var COLLAPSING_DISPLAYS = /* @__PURE__ */ new Set(["none", "contents"]);
40960
40832
  var cssLayoutConditionalDisplayCollapse = defineCrossRule({
40961
40833
  id: "css-layout-conditional-display-collapse",
40962
40834
  severity: "warn",
40963
- messages: messages152,
40835
+ messages: messages151,
40964
40836
  meta: {
40965
40837
  description: "Disallow conditional display collapse in flow without reserved geometry.",
40966
40838
  fixable: false,
@@ -40981,13 +40853,13 @@ var cssLayoutConditionalDisplayCollapse = defineCrossRule({
40981
40853
  if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
40982
40854
  const reservedSpace = readReservedSpaceFact(context.layout, node);
40983
40855
  if (reservedSpace.hasReservedSpace) continue;
40984
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse", messages152.conditionalDisplayCollapse, cssLayoutConditionalDisplayCollapse.severity, { display })) continue;
40856
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse", messages151.conditionalDisplayCollapse, cssLayoutConditionalDisplayCollapse.severity, { display })) continue;
40985
40857
  }
40986
40858
  }
40987
40859
  });
40988
40860
 
40989
40861
  // src/cross-file/rules/css-layout-conditional-white-space-wrap-shift.ts
40990
- var messages153 = {
40862
+ var messages152 = {
40991
40863
  conditionalWhiteSpaceShift: "Conditional white-space '{{whiteSpace}}' on '{{tag}}' can reflow text and shift siblings; keep wrapping behavior stable or reserve geometry."
40992
40864
  };
40993
40865
  var WRAP_SHIFT_VALUES = /* @__PURE__ */ new Set(["nowrap", "pre"]);
@@ -40996,7 +40868,7 @@ var BLOCK_SIZE_PROPERTIES = ["height", "min-height"];
40996
40868
  var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
40997
40869
  id: "css-layout-conditional-white-space-wrap-shift",
40998
40870
  severity: "warn",
40999
- messages: messages153,
40871
+ messages: messages152,
41000
40872
  meta: {
41001
40873
  description: "Disallow conditional white-space wrapping mode toggles that can trigger CLS.",
41002
40874
  fixable: false,
@@ -41019,7 +40891,7 @@ var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
41019
40891
  if (!flow.inFlow) continue;
41020
40892
  if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
41021
40893
  if (hasStableTextShell(snapshot)) continue;
41022
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift", messages153.conditionalWhiteSpaceShift, cssLayoutConditionalWhiteSpaceWrapShift.severity, { whiteSpace })) continue;
40894
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift", messages152.conditionalWhiteSpaceShift, cssLayoutConditionalWhiteSpaceWrapShift.severity, { whiteSpace })) continue;
41023
40895
  }
41024
40896
  }
41025
40897
  });
@@ -41041,13 +40913,13 @@ function hasWrapShiftDelta(delta) {
41041
40913
  }
41042
40914
 
41043
40915
  // src/cross-file/rules/css-layout-overflow-mode-toggle-instability.ts
41044
- var messages154 = {
40916
+ var messages153 = {
41045
40917
  overflowModeToggle: "Conditional overflow mode changes scrolling ('{{overflow}}') on '{{tag}}' without `scrollbar-gutter: stable`, which can trigger CLS."
41046
40918
  };
41047
40919
  var cssLayoutOverflowModeToggleInstability = defineCrossRule({
41048
40920
  id: "css-layout-overflow-mode-toggle-instability",
41049
40921
  severity: "warn",
41050
- messages: messages154,
40922
+ messages: messages153,
41051
40923
  meta: {
41052
40924
  description: "Disallow conditional overflow mode switches that can introduce scrollbar-induced layout shifts.",
41053
40925
  fixable: false,
@@ -41072,7 +40944,7 @@ var cssLayoutOverflowModeToggleInstability = defineCrossRule({
41072
40944
  const gutter = readKnownNormalizedWithGuard(snapshot, "scrollbar-gutter");
41073
40945
  if (gutter !== null && gutter.startsWith("stable")) continue;
41074
40946
  const overflowValue = scrollFact.overflowY ?? scrollFact.overflow ?? "auto";
41075
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowModeToggleInstability.id, "overflowModeToggle", messages154.overflowModeToggle, cssLayoutOverflowModeToggleInstability.severity, { overflow: overflowValue })) continue;
40947
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowModeToggleInstability.id, "overflowModeToggle", messages153.overflowModeToggle, cssLayoutOverflowModeToggleInstability.severity, { overflow: overflowValue })) continue;
41076
40948
  }
41077
40949
  }
41078
40950
  });
@@ -41092,7 +40964,7 @@ function hasAnyScrollValue(delta) {
41092
40964
  }
41093
40965
 
41094
40966
  // src/cross-file/rules/css-layout-box-sizing-toggle-with-chrome.ts
41095
- var messages155 = {
40967
+ var messages154 = {
41096
40968
  boxSizingToggleWithChrome: "Conditional `box-sizing` toggle on '{{tag}}' combines with non-zero padding/border, which can shift layout and trigger CLS."
41097
40969
  };
41098
40970
  var BOX_SIZING_VALUES = /* @__PURE__ */ new Set(["content-box", "border-box"]);
@@ -41109,7 +40981,7 @@ var CHROME_PROPERTIES = [
41109
40981
  var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
41110
40982
  id: "css-layout-box-sizing-toggle-with-chrome",
41111
40983
  severity: "warn",
41112
- messages: messages155,
40984
+ messages: messages154,
41113
40985
  meta: {
41114
40986
  description: "Disallow conditional box-sizing mode toggles when box chrome contributes to geometry shifts.",
41115
40987
  fixable: false,
@@ -41127,7 +40999,7 @@ var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
41127
40999
  if (!boxSizingDelta.hasConditional) continue;
41128
41000
  if (!boxSizingDelta.hasDelta) continue;
41129
41001
  if (!hasNonZeroChrome(snapshot)) continue;
41130
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome", messages155.boxSizingToggleWithChrome, cssLayoutBoxSizingToggleWithChrome.severity)) continue;
41002
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome", messages154.boxSizingToggleWithChrome, cssLayoutBoxSizingToggleWithChrome.severity)) continue;
41131
41003
  }
41132
41004
  }
41133
41005
  });
@@ -41136,13 +41008,13 @@ function hasNonZeroChrome(snapshot) {
41136
41008
  }
41137
41009
 
41138
41010
  // src/cross-file/rules/css-layout-content-visibility-no-intrinsic-size.ts
41139
- var messages156 = {
41011
+ var messages155 = {
41140
41012
  missingIntrinsicSize: "`content-visibility: auto` on '{{tag}}' lacks intrinsic size reservation (`contain-intrinsic-size`/min-height/height/aspect-ratio), which can cause CLS."
41141
41013
  };
41142
41014
  var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
41143
41015
  id: "css-layout-content-visibility-no-intrinsic-size",
41144
41016
  severity: "warn",
41145
- messages: messages156,
41017
+ messages: messages155,
41146
41018
  meta: {
41147
41019
  description: "Require intrinsic size reservation when using content-visibility auto to avoid late layout shifts.",
41148
41020
  fixable: false,
@@ -41157,19 +41029,19 @@ var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
41157
41029
  if (!isDeferredContainerLike(node, snapshot.node.textualContent)) continue;
41158
41030
  const reservedSpace = readReservedSpaceFact(context.layout, node);
41159
41031
  if (reservedSpace.hasReservedSpace) continue;
41160
- if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages156.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
41032
+ if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages155.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
41161
41033
  }
41162
41034
  }
41163
41035
  });
41164
41036
 
41165
41037
  // src/cross-file/rules/css-layout-conditional-offset-shift.ts
41166
- var messages157 = {
41038
+ var messages156 = {
41167
41039
  conditionalOffsetShift: "Conditional style applies non-zero '{{property}}' offset ({{value}}), which can cause layout shifts when conditions toggle."
41168
41040
  };
41169
41041
  var cssLayoutConditionalOffsetShift = defineCrossRule({
41170
41042
  id: "css-layout-conditional-offset-shift",
41171
41043
  severity: "warn",
41172
- messages: messages157,
41044
+ messages: messages156,
41173
41045
  meta: {
41174
41046
  description: "Disallow conditional non-zero block-axis offsets that can trigger layout shifts.",
41175
41047
  fixable: false,
@@ -41195,7 +41067,7 @@ var cssLayoutConditionalOffsetShift = defineCrossRule({
41195
41067
  ref.solid.sourceFile,
41196
41068
  cssLayoutConditionalOffsetShift.id,
41197
41069
  "conditionalOffsetShift",
41198
- resolveMessage(messages157.conditionalOffsetShift, {
41070
+ resolveMessage(messages156.conditionalOffsetShift, {
41199
41071
  property: match.property,
41200
41072
  value: `${formatFixed(match.value)}px`
41201
41073
  }),
@@ -41256,7 +41128,7 @@ function hasStableBaseline(baselineBySignal, property, expectedPx) {
41256
41128
 
41257
41129
  // src/cross-file/rules/jsx-layout-unstable-style-toggle.ts
41258
41130
  var import_typescript137 = __toESM(require("typescript"), 1);
41259
- var messages158 = {
41131
+ var messages157 = {
41260
41132
  unstableLayoutStyleToggle: "Dynamic style value for '{{property}}' can toggle layout geometry at runtime and cause CLS."
41261
41133
  };
41262
41134
  var PX_NUMBER_PROPERTIES = /* @__PURE__ */ new Set([
@@ -41279,7 +41151,7 @@ var POSITIONED_OFFSET_PROPERTIES = /* @__PURE__ */ new Set([
41279
41151
  var jsxLayoutUnstableStyleToggle = defineCrossRule({
41280
41152
  id: "jsx-layout-unstable-style-toggle",
41281
41153
  severity: "warn",
41282
- messages: messages158,
41154
+ messages: messages157,
41283
41155
  meta: {
41284
41156
  description: "Flag dynamic inline style values on layout-sensitive properties that can trigger CLS.",
41285
41157
  fixable: false,
@@ -41301,7 +41173,7 @@ var jsxLayoutUnstableStyleToggle = defineCrossRule({
41301
41173
  solid.sourceFile,
41302
41174
  jsxLayoutUnstableStyleToggle.id,
41303
41175
  "unstableLayoutStyleToggle",
41304
- resolveMessage(messages158.unstableLayoutStyleToggle, { property: normalized }),
41176
+ resolveMessage(messages157.unstableLayoutStyleToggle, { property: normalized }),
41305
41177
  "warn"
41306
41178
  )
41307
41179
  );
@@ -41418,14 +41290,14 @@ function unwrapTypeWrapper(node) {
41418
41290
 
41419
41291
  // src/cross-file/rules/jsx-layout-classlist-geometry-toggle.ts
41420
41292
  var import_typescript138 = __toESM(require("typescript"), 1);
41421
- var messages159 = {
41293
+ var messages158 = {
41422
41294
  classListGeometryToggle: "classList toggles '{{className}}', and matching CSS changes layout-affecting '{{property}}', which can cause CLS."
41423
41295
  };
41424
41296
  var OUT_OF_FLOW_POSITIONS2 = /* @__PURE__ */ new Set(["fixed", "absolute"]);
41425
41297
  var jsxLayoutClasslistGeometryToggle = defineCrossRule({
41426
41298
  id: "jsx-layout-classlist-geometry-toggle",
41427
41299
  severity: "warn",
41428
- messages: messages159,
41300
+ messages: messages158,
41429
41301
  meta: {
41430
41302
  description: "Flag classList-driven class toggles that map to layout-affecting CSS geometry changes.",
41431
41303
  fixable: false,
@@ -41451,7 +41323,7 @@ var jsxLayoutClasslistGeometryToggle = defineCrossRule({
41451
41323
  solid.sourceFile,
41452
41324
  jsxLayoutClasslistGeometryToggle.id,
41453
41325
  "classListGeometryToggle",
41454
- resolveMessage(messages159.classListGeometryToggle, {
41326
+ resolveMessage(messages158.classListGeometryToggle, {
41455
41327
  className,
41456
41328
  property
41457
41329
  }),
@@ -41500,14 +41372,14 @@ function isDynamicallyToggleable(node) {
41500
41372
  }
41501
41373
 
41502
41374
  // src/cross-file/rules/jsx-layout-picture-source-ratio-consistency.ts
41503
- var messages160 = {
41375
+ var messages159 = {
41504
41376
  inconsistentPictureRatio: "`<picture>` source ratio {{sourceRatio}} differs from fallback img ratio {{imgRatio}}, which can cause reserved-space mismatch and CLS."
41505
41377
  };
41506
41378
  var RATIO_DELTA_THRESHOLD = 0.02;
41507
41379
  var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
41508
41380
  id: "jsx-layout-picture-source-ratio-consistency",
41509
41381
  severity: "warn",
41510
- messages: messages160,
41382
+ messages: messages159,
41511
41383
  meta: {
41512
41384
  description: "Require consistent intrinsic aspect ratios across <picture> sources and fallback image.",
41513
41385
  fixable: false,
@@ -41530,7 +41402,7 @@ var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
41530
41402
  solid.sourceFile,
41531
41403
  jsxLayoutPictureSourceRatioConsistency.id,
41532
41404
  "inconsistentPictureRatio",
41533
- resolveMessage(messages160.inconsistentPictureRatio, {
41405
+ resolveMessage(messages159.inconsistentPictureRatio, {
41534
41406
  sourceRatio: mismatch.sourceRatio,
41535
41407
  imgRatio: mismatch.imgRatio
41536
41408
  }),
@@ -41600,13 +41472,13 @@ function formatRatio(value2) {
41600
41472
  }
41601
41473
 
41602
41474
  // src/cross-file/rules/jsx-layout-fill-image-parent-must-be-sized.ts
41603
- var messages161 = {
41475
+ var messages160 = {
41604
41476
  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."
41605
41477
  };
41606
41478
  var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
41607
41479
  id: "jsx-layout-fill-image-parent-must-be-sized",
41608
41480
  severity: "warn",
41609
- messages: messages161,
41481
+ messages: messages160,
41610
41482
  meta: {
41611
41483
  description: "Require stable parent size and positioning for fill-image component usage.",
41612
41484
  fixable: false,
@@ -41636,7 +41508,7 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
41636
41508
  solid.sourceFile,
41637
41509
  jsxLayoutFillImageParentMustBeSized.id,
41638
41510
  "unsizedFillParent",
41639
- resolveMessage(messages161.unsizedFillParent, {
41511
+ resolveMessage(messages160.unsizedFillParent, {
41640
41512
  component: element.tag ?? "Image"
41641
41513
  }),
41642
41514
  "warn"
@@ -41647,6 +41519,210 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
41647
41519
  }
41648
41520
  });
41649
41521
 
41522
+ // src/cross-file/rules/jsx-layout-policy-touch-target.ts
41523
+ var messages161 = {
41524
+ heightTooSmall: "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
41525
+ widthTooSmall: "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
41526
+ paddingTooSmall: "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
41527
+ noReservedBlockSize: "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.",
41528
+ noReservedInlineSize: "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold."
41529
+ };
41530
+ var INTERACTIVE_HTML_TAGS2 = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea", "label", "summary"]);
41531
+ var INTERACTIVE_ARIA_ROLES2 = /* @__PURE__ */ new Set([
41532
+ "button",
41533
+ "link",
41534
+ "checkbox",
41535
+ "radio",
41536
+ "combobox",
41537
+ "listbox",
41538
+ "menuitem",
41539
+ "menuitemcheckbox",
41540
+ "menuitemradio",
41541
+ "option",
41542
+ "switch",
41543
+ "tab"
41544
+ ]);
41545
+ function classifyInteractive(node, solid, element, layout) {
41546
+ const tag = node.tagName;
41547
+ if (tag !== null && INTERACTIVE_HTML_TAGS2.has(tag)) {
41548
+ if (tag === "input" || tag === "select" || tag === "textarea") return "input";
41549
+ return "button";
41550
+ }
41551
+ const roleAttr = getJSXAttributeEntity(solid, element, "role");
41552
+ if (roleAttr !== null && roleAttr.valueNode !== null) {
41553
+ const role = getStaticStringFromJSXValue(roleAttr.valueNode);
41554
+ if (role !== null && INTERACTIVE_ARIA_ROLES2.has(role)) return "button";
41555
+ }
41556
+ const hostRef = layout.hostElementRefsByNode.get(node) ?? null;
41557
+ if (hostRef !== null && hostRef.element.tagName !== null) {
41558
+ const hostTag = hostRef.element.tagName;
41559
+ if (INTERACTIVE_HTML_TAGS2.has(hostTag)) {
41560
+ if (hostTag === "input" || hostTag === "select" || hostTag === "textarea") return "input";
41561
+ return "button";
41562
+ }
41563
+ }
41564
+ return null;
41565
+ }
41566
+ function isVisuallyHidden(snapshot) {
41567
+ const position = readKnownNormalized(snapshot, "position");
41568
+ if (position !== "absolute" && position !== "fixed") return false;
41569
+ const node = snapshot.node;
41570
+ const opacityAttr = node.inlineStyleValues.get("opacity");
41571
+ if (opacityAttr === "0") return true;
41572
+ if (node.classTokenSet.has("opacity-0")) return true;
41573
+ return false;
41574
+ }
41575
+ var jsxLayoutPolicyTouchTarget = defineCrossRule({
41576
+ id: "jsx-layout-policy-touch-target",
41577
+ severity: "warn",
41578
+ messages: messages161,
41579
+ meta: {
41580
+ description: "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.",
41581
+ fixable: false,
41582
+ category: "css-a11y"
41583
+ },
41584
+ check(context, emit) {
41585
+ const policy = getActivePolicy();
41586
+ if (policy === null) return;
41587
+ const policyName = getActivePolicyName() ?? "";
41588
+ const { layout } = context;
41589
+ const elements = layout.elements;
41590
+ for (let i = 0; i < elements.length; i++) {
41591
+ const node = elements[i];
41592
+ if (!node) continue;
41593
+ const ref = readElementRef(layout, node);
41594
+ if (!ref) continue;
41595
+ const kind = classifyInteractive(node, ref.solid, ref.element, layout);
41596
+ if (kind === null) continue;
41597
+ const snapshot = collectSignalSnapshot(context, node);
41598
+ if (isVisuallyHidden(snapshot)) continue;
41599
+ const tag = node.tagName ?? node.tag ?? "element";
41600
+ checkDimension(
41601
+ snapshot,
41602
+ "height",
41603
+ kind === "button" ? policy.minButtonHeight : policy.minInputHeight,
41604
+ layout,
41605
+ node,
41606
+ emit,
41607
+ "heightTooSmall",
41608
+ messages161.heightTooSmall,
41609
+ tag,
41610
+ policyName
41611
+ );
41612
+ checkDimension(
41613
+ snapshot,
41614
+ "min-height",
41615
+ kind === "button" ? policy.minButtonHeight : policy.minInputHeight,
41616
+ layout,
41617
+ node,
41618
+ emit,
41619
+ "heightTooSmall",
41620
+ messages161.heightTooSmall,
41621
+ tag,
41622
+ policyName
41623
+ );
41624
+ checkDimension(
41625
+ snapshot,
41626
+ "width",
41627
+ kind === "button" ? policy.minButtonWidth : policy.minTouchTarget,
41628
+ layout,
41629
+ node,
41630
+ emit,
41631
+ "widthTooSmall",
41632
+ messages161.widthTooSmall,
41633
+ tag,
41634
+ policyName
41635
+ );
41636
+ checkDimension(
41637
+ snapshot,
41638
+ "min-width",
41639
+ kind === "button" ? policy.minButtonWidth : policy.minTouchTarget,
41640
+ layout,
41641
+ node,
41642
+ emit,
41643
+ "widthTooSmall",
41644
+ messages161.widthTooSmall,
41645
+ tag,
41646
+ policyName
41647
+ );
41648
+ if (kind === "button") {
41649
+ checkDimension(
41650
+ snapshot,
41651
+ "padding-left",
41652
+ policy.minButtonHorizontalPadding,
41653
+ layout,
41654
+ node,
41655
+ emit,
41656
+ "paddingTooSmall",
41657
+ messages161.paddingTooSmall,
41658
+ tag,
41659
+ policyName
41660
+ );
41661
+ checkDimension(
41662
+ snapshot,
41663
+ "padding-right",
41664
+ policy.minButtonHorizontalPadding,
41665
+ layout,
41666
+ node,
41667
+ emit,
41668
+ "paddingTooSmall",
41669
+ messages161.paddingTooSmall,
41670
+ tag,
41671
+ policyName
41672
+ );
41673
+ }
41674
+ const reservedSpace = readReservedSpaceFact(layout, node);
41675
+ const minBlock = kind === "button" ? policy.minButtonHeight : policy.minInputHeight;
41676
+ const minInline = kind === "button" ? policy.minButtonWidth : policy.minTouchTarget;
41677
+ if (!reservedSpace.hasUsableBlockDimension) {
41678
+ emitLayoutDiagnostic(
41679
+ layout,
41680
+ node,
41681
+ emit,
41682
+ jsxLayoutPolicyTouchTarget.id,
41683
+ "noReservedBlockSize",
41684
+ messages161.noReservedBlockSize,
41685
+ "warn",
41686
+ { tag, min: String(minBlock), policy: policyName }
41687
+ );
41688
+ }
41689
+ if (!reservedSpace.hasUsableInlineDimension) {
41690
+ emitLayoutDiagnostic(
41691
+ layout,
41692
+ node,
41693
+ emit,
41694
+ jsxLayoutPolicyTouchTarget.id,
41695
+ "noReservedInlineSize",
41696
+ messages161.noReservedInlineSize,
41697
+ "warn",
41698
+ { tag, min: String(minInline), policy: policyName }
41699
+ );
41700
+ }
41701
+ }
41702
+ }
41703
+ });
41704
+ function checkDimension(snapshot, signal, min, layout, node, emit, messageId, template, tag, policyName) {
41705
+ const px = readKnownPx(snapshot, signal);
41706
+ if (px === null) return;
41707
+ if (px >= min) return;
41708
+ emitLayoutDiagnostic(
41709
+ layout,
41710
+ node,
41711
+ emit,
41712
+ jsxLayoutPolicyTouchTarget.id,
41713
+ messageId,
41714
+ template,
41715
+ "warn",
41716
+ {
41717
+ signal,
41718
+ value: formatRounded(px),
41719
+ min: String(min),
41720
+ tag,
41721
+ policy: policyName
41722
+ }
41723
+ );
41724
+ }
41725
+
41650
41726
  // src/cross-file/rules/index.ts
41651
41727
  var rules3 = [
41652
41728
  jsxNoUndefinedCssClass,
@@ -41678,7 +41754,8 @@ var rules3 = [
41678
41754
  cssLayoutOverflowModeToggleInstability,
41679
41755
  cssLayoutBoxSizingToggleWithChrome,
41680
41756
  cssLayoutContentVisibilityNoIntrinsicSize,
41681
- cssLayoutConditionalOffsetShift
41757
+ cssLayoutConditionalOffsetShift,
41758
+ jsxLayoutPolicyTouchTarget
41682
41759
  ];
41683
41760
 
41684
41761
  // src/cross-file/plugin.ts
@@ -41980,19 +42057,6 @@ var RULES = [
41980
42057
  "paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`."
41981
42058
  }
41982
42059
  },
41983
- {
41984
- "id": "css-policy-touch-target",
41985
- "severity": "warn",
41986
- "description": "Enforce minimum interactive element sizes per accessibility policy.",
41987
- "fixable": false,
41988
- "category": "css-a11y",
41989
- "plugin": "css",
41990
- "messages": {
41991
- "heightTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
41992
- "widthTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
41993
- "paddingTooSmall": "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
41994
- }
41995
- },
41996
42060
  {
41997
42061
  "id": "css-policy-typography",
41998
42062
  "severity": "warn",
@@ -42016,6 +42080,21 @@ var RULES = [
42016
42080
  "missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
42017
42081
  }
42018
42082
  },
42083
+ {
42084
+ "id": "jsx-layout-policy-touch-target",
42085
+ "severity": "warn",
42086
+ "description": "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.",
42087
+ "fixable": false,
42088
+ "category": "css-a11y",
42089
+ "plugin": "cross-file",
42090
+ "messages": {
42091
+ "heightTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
42092
+ "widthTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
42093
+ "paddingTooSmall": "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
42094
+ "noReservedBlockSize": "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.",
42095
+ "noReservedInlineSize": "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold."
42096
+ }
42097
+ },
42019
42098
  {
42020
42099
  "id": "css-no-discrete-transition",
42021
42100
  "severity": "error",
@@ -43674,7 +43753,7 @@ var RULES = [
43674
43753
  ];
43675
43754
  var RULES_BY_CATEGORY = {
43676
43755
  "correctness": [{ "id": "avoid-conditional-spreads", "severity": "error", "description": "Disallow conditional spread operators that create empty objects. Patterns like `...(condition ? {...} : {})` are fragile and create unnecessary object creations.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "avoidConditionalSpread": "Avoid conditional spread with empty object fallback. Instead of `...(cond ? {...} : {})`, build the object first with conditional property assignment, then spread once.", "avoidLogicalAndSpread": "Avoid logical AND spread pattern. Instead of `...(cond && {...})`, use explicit conditional property assignment for clarity." } }, { "id": "avoid-non-null-assertions", "severity": "error", "description": "Disallow non-null assertion operator (`!`). Use optional chaining, nullish coalescing, or proper type narrowing instead.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidNonNull": 'Avoid non-null assertion on "{{name}}". Non-null assertions bypass type safety. Use optional chaining (`?.`), nullish coalescing (`??`), or proper type narrowing instead.' } }, { "id": "avoid-object-assign", "severity": "error", "description": "Disallow Object.assign(). Prefer object spread syntax or structuredClone() for copying objects.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidMerge": "Avoid Object.assign() for merging. Use object spread syntax { ...obj } instead.", "avoidMutation": "Avoid Object.assign() for mutation. Consider immutable patterns like { ...existing, ...props }." } }, { "id": "avoid-object-spread", "severity": "error", "description": "Disallow object spread operators that break Solid's fine-grained reactivity.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidObjectCopy": "Avoid object spread for copying. Use direct property access.", "avoidObjectMerge": "Avoid object spread for merging. Use mergeProps() from 'solid-js'.", "avoidObjectUpdate": "Avoid object spread for updates. Use produce() or direct assignment.", "avoidJsxSpread": "Avoid JSX prop spreading. Use splitProps() to separate props.", "avoidRestDestructure": "Avoid rest destructuring. Use splitProps() from 'solid-js'.", "avoidPropsSpread": "Spreading props breaks reactivity. Use splitProps() to separate known props.", "avoidStoreSpread": "Spreading store creates a static snapshot. Access properties directly.", "avoidSignalSpread": "Spreading signal result captures current value. Wrap in createMemo().", "avoidClassListSpread": "Spreading in classList breaks reactivity. Wrap in createMemo().", "avoidStyleSpread": "Spreading in style breaks reactivity. Wrap in createMemo().", "unnecessarySplitProps": "Unnecessary splitProps with empty array. Remove it and use {{source}} directly." } }, { "id": "avoid-type-casting", "severity": "error", "description": "Disallow type casting methods that bypass TypeScript's type safety. Includes unnecessary casts, double assertions, casting to any, type predicates, and unsafe generic assertions.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "unnecessaryCast": `Unnecessary type assertion: "{{name}}" is already of type "{{exprType}}", which is assignable to "{{type}}". Remove the cast - it adds noise and suggests you don't understand the types.`, "doubleAssertion": 'Double assertion detected: "{{name}}" is cast through unknown/any to "{{type}}". This bypasses type safety. You are creating sloppy architecture.', "castToAny": 'Casting "{{name}}" to `any` disables all type checking. Use `unknown` with proper type guards, or fix the underlying type issue.', "castToUnknown": "Casting to `unknown` requires runtime type checks before use. You are creating sloppy architecture.", "simpleAssertion": 'Type assertion on "{{name}}" to "{{type}}" bypasses type checking. Why are you doing this? Do you EVEN need this? This is sloppy architecture.', "assertionInLoop": 'Type assertion on "{{name}}" inside a loop. Repeated casts to "{{type}}" without validation can mask type errors. Consider validating the type once before the loop.', "importAssertion": 'Type assertion on dynamic import to "{{type}}". Import types should be validated at runtime or use proper module type declarations.', "typePredicate": 'Type predicate function asserts "{{param}}" is "{{type}}". Why are you doing this? Do you EVEN need this? This is sloppy architecture.', "unsafeGeneric": 'Casting to generic type parameter "{{typeParam}}" without runtime validation. The function returns an unverified type. This is sloppy architecture.' } }, { "id": "avoid-unsafe-type-annotations", "severity": "error", "description": "Disallow `any` and `unknown` in value-level type annotation positions (parameters, returns, variables, properties)", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "anyParameter": "Parameter '{{name}}' is typed `any`{{inFunction}}. This disables type checking for all callers. Use a specific type, a generic, or `unknown` with proper type narrowing.", "anyReturn": "Function '{{name}}' returns `any`. This disables type checking for all callers. Use a specific return type.", "anyVariable": "Variable '{{name}}' is typed `any`. This disables all type checking on this variable. Use a specific type or `unknown` with type narrowing.", "anyProperty": "Property '{{name}}' is typed `any`. This disables type checking for all accesses. Use a specific type.", "unknownParameter": "Parameter '{{name}}' is typed `unknown`{{inFunction}}. Callers can pass anything and the function body requires type narrowing on every use. Use a specific type or a generic constraint.", "unknownReturn": "Function '{{name}}' returns `unknown`. Callers must narrow the return value before use. Use a specific return type or a generic.", "unknownVariable": "Variable '{{name}}' is typed `unknown`. Every use requires type narrowing. Use a specific type or parse the value at the boundary.", "unknownProperty": "Property '{{name}}' is typed `unknown`. Every access requires type narrowing. Use a specific type." } }, { "id": "event-handlers", "severity": "error", "description": "Enforce naming DOM element event handlers consistently and prevent Solid's analysis from misunderstanding whether a prop should be an event handler.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "detectedAttr": 'The "{{name}}" prop looks like an event handler but has a static value ({{staticValue}}), so Solid will treat it as an attribute instead of attaching an event listener. Use attr:{{name}} to make this explicit, or provide a function value.', "naming": 'The "{{name}}" prop is ambiguous. Solid cannot determine if this is an event handler or an attribute. Use {{handlerName}} for an event handler, or {{attrName}} for an attribute.', "capitalization": 'The "{{name}}" prop should be {{fixedName}} for Solid to recognize it as an event handler. Event handlers use camelCase with an uppercase letter after "on".', "nonstandard": 'The "{{name}}" prop uses a nonstandard event name. Use {{fixedName}} instead, which is the standard DOM event name that Solid recognizes.', "makeHandler": "Change {{name}} to {{handlerName}} (event handler).", "makeAttr": "Change {{name}} to {{attrName}} (attribute).", "spreadHandler": 'The "{{name}}" prop is being spread into JSX, which prevents Solid from attaching it as an event listener. Add it directly as a JSX attribute instead: {{name}}={...}.' } }, { "id": "missing-jsdoc-comments", "severity": "error", "description": "Require JSDoc comments on functions with appropriate tags for parameters, return values, and throws.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "missingJsdoc": "Function '{{name}}' is missing a JSDoc comment.", "missingParam": "JSDoc for '{{name}}' is missing @param tag for '{{param}}'.", "missingReturn": "JSDoc for '{{name}}' is missing @returns tag.", "missingThrows": "JSDoc for '{{name}}' is missing @throws tag.", "missingExample": "JSDoc for '{{name}}' is missing @example tag.", "missingClassJsdoc": "Class '{{name}}' is missing a JSDoc comment.", "missingPropertyJsdoc": "Property '{{name}}' is missing a JSDoc comment." } }, { "id": "no-ai-slop-comments", "severity": "error", "description": "Disallow comments containing specified forbidden words or phrases. Useful for enforcing comment style guidelines and detecting AI-generated boilerplate.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "forbiddenWord": "Comment contains forbidden word '{{word}}'." } }, { "id": "no-array-handlers", "severity": "error", "description": "Disallow array handlers in JSX event properties.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "noArrayHandlers": 'Passing an array to "{{handlerName}}" is type-unsafe. The array syntax `[handler, data]` passes data as the first argument, making the event object the second argument. Use a closure instead: `{{handlerName}}={() => handler(data)}`.' } }, { "id": "no-banner-comments", "severity": "error", "description": "Disallow banner-style comments with repeated separator characters.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "banner": "Avoid banner-style comments with repeated separator characters. Use simple comments instead." } }, { "id": "no-destructure", "severity": "error", "description": "Disallow destructuring props in Solid components. Props must be accessed via property access (props.x) to preserve reactivity.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "noDestructure": "Destructuring component props breaks Solid's reactivity. Props are reactive getters, so `{ a }` captures the value at component creation time and won't update. Use `props.a` to access props reactively.", "noDestructureWithDefaults": "Destructuring component props breaks Solid's reactivity. For default values, use `mergeProps({ a: defaultValue }, props)` instead of `{ a = defaultValue }`.", "noDestructureWithRest": "Destructuring component props breaks Solid's reactivity. For rest patterns, use `splitProps(props, ['a', 'b'])` instead of `{ a, b, ...rest }`.", "noDestructureWithBoth": "Destructuring component props breaks Solid's reactivity. For default values with rest, use `splitProps(mergeProps({ a: defaultValue }, props), ['a'])` to combine both patterns." } }, { "id": "no-inline-imports", "severity": "error", "description": "Disallow inline type imports. Import types at the top of the file for clarity and maintainability.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "inlineImport": "Avoid inline imports. Import `{{specifier}}` at the top of the file instead." } }, { "id": "string-concat-in-loop", "severity": "error", "description": "Disallow string concatenation with += inside loops. Use array.push() and .join() instead.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "stringConcatInLoop": "Avoid string concatenation with += inside loops. Use an array with .push() and .join() instead." } }],
43677
- "css-a11y": [{ "id": "css-no-outline-none-without-focus-visible", "severity": "error", "description": "Disallow removing outline without explicit focus-visible replacement.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingFocusVisible": "Focus outline removed without matching `:focus-visible` replacement." } }, { "id": "css-policy-contrast", "severity": "warn", "description": "Enforce minimum contrast ratio between foreground and background colors per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "insufficientContrast": "Contrast ratio `{{ratio}}:1` between `{{fg}}` and `{{bg}}` is below the minimum `{{min}}:1` for `{{textSize}}` text in policy `{{policy}}`." } }, { "id": "css-policy-spacing", "severity": "warn", "description": "Enforce minimum letter-spacing, word-spacing, and paragraph spacing per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "letterSpacingTooSmall": "Letter spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Word spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`." } }, { "id": "css-policy-touch-target", "severity": "warn", "description": "Enforce minimum interactive element sizes per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "heightTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.", "widthTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.", "paddingTooSmall": "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`." } }, { "id": "css-policy-typography", "severity": "warn", "description": "Enforce minimum font sizes and line heights per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "fontTooSmall": "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.", "lineHeightTooSmall": "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`." } }, { "id": "css-require-reduced-motion-override", "severity": "warn", "description": "Require reduced-motion override for animated selectors.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override." } }],
43756
+ "css-a11y": [{ "id": "css-no-outline-none-without-focus-visible", "severity": "error", "description": "Disallow removing outline without explicit focus-visible replacement.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingFocusVisible": "Focus outline removed without matching `:focus-visible` replacement." } }, { "id": "css-policy-contrast", "severity": "warn", "description": "Enforce minimum contrast ratio between foreground and background colors per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "insufficientContrast": "Contrast ratio `{{ratio}}:1` between `{{fg}}` and `{{bg}}` is below the minimum `{{min}}:1` for `{{textSize}}` text in policy `{{policy}}`." } }, { "id": "css-policy-spacing", "severity": "warn", "description": "Enforce minimum letter-spacing, word-spacing, and paragraph spacing per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "letterSpacingTooSmall": "Letter spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Word spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`." } }, { "id": "css-policy-typography", "severity": "warn", "description": "Enforce minimum font sizes and line heights per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "fontTooSmall": "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.", "lineHeightTooSmall": "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`." } }, { "id": "css-require-reduced-motion-override", "severity": "warn", "description": "Require reduced-motion override for animated selectors.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override." } }, { "id": "jsx-layout-policy-touch-target", "severity": "warn", "description": "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.", "fixable": false, "category": "css-a11y", "plugin": "cross-file", "messages": { "heightTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "widthTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "paddingTooSmall": "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "noReservedBlockSize": "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.", "noReservedInlineSize": "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold." } }],
43678
43757
  "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." } }],
43679
43758
  "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." } }],
43680
43759
  "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}}`." } }],