@fabriziosalmi/slopless 1.4.1

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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist/engine/api.d.ts +16 -0
  4. package/dist/engine/api.js +543 -0
  5. package/dist/index.d.ts +122 -0
  6. package/dist/index.js +590 -0
  7. package/package.json +59 -0
  8. package/rules/VBC-001.yaml +33 -0
  9. package/rules/VBC-002.yaml +13 -0
  10. package/rules/VBC-003.yaml +36 -0
  11. package/rules/VBC-004.yaml +33 -0
  12. package/rules/VBC-005.yaml +24 -0
  13. package/rules/VBC-006.yaml +16 -0
  14. package/rules/VBC-007.yaml +28 -0
  15. package/rules/VBC-008.yaml +21 -0
  16. package/rules/VBC-009.yaml +24 -0
  17. package/rules/VBC-010.yaml +24 -0
  18. package/rules/VBC-011.yaml +21 -0
  19. package/rules/VBC-012.yaml +21 -0
  20. package/rules/VBC-013.yaml +17 -0
  21. package/rules/VBC-014.yaml +15 -0
  22. package/rules/VBC-015.yaml +40 -0
  23. package/rules/VBC-016.yaml +21 -0
  24. package/rules/VBC-017.yaml +23 -0
  25. package/rules/VBC-018.yaml +19 -0
  26. package/rules/VBC-019.yaml +42 -0
  27. package/rules/VBC-020.yaml +22 -0
  28. package/rules/VBC-021.yaml +22 -0
  29. package/rules/VBC-022.yaml +21 -0
  30. package/rules/VBC-023.yaml +22 -0
  31. package/rules/VBC-024.yaml +26 -0
  32. package/rules/VBC-025.yaml +13 -0
  33. package/rules/VBC-026.yaml +17 -0
  34. package/rules/VBC-027.yaml +27 -0
  35. package/rules/VBC-028.yaml +18 -0
  36. package/rules/VBC-029.yaml +22 -0
  37. package/rules/VBC-030.yaml +22 -0
  38. package/rules/VBC-032.yaml +20 -0
  39. package/rules/VBC-033.yaml +26 -0
  40. package/rules/VBC-034.yaml +41 -0
  41. package/rules/VBC-035.yaml +30 -0
  42. package/rules/VBC-039.yaml +18 -0
  43. package/rules/VBC-042.yaml +26 -0
  44. package/rules/VBC-045.yaml +13 -0
  45. package/rules/VBC-046.yaml +13 -0
  46. package/rules/VBC-048.yaml +23 -0
  47. package/rules/VBC-049.yaml +19 -0
  48. package/rules/VBC-051.yaml +23 -0
  49. package/rules/VBC-052.yaml +19 -0
  50. package/rules/VBC-053.yaml +19 -0
  51. package/rules/VBC-054.yaml +29 -0
  52. package/rules/VBC-056.yaml +28 -0
  53. package/rules/VBC-057.yaml +18 -0
  54. package/rules/VBC-058.yaml +18 -0
  55. package/rules/VBC-059.yaml +21 -0
  56. package/rules/VBC-063-B.yaml +15 -0
  57. package/rules/VBC-063.yaml +18 -0
  58. package/rules/VBC-065.yaml +33 -0
  59. package/rules/VBC-066.yaml +16 -0
  60. package/rules/VBC-068.yaml +21 -0
  61. package/rules/VBC-070.yaml +19 -0
  62. package/rules/VBC-077.yaml +35 -0
  63. package/rules/VBC-079.yaml +18 -0
  64. package/rules/VBC-080.yaml +18 -0
  65. package/rules/VBC-084.yaml +22 -0
  66. package/rules/VBC-086.yaml +22 -0
  67. package/rules/VBC-090.yaml +28 -0
  68. package/rules/VBC-096.yaml +19 -0
  69. package/rules/VBC-097.yaml +21 -0
  70. package/rules/VBC-098.yaml +18 -0
  71. package/rules/VBC-101.yaml +23 -0
  72. package/rules/VBC-102.yaml +20 -0
  73. package/rules/VBC-103.yaml +35 -0
  74. package/rules/VBC-104.yaml +24 -0
  75. package/rules/VBC-106.yaml +14 -0
  76. package/rules/VBC-112.yaml +20 -0
  77. package/rules/VBC-113.yaml +19 -0
  78. package/rules/VBC-117.yaml +21 -0
  79. package/rules/VBC-123.yaml +29 -0
  80. package/rules/VBC-131.yaml +20 -0
  81. package/rules/VBC-150.yaml +27 -0
  82. package/rules/VBC-153.yaml +21 -0
  83. package/rules/VBC-158.yaml +16 -0
  84. package/rules/VBC-161.yaml +27 -0
  85. package/rules/VBC-172.yaml +25 -0
  86. package/rules/VBC-197.yaml +22 -0
  87. package/rules/VBC-201.yaml +35 -0
  88. package/rules/VBC-206.yaml +19 -0
  89. package/rules/VBC-208.yaml +27 -0
  90. package/rules/VBC-212.yaml +19 -0
  91. package/rules/VBC-215.yaml +24 -0
  92. package/rules/VBC-305.yaml +20 -0
  93. package/rules/VBC-324.yaml +24 -0
  94. package/rules/VBC-334.yaml +23 -0
  95. package/rules/VBC-338.yaml +32 -0
  96. package/rules/VBC-340.yaml +22 -0
  97. package/rules/VBC-344.yaml +19 -0
  98. package/rules/VBC-347.yaml +21 -0
  99. package/rules/VBC-348.yaml +19 -0
  100. package/rules/VBC-385.yaml +21 -0
  101. package/rules/VBC-398.yaml +22 -0
  102. package/rules/VBC-401.yaml +12 -0
  103. package/rules/VBC-414.yaml +23 -0
  104. package/rules/VBC-421.yaml +20 -0
  105. package/rules/VBC-501.yaml +18 -0
  106. package/rules/VBC-502.yaml +23 -0
  107. package/rules/VBC-503.yaml +18 -0
  108. package/rules/VBC-504.yaml +20 -0
  109. package/rules/VBC-800.yaml +17 -0
  110. package/rules/VBC-901.yaml +48 -0
  111. package/rules/VBC-902.yaml +19 -0
  112. package/rules/VBC-903.yaml +17 -0
  113. package/rules/VBC-904.yaml +21 -0
  114. package/rules/VBC-905.yaml +17 -0
  115. package/rules/VBC-906.yaml +29 -0
  116. package/rules/VBC-907.yaml +24 -0
  117. package/rules/VBC-908.yaml +22 -0
  118. package/rules/VBC-909.yaml +21 -0
  119. package/rules/VBC-910.yaml +18 -0
  120. package/rules/VBC-911.yaml +21 -0
  121. package/rules/VBC-913.yaml +30 -0
  122. package/rules/VBC-914.yaml +31 -0
  123. package/rules/VBC-915.yaml +19 -0
  124. package/rules/VBC-916.yaml +24 -0
  125. package/rules/VBC-917.yaml +21 -0
  126. package/rules/VBC-918.yaml +22 -0
  127. package/rules/VBC-920.yaml +20 -0
  128. package/rules/VBC-921.yaml +45 -0
  129. package/rules/VBC-922.yaml +15 -0
  130. package/rules/VBC-923.yaml +15 -0
  131. package/rules/VBC-924.yaml +12 -0
  132. package/rules/VBC-925.yaml +14 -0
  133. package/rules/VBC-926.yaml +14 -0
  134. package/rules/VBC-927.yaml +24 -0
  135. package/rules/VBC-928.yaml +25 -0
  136. package/rules/VBC-929.yaml +21 -0
  137. package/rules/VBC-930.yaml +16 -0
  138. package/rules/VBC-931.yaml +16 -0
  139. package/rules/VBC-932.yaml +16 -0
  140. package/rules/VBC-933.yaml +28 -0
  141. package/rules/VBC-934.yaml +24 -0
  142. package/rules/VBC-935.yaml +26 -0
  143. package/rules/VBC-936.yaml +24 -0
  144. package/rules/VBC-937.yaml +24 -0
  145. package/rules/VBC-938.yaml +23 -0
  146. package/rules/VBC-939.yaml +23 -0
  147. package/rules/VBC-940.yaml +30 -0
  148. package/rules/VBC-941.yaml +28 -0
  149. package/rules/VBC-942.yaml +23 -0
  150. package/rules/VBC-943.yaml +22 -0
  151. package/rules/VBC-944.yaml +26 -0
  152. package/rules/VBC-945.yaml +33 -0
  153. package/rules/VBC-946.yaml +26 -0
  154. package/rules/VBC-947.yaml +22 -0
  155. package/rules/VBC-948.yaml +26 -0
