mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-31 01:35:13 +08:00
140 lines
7.3 KiB
JSON
140 lines
7.3 KiB
JSON
{
|
|
"//": [
|
|
"Correctness rules only — deliberately NOT `extends: stylelint-config-standard`.",
|
|
"That preset is largely a formatter: it would demand its own indentation, quoting",
|
|
"and case conventions across the whole styles/ tree and bury the two or three",
|
|
"findings that actually matter under a thousand cosmetic ones. The house style",
|
|
"here is tabs + one-liners for short rules, and it is deliberate.",
|
|
"",
|
|
"What is switched on below is the set that can catch a real bug: a typo'd",
|
|
"property or pseudo-class, a broken hex, the same property twice in one block,",
|
|
"an empty block, a duplicate selector. Plus the project's own invariants from",
|
|
"docs/conventions.md — no stray !important, and no colour literal outside the palettes.",
|
|
"",
|
|
"audit.py stays: it checks things stylelint cannot see (var() defined-vs-used",
|
|
"across files, declarations shadowed within a cascade layer).",
|
|
"",
|
|
"TWO formatting rules are the deliberate exception, and they are @stylistic/* because",
|
|
"stylelint 16 removed every stylistic rule from core. The objection above is to the",
|
|
"PRESET, not to the idea: a preset imposes a hundred opinions nobody chose, while these",
|
|
"two each close a measured drift the house style already had a majority on — quotes were",
|
|
"309 double vs 47 single in attribute selectors, leading zeros 97 bare vs 5. Picking the",
|
|
"majority cost 77 autofixed characters and changed the built cascade.css by 0 bytes",
|
|
"(116548 both ways; the awk squeeze is string-aware and eats either quote identically).",
|
|
"string-quotes leaves a string that CONTAINS the preferred quote alone rather than escape",
|
|
"it — which is why the four <svg xmlns=\"…\"> data URIs in 03-palettes.css stay single.",
|
|
"That is the rule's own definition, not an exemption we wrote.",
|
|
"",
|
|
"What still CANNOT be gated, so it is on review alone: attribute-quote PRESENCE",
|
|
"(input[type=file]) and `0px` — stylelint 16 removed selector-attribute-quotes and",
|
|
"length-zero-no-unit and @stylistic never ported either.",
|
|
"",
|
|
"The second block of rules below is the same argument one layer deeper: all of them",
|
|
"are correctness, none is formatting, and every one found ZERO violations in the",
|
|
"current tree — so they cost nothing today and catch the next mistake for free.",
|
|
"declaration-property-value-no-unknown is the one that matters most: it is the only",
|
|
"check that reads a declaration's VALUE against the property's grammar, i.e. the only",
|
|
"thing that can catch a value which is invalid at computed-value time and therefore",
|
|
"vanishes in SILENCE — the exact failure mode this project tore the --*-rgb component",
|
|
"bridges out for. custom-property-no-missing-var-function catches `color: --fs-accent`",
|
|
"(a bare custom-property name where var() was meant), which parses and does nothing."
|
|
],
|
|
"plugins": ["@stylistic/stylelint-plugin"],
|
|
"rules": {
|
|
"@stylistic/string-quotes": "double",
|
|
"@stylistic/number-leading-zero": "never",
|
|
|
|
"color-no-invalid-hex": true,
|
|
"function-calc-no-unspaced-operator": true,
|
|
"string-no-newline": true,
|
|
"unit-no-unknown": true,
|
|
"property-no-unknown": true,
|
|
"declaration-block-no-duplicate-properties": [
|
|
true,
|
|
{ "ignore": ["consecutive-duplicates-with-different-values"] }
|
|
],
|
|
"declaration-block-no-shorthand-property-overrides": true,
|
|
"block-no-empty": true,
|
|
"selector-pseudo-class-no-unknown": true,
|
|
"selector-pseudo-element-no-unknown": [
|
|
true,
|
|
{
|
|
"ignorePseudoElements": [
|
|
"picker",
|
|
"picker-icon",
|
|
"checkmark",
|
|
"-webkit-slider-thumb",
|
|
"-moz-range-thumb",
|
|
"-webkit-search-decoration",
|
|
"file-selector-button"
|
|
]
|
|
}
|
|
],
|
|
"selector-type-no-unknown": true,
|
|
"media-feature-name-no-unknown": true,
|
|
"at-rule-no-unknown": true,
|
|
"comment-no-empty": true,
|
|
"no-duplicate-at-import-rules": true,
|
|
"no-duplicate-selectors": true,
|
|
"no-empty-source": true,
|
|
"no-invalid-double-slash-comments": true,
|
|
"no-invalid-position-at-import-rule": true,
|
|
"no-irregular-whitespace": true,
|
|
|
|
"declaration-property-value-no-unknown": true,
|
|
"custom-property-no-missing-var-function": true,
|
|
"declaration-block-no-duplicate-custom-properties": true,
|
|
"function-no-unknown": true,
|
|
"media-query-no-invalid": true,
|
|
"selector-anb-no-unmatchable": true,
|
|
"keyframe-declaration-no-important": true,
|
|
"no-invalid-position-declaration": true,
|
|
"font-family-no-missing-generic-family-keyword": true,
|
|
"syntax-string-no-invalid": true,
|
|
|
|
"no-descending-specificity": null,
|
|
|
|
"declaration-no-important": true
|
|
},
|
|
"overrides": [
|
|
{
|
|
"//": [
|
|
"The only files allowed an !important, and each for a documented reason.",
|
|
"theme/90-responsive + pages/20-overview outrank an inline style= attribute",
|
|
"written by 29_ports.js / ui.js — no cascade layer can do that, only an author",
|
|
"!important can. theme/45-misc is the same case: the realtime graphs write",
|
|
"style='width:100%' on the box they draw into, and the theme has to widen it or",
|
|
"the drawing (sized from #view) is clipped by the card's gutter. theme/65-dropdown",
|
|
"carries the widget's ul-margin flag (ui.js writes margin inline on an open list),",
|
|
"moved in with the machinery absorbed from base/80-dropdown.css. styles/base",
|
|
"keeps the .left/.right/.center forcing utilities and the two inline-style fighters",
|
|
"in 95-luci. theme/95-a11y-media is the reduced-motion block: !important INVERTS",
|
|
"the layer order, so it is the only way one rule can stop animations declared in",
|
|
"base as well as in theme.",
|
|
"theme/30-tables carries THREE flags, one per adversary a layer cannot reach.",
|
|
"`word-break: normal` fights two of them: luci-mod-status's processes.js writes",
|
|
"style='word-break: break-word' on its Command span, the deprecated alias for",
|
|
"`overflow-wrap: anywhere`, erasing the column's min-content from an inline declaration",
|
|
"— measured on the router, the column sat at 126px against a 353px token and no layered",
|
|
"rule could move it; and luci-mod-dashboard ships `word-break: break-all` UNLAYERED,",
|
|
"which splits a token that would have fitted. The other two hold a carded cell's width",
|
|
"against that same unlayered sheet's `flex: 2 2 10%`, two because a meter cell needs the",
|
|
"whole line where a label/value pair needs half.",
|
|
"This list is the contract. Adding a file to it needs a reason in the file.",
|
|
"The same list lives in tools/audit.py (BANG_OK); tools/bang-ok.mjs (npm run bang-ok)",
|
|
"fails if the two drift, so a file added here but not there (or vice versa) is caught."
|
|
],
|
|
"files": [
|
|
"luci-theme-footstrap/styles/base/*.css",
|
|
"luci-theme-footstrap/styles/theme/30-tables.css",
|
|
"luci-theme-footstrap/styles/theme/45-misc.css",
|
|
"luci-theme-footstrap/styles/theme/65-dropdown.css",
|
|
"luci-theme-footstrap/styles/theme/90-responsive.css",
|
|
"luci-theme-footstrap/styles/theme/95-a11y-media.css",
|
|
"luci-theme-footstrap/styles/pages/20-overview.css"
|
|
],
|
|
"rules": { "declaration-no-important": null }
|
|
}
|
|
]
|
|
}
|