@@ -0,0 +1,35 @@
1
+ id: VBC-077
2
+ name: deceptive-function-name
3
+ severity: error
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ - semantic
8
+ match:
9
+ ast_check:
10
+ type: lying-function-names
11
+ message: >-
12
+ Lying function name detected: '{name}' implies a non-mutating action but contains side-effect
13
+ keywords (set, delete, update). This is major vibecoding.
14
+ tests:
15
+ fire:
16
+ - |
17
+ function getUser(id) {
18
+ this.cache.set(id, 1);
19
+ return id;
20
+ }
21
+ quiet:
22
+ - |
23
+ function getUser(id) {
24
+ return this.users[id];
25
+ }
26
+ - |
27
+ function getOffset(node) {
28
+ return node.dataset.offset;
29
+ }
30
+ - |
31
+ function getAll() {
32
+ const out = [];
33
+ for (const x of this.buffer) out.push(x);
34
+ return out;
35
+ }
@@ -0,0 +1,18 @@
1
+ id: VBC-079
2
+ name: debugger-statements
3
+ severity: error
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ match:
8
+ regex: debugger;
9
+ file_types:
10
+ - js
11
+ - ts
12
+ message: >-
13
+ debugger; statement found at line {line}. This should never reach production.
14
+ tests:
15
+ fire:
16
+ - debugger;
17
+ quiet:
18
+ - inspect(state);
@@ -0,0 +1,18 @@
1
+ id: VBC-080
2
+ name: browser-alert
3
+ severity: error
4
+ category: clean-code
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: \balert\(
9
+ file_types:
10
+ - js
11
+ - ts
12
+ message: >-
13
+ Avoid using alert() at line {line}. Use a modern UI notification system.
14
+ tests:
15
+ fire:
16
+ - alert("saved");
17
+ quiet:
18
+ - showToast("saved");
@@ -0,0 +1,22 @@
1
+ id: VBC-084
2
+ name: long-line-limit
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ match:
8
+ regex: ^.{120,}$
9
+ scan: all
10
+ file_types:
11
+ - js
12
+ - ts
13
+ - py
14
+ - go
15
+ message: >-
16
+ Line is too long ({count} characters). Consider wrapping it. Max recommended: 120.
17
+ tests:
18
+ fire:
19
+ - >-
20
+ const message = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
21
+ quiet:
22
+ - const message = "short";
@@ -0,0 +1,22 @@
1
+ id: VBC-086
2
+ name: target-blank-rel
3
+ severity: error
4
+ category: core
5
+ tags:
6
+ - security
7
+ match:
8
+ regex: >-
9
+ <a\b(?![^>]*\brel\s*=\s*["'{][^"'}]*(?:noopener|noreferrer))[^>]*\btarget\s*=\s*["'{]{0,2}_blank
10
+ multiline: true
11
+ file_types:
12
+ - html
13
+ - js
14
+ - ts
15
+ - tsx
16
+ message: target='_blank' without rel='noopener' detected at line {line}.
17
+ tests:
18
+ fire:
19
+ - <a href="/x" target="_blank">docs</a>
20
+ quiet:
21
+ - <a href="/x" target="_blank" rel="noopener">docs</a>
22
+ - <a href="/x" rel="noreferrer" target="_blank">docs</a>
@@ -0,0 +1,28 @@
1
+ id: VBC-090
2
+ name: passive-aggressive-comments
3
+ severity: warning
4
+ category: core
5
+ tags:
6
+ - team-culture
7
+ match:
8
+ regex: >-
9
+ (blame \w+|fix later|hope it works|idk why this works|this is cursed|wtf is this|no idea
10
+ why|pray it works|don't touch this|shouldn't work but|works on my machine)
11
+ flags: i
12
+ scan: comments
13
+ file_types:
14
+ - js
15
+ - ts
16
+ - py
17
+ - sh
18
+ - c
19
+ - cpp
20
+ - md
21
+ message: >-
22
+ Passive-aggressive or helpless comment '{match}' detected at line {line}. Replace with a
23
+ technical explanation of the actual constraint or a link to the relevant issue tracker ticket.
24
+ tests:
25
+ fire:
26
+ - // fix later, hope it works
27
+ quiet:
28
+ - // retries three times, then gives up
@@ -0,0 +1,19 @@
1
+ id: VBC-096
2
+ name: complex-regex-no-comment
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ match:
8
+ regex: /(?:[^/\n\\]|\\.){30,}/[dgimsuvy]*
9
+ file_types:
10
+ - js
11
+ - ts
12
+ message: >-
13
+ Long regular expression literal at line {line} with no explanation. Add a comment stating
14
+ what it matches, or build it from named parts.
15
+ tests:
16
+ fire:
17
+ - const re = /^[a-z0-9]+(?:[-_][a-z0-9]+)*@example\.com$/;
18
+ quiet:
19
+ - const re = /^\d+$/;
@@ -0,0 +1,21 @@
1
+ id: VBC-097
2
+ name: br-tags-for-layout
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - html
7
+ - accessibility
8
+ match:
9
+ regex: <br\s*/?>(\s*<br\s*/?>)+
10
+ multiline: true
11
+ file_types:
12
+ - html
13
+ - js
14
+ - ts
15
+ - tsx
16
+ message: Multiple <br> tags used for layout. Use CSS margin or padding instead.
17
+ tests:
18
+ fire:
19
+ - <p>one<br /><br />two</p>
20
+ quiet:
21
+ - <p>one<br />two</p>
@@ -0,0 +1,18 @@
1
+ id: VBC-098
2
+ name: z-index-war
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - css
7
+ match:
8
+ regex: z-index\s*:\s*[0-9]{4,}
9
+ file_types:
10
+ - css
11
+ - scss
12
+ - less
13
+ message: Extremely high z-index value detected at line {line} (z-index wars).
14
+ tests:
15
+ fire:
16
+ - '.modal { z-index: 99999; }'
17
+ quiet:
18
+ - '.modal { z-index: 10; }'
@@ -0,0 +1,23 @@
1
+ id: VBC-101
2
+ name: img-missing-alt
3
+ severity: error
4
+ category: ux-dx
5
+ tags:
6
+ - accessibility
7
+ match:
8
+ regex: <img\b(?![^>]*\balt\s*=)[^>]*>
9
+ multiline: true
10
+ file_types:
11
+ - html
12
+ - js
13
+ - ts
14
+ - tsx
15
+ message: Image missing 'alt' attribute at line {line}.
16
+ tests:
17
+ fire:
18
+ - |
19
+ <img
20
+ src="/logo.png"
21
+ />
22
+ quiet:
23
+ - <img src="/logo.png" alt="Logo" />
@@ -0,0 +1,20 @@
1
+ id: VBC-102
2
+ name: multi-statement-one-liner
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ match:
8
+ ast_check:
9
+ type: one-liner-limit
10
+ message: >-
11
+ Multiple statements on a single line detected. This reduces readability.
12
+ tests:
13
+ fire:
14
+ - function run() { start(); stop(); }
15
+ quiet:
16
+ - |
17
+ function run() {
18
+ start();
19
+ stop();
20
+ }
@@ -0,0 +1,35 @@
1
+ id: VBC-103
2
+ name: ascii-art-block
3
+ severity: warning
4
+ category: docs
5
+ tags:
6
+ - docs
7
+ - style
8
+ match:
9
+ regex: >-
10
+ (?:^[ \t]*(?:#|//)(?=[^\nA-Za-z0-9]*$)[^\n]*[-=*#_|/\\+~^]{6,}[^\n]*$\n){3,}
11
+ flags: m
12
+ multiline: true
13
+ file_types:
14
+ - js
15
+ - ts
16
+ - py
17
+ - sh
18
+ message: >-
19
+ Block of decoration at line {line}: three or more comment lines that are shape and nothing
20
+ else. A rule around a section heading is punctuation and is not reported; a banner is furniture
21
+ that has to be maintained.
22
+ tests:
23
+ fire:
24
+ - |
25
+ # ####################
26
+ # ####################
27
+ # ####################
28
+ quiet:
29
+ - |
30
+ # --------------------------------
31
+ # Shared fixtures from public data
32
+ # --------------------------------
33
+ - |
34
+ # ------------------------------
35
+ - '# - handles retries'
@@ -0,0 +1,24 @@
1
+ id: VBC-104
2
+ name: inconsistent-import
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ match:
8
+ regex: >-
9
+ ^\s*import\s[\s\S]*?^\s*(?:const|let|var)\s[^\n]*=\s*require\(|^\s*(?:const|let|var)\s[^\n]*=\s*require\([\s\S]*?^\s*import\s
10
+ flags: m
11
+ multiline: true
12
+ file_types:
13
+ - js
14
+ - ts
15
+ message: Mixing 'require' and 'import' in the same file at line {line}.
16
+ tests:
17
+ fire:
18
+ - |
19
+ import fs from 'fs';
20
+ const path = require('path');
21
+ quiet:
22
+ - |
23
+ import fs from 'fs';
24
+ import path from 'path';
@@ -0,0 +1,14 @@
1
+ id: VBC-106
2
+ name: spaces-in-filenames
3
+ severity: error
4
+ category: git
5
+ tags:
6
+ - git
7
+ - cross-platform
8
+ match:
9
+ git_check: spaces_in_filenames
10
+ message: >-
11
+ Filename '{file}' contains spaces. This causes issues on some operating systems and CLI
12
+ tools.
13
+ tests:
14
+ external: 'git-checker.test.ts: needs a staged file list'
@@ -0,0 +1,20 @@
1
+ id: VBC-112
2
+ name: scroll-hijacking
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: window\.addEventListener\(['"]wheel['"]|\bscroll-behavior:\s*smooth
9
+ file_types:
10
+ - js
11
+ - ts
12
+ - css
13
+ message: >-
14
+ Potential scroll hijacking or forced smooth scrolling detected at line {line}.
15
+ tests:
16
+ fire:
17
+ - window.addEventListener('wheel', onWheel);
18
+ - 'html { scroll-behavior: smooth; }'
19
+ quiet:
20
+ - window.addEventListener('scroll', onScroll);
@@ -0,0 +1,19 @@
1
+ id: VBC-113
2
+ name: disable-zoom
3
+ severity: error
4
+ category: ux-dx
5
+ tags:
6
+ - accessibility
7
+ match:
8
+ regex: user-scalable\s*=\s*(?:no|0)\b|maximum-scale\s*=\s*1(?:\.0+)?(?![\d.])
9
+ flags: i
10
+ file_types:
11
+ - html
12
+ message: Disabling pinch-to-zoom is an accessibility violation.
13
+ tests:
14
+ fire:
15
+ - <meta name="viewport" content="width=device-width, user-scalable=no">
16
+ - <meta name="viewport" content="width=device-width, maximum-scale=1">
17
+ quiet:
18
+ - <meta name="viewport" content="width=device-width, initial-scale=1">
19
+ - <meta name="viewport" content="maximum-scale=10">
@@ -0,0 +1,21 @@
1
+ id: VBC-117
2
+ name: block-paste
3
+ severity: error
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ - security
8
+ match:
9
+ regex: >-
10
+ onPaste=['"]return false['"]|\.addEventListener\(['"]paste['"],.*?\.preventDefault\(
11
+ file_types:
12
+ - js
13
+ - ts
14
+ - html
15
+ message: >-
16
+ Blocking paste in input fields harms both UX and security (password managers).
17
+ tests:
18
+ fire:
19
+ - input.addEventListener('paste', e => e.preventDefault());
20
+ quiet:
21
+ - input.addEventListener('paste', e => normalise(e));
@@ -0,0 +1,29 @@
1
+ id: VBC-123
2
+ name: placeholder-as-label
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ - accessibility
8
+ match:
9
+ regex: >-
10
+ <input\b(?![^>]*(?:aria-label|aria-labelledby|\bid\s*=))[^>]*\bplaceholder\s*=
11
+ multiline: true
12
+ file_types:
13
+ - html
14
+ - js
15
+ - ts
16
+ - tsx
17
+ message: >-
18
+ Using placeholder as the only label at line {line} is bad for accessibility.
19
+ tests:
20
+ fire:
21
+ - |
22
+ <input
23
+ type="email"
24
+ placeholder="Email"
25
+ />
26
+ quiet:
27
+ - |
28
+ <label for="email">Email</label>
29
+ <input id="email" placeholder="you@example.com" />
@@ -0,0 +1,20 @@
1
+ id: VBC-131
2
+ name: video-background
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: <video\b(?=[^>]*\bautoplay\b)(?=[^>]*\bloop\b)(?=[^>]*\bmuted\b)[^>]*>
9
+ multiline: true
10
+ file_types:
11
+ - html
12
+ - js
13
+ - ts
14
+ - tsx
15
+ message: Autoplay video backgrounds can be distracting and impact performance.
16
+ tests:
17
+ fire:
18
+ - <video autoplay loop muted src="bg.mp4"></video>
19
+ quiet:
20
+ - <video controls src="talk.mp4"></video>
@@ -0,0 +1,27 @@
1
+ id: VBC-150
2
+ name: production-todo
3
+ severity: warning
4
+ category: core
5
+ tags:
6
+ - maintenance
7
+ match:
8
+ regex: >-
9
+ \b(?:TODO|FIXME|HACK|XXX)\b(?=\s*[\(\[][^)\]\n]+[\)\]]|\s*:?\s*(?:#\d+|[A-Z][A-Z0-9]+-\d+))
10
+ scan: comments
11
+ file_types:
12
+ - js
13
+ - ts
14
+ - py
15
+ - sh
16
+ - c
17
+ - cpp
18
+ message: >-
19
+ Tracked debt marker still in the code at line {line}. It is attributed, so close the ticket
20
+ or delete the marker before shipping.
21
+ tests:
22
+ fire:
23
+ - '// TODO(fab): rework the cache key'
24
+ - '// FIXME #1234 flaky under load'
25
+ quiet:
26
+ - '// TODO: rework the cache key'
27
+ - // explains the cache key
@@ -0,0 +1,21 @@
1
+ id: VBC-153
2
+ name: sudo-setup
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - dx
7
+ - security
8
+ match:
9
+ regex: sudo\s+npm\s+install|sudo\s+pip\s+install
10
+ file_types:
11
+ - md
12
+ - sh
13
+ message: >-
14
+ Setup instructions requiring sudo detected at line {line}. Use local environments instead.
15
+ tests:
16
+ fire:
17
+ - file: fixture.md
18
+ code: Run `sudo npm install -g slopless`.
19
+ quiet:
20
+ - file: fixture.md
21
+ code: Run `npm install -D slopless`.
@@ -0,0 +1,16 @@
1
+ id: VBC-158
2
+ name: fouc-prevention
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: '@import'
9
+ file_types:
10
+ - css
11
+ message: Use of @import in CSS can cause Flash of Unstyled Content (FOUC).
12
+ tests:
13
+ fire:
14
+ - '@import url("theme.css");'
15
+ quiet:
16
+ - '.card { color: red; }'
@@ -0,0 +1,27 @@
1
+ id: VBC-161
2
+ name: oversized-sticky-header
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: position:\s*sticky[\s\S]{0,200}?height:\s*(?:[3-9][0-9]|1[0-9][0-9])vh
9
+ multiline: true
10
+ file_types:
11
+ - css
12
+ message: Oversized sticky header (>30vh) detected at line {line}.
13
+ tests:
14
+ fire:
15
+ - |
16
+ .hero {
17
+ position: sticky;
18
+ top: 0;
19
+ height: 60vh;
20
+ }
21
+ quiet:
22
+ - |
23
+ .hero {
24
+ position: sticky;
25
+ top: 0;
26
+ height: 8vh;
27
+ }
@@ -0,0 +1,25 @@
1
+ id: VBC-172
2
+ name: hamburger-on-desktop
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ match:
8
+ regex: >-
9
+ @media[^{]*min-width:\s*(?:1[2-9][0-9][0-9]|[2-9][0-9]{3})px[^{]*\{[\s\S]{0,400}?\.(?:hamburger|burger|menu-toggle|nav-toggle)
10
+ multiline: true
11
+ file_types:
12
+ - css
13
+ - scss
14
+ message: Hamburger menu detected on desktop resolutions at line {line}.
15
+ tests:
16
+ fire:
17
+ - |
18
+ @media (min-width: 1280px) {
19
+ .hamburger-menu { display: block; }
20
+ }
21
+ quiet:
22
+ - |
23
+ @media (max-width: 640px) {
24
+ .hamburger-menu { display: block; }
25
+ }
@@ -0,0 +1,22 @@
1
+ id: VBC-197
2
+ name: hardcoded-node-version
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - dx
7
+ - portability
8
+ match:
9
+ regex: node\s+-v|node\s+--version
10
+ file_types:
11
+ - sh
12
+ - json
13
+ message: >-
14
+ Hardcoded system version check detected at line {line}. Use an .nvmrc or engine field in
15
+ package.json instead.
16
+ tests:
17
+ fire:
18
+ - file: fixture.sh
19
+ code: node -v
20
+ quiet:
21
+ - file: fixture.sh
22
+ code: node build.js
@@ -0,0 +1,35 @@
1
+ id: VBC-201
2
+ name: redundant-async
3
+ severity: warning
4
+ category: clean-code
5
+ tags:
6
+ - clean-code
7
+ - performance
8
+ match:
9
+ file_types:
10
+ - ts
11
+ - tsx
12
+ - js
13
+ - jsx
14
+ ast_check:
15
+ type: async-without-await
16
+ message: >-
17
+ async function with no await at line {line}. The async keyword only wraps the return value
18
+ in a Promise here; drop it or await something.
19
+ tests:
20
+ fire:
21
+ - |
22
+ async function load() {
23
+ return cached;
24
+ }
25
+ quiet:
26
+ - |
27
+ async function load() {
28
+ return await fetchIt();
29
+ }
30
+ - |
31
+ function load() {
32
+ return cached;
33
+ }
34
+ - |
35
+ const spy = vi.fn(async (name) => undefined);
@@ -0,0 +1,19 @@
1
+ id: VBC-206
2
+ name: pure-black-background
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - ux
7
+ - design
8
+ match:
9
+ regex: background-color:\s*#000000|background:\s*#000
10
+ file_types:
11
+ - css
12
+ message: >-
13
+ Pure black backgrounds (#000000) can cause eye strain. Consider a soft dark grey (e.g.,
14
+ #121212).
15
+ tests:
16
+ fire:
17
+ - 'body { background-color: #000000; }'
18
+ quiet:
19
+ - 'body { background-color: #12151b; }'
@@ -0,0 +1,27 @@
1
+ id: VBC-208
2
+ name: hardcoded-colors
3
+ severity: warning
4
+ category: ux-dx
5
+ tags:
6
+ - design
7
+ - maintainability
8
+ match:
9
+ regex: color:\s*(#[0-9a-fA-F]{3,6}|rgba?\(.*?\))
10
+ file_types:
11
+ - css
12
+ - scss
13
+ exclude_files:
14
+ - '**/theme.css'
15
+ - '**/vars.css'
16
+ - '**/variables.css'
17
+ - '**/tokens.css'
18
+ - '**/*.tokens.*'
19
+ message: >-
20
+ Hardcoded color value detected at line {line}. Consider using a CSS variable or theme token.
21
+ tests:
22
+ fire:
23
+ - '.card { color: #ff0044; }'
24
+ quiet:
25
+ - '.card { color: var(--text); }'
26
+ - file: theme.css
27
+ code: '.card { color: #ff0044; }'