This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 2a18a556b09 chore(frontend): lint Emotion CSS-in-JS with Stylelint 
(#41871)
2a18a556b09 is described below

commit 2a18a556b09b736e7a241fa85c885933e2d3a683
Author: Evan Rusackas <[email protected]>
AuthorDate: Sun Jul 12 08:45:02 2026 -0700

    chore(frontend): lint Emotion CSS-in-JS with Stylelint (#41871)
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
    Co-authored-by: Đỗ Trọng Hải <[email protected]>
---
 .pre-commit-config.yaml                            |   6 +
 .rat-excludes                                      |   1 +
 scripts/stylelint.sh                               |  49 ++
 superset-frontend/.stylelintignore                 |   9 +
 superset-frontend/package-lock.json                | 926 +++++++++++++++++++--
 superset-frontend/package.json                     |   6 +-
 .../superset-core/src/theme/GlobalStyles.tsx       |   8 +-
 .../src/components/AsyncAceEditor/index.tsx        |   1 -
 .../DropdownContainer/DropdownContainer.tsx        |   4 +-
 .../src/components/Icons/Icons.stories.tsx         |   6 +-
 .../src/components/Modal/Modal.tsx                 |   4 +-
 .../src/styles/index.tsx                           |   1 -
 .../src/SqlLab/SqlLabGlobalStyles.tsx              |  10 +-
 .../src/SqlLab/components/EditorWrapper/index.tsx  |   4 +-
 .../src/SqlLab/components/ResultSet/index.tsx      |   1 -
 .../src/components/MessageToasts/Toast.tsx         |   2 +-
 .../src/components/ResizableSidebar/index.tsx      |   2 +-
 .../components/AddSliceCard/AddSliceCard.tsx       |   2 +-
 .../DashboardBuilder/DashboardBuilder.tsx          |   4 +-
 .../src/dashboard/components/DashboardGrid.tsx     |   8 +-
 .../dashboard/components/FiltersBadge/Styles.tsx   |   1 -
 .../src/dashboard/components/Header/index.tsx      |   2 +-
 .../components/filterscope/FilterScopeSelector.tsx |   2 +-
 .../src/dashboard/components/menu/HoverMenu.tsx    |   2 +-
 .../FilterControls/FilterControlShared.tsx         |   4 +-
 .../nativeFilters/FilterBar/Vertical.tsx           |   2 +-
 .../FiltersConfigForm/RemovedFilter.tsx            |   2 +-
 .../explore/components/ControlPanelsContainer.tsx  |   8 +-
 .../explore/components/DataTableControl/index.tsx  |   2 +-
 .../DatasourcePanelDragOption/index.tsx            |   2 +-
 .../controls/VizTypeControl/VizTypeGallery.tsx     |   2 +-
 .../components/controls/VizTypeControl/index.tsx   |   1 -
 .../databases/UploadDataModel/ColumnsPreview.tsx   |   4 +-
 .../src/features/home/SavedQueries.tsx             |   1 -
 .../src/visualizations/TimeTable/TimeTable.tsx     |   2 +-
 superset-frontend/stylelint.config.js              |  34 +
 36 files changed, 1010 insertions(+), 115 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a1f3214ab05..98b34d3dc0a 100755
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -81,6 +81,12 @@ repos:
         language: system
         pass_filenames: true
         files: ^superset-frontend/.*\.(js|jsx|ts|tsx)$
+      - id: stylelint-frontend
+        name: stylelint (frontend css-in-js)
+        entry: ./scripts/stylelint.sh
+        language: system
+        pass_filenames: true
+        files: ^superset-frontend/.*\.(js|jsx|ts|tsx)$
       - id: eslint-docs
         name: eslint (docs)
         entry: bash -c 'cd docs && FILES=$(printf "%s\n" "$@" | sed 
"s|^docs/||" | tr "\n" " ") && yarn eslint --fix --quiet $FILES'
diff --git a/.rat-excludes b/.rat-excludes
index 30b1ef1da9d..7b788738f2d 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -7,6 +7,7 @@
 .codecov.yml
 .eslintrc
 .eslintignore
+.stylelintignore
 .flake8
 .nvmrc
 .prettierrc
diff --git a/scripts/stylelint.sh b/scripts/stylelint.sh
new file mode 100755
index 00000000000..82980526ec9
--- /dev/null
+++ b/scripts/stylelint.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+script_dir="$(dirname "$(realpath "$0")")"
+root_dir="$(dirname "$script_dir")"
+frontend_dir=superset-frontend
+
+if [[ ! -d "$root_dir/$frontend_dir" ]]; then
+  echo "Error: $frontend_dir directory not found in $root_dir" >&2
+  exit 1
+fi
+
+cd "$root_dir/$frontend_dir"
+
+# Filter files to only include JS/TS files and remove the frontend dir prefix
+js_ts_files=()
+for file in "$@"; do
+  # Remove superset-frontend/ prefix if present
+  cleaned_file="${file#$frontend_dir/}"
+
+  # Only include JS/TS files (Emotion styles live in these)
+  if [[ "$cleaned_file" =~ \.(js|jsx|ts|tsx)$ ]]; then
+    js_ts_files+=("$cleaned_file")
+  fi
+done
+
+# Only run if we have JS/TS files to lint. --allow-empty-input keeps the hook
+# green when every passed file is covered by .stylelintignore.
+if [ ${#js_ts_files[@]} -gt 0 ]; then
+  npx stylelint --allow-empty-input "${js_ts_files[@]}"
+else
+  echo "No JavaScript/TypeScript files to lint for styles"
+fi
diff --git a/superset-frontend/.stylelintignore 
b/superset-frontend/.stylelintignore
new file mode 100644
index 00000000000..9691403a12f
--- /dev/null
+++ b/superset-frontend/.stylelintignore
@@ -0,0 +1,9 @@
+# Files whose Emotion template literals nest a `css` tag inside an
+# interpolation (e.g. styled(X)`${props => css`...`}`) or otherwise use a
+# structure that postcss-styled-syntax cannot parse. Stylelint reports a
+# CssSyntaxError on these rather than a real lint finding, so they are
+# excluded until the syntax gains nested-template support.
+src/components/ListView/Filters/Base.ts
+src/features/databases/DatabaseModal/styles.ts
+src/features/databases/UploadDataModel/styles.ts
+packages/superset-ui-core/src/components/Typography/index.tsx
diff --git a/superset-frontend/package-lock.json 
b/superset-frontend/package-lock.json
index 91dc079a63b..20a3638f61e 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -263,6 +263,7 @@
         "open-cli": "^9.0.0",
         "oxlint": "^1.72.0",
         "po2json": "^0.4.5",
+        "postcss-styled-syntax": "^0.7.2",
         "prettier": "3.9.4",
         "prettier-plugin-packagejson": "^3.0.2",
         "process": "^0.11.10",
@@ -275,6 +276,7 @@
         "speed-measure-webpack-plugin": "^1.6.0",
         "storybook": "10.4.6",
         "style-loader": "^4.0.0",
+        "stylelint": "^17.14.0",
         "swc-loader": "^0.2.7",
         "ts-jest": "^29.4.11",
         "tscw-config": "^1.1.2",
@@ -2703,6 +2705,67 @@
       "dev": true,
       "license": "CC0-1.0"
     },
+    "node_modules/@cacheable/memory": {
+      "version": "2.2.0",
+      "resolved": 
"https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz";,
+      "integrity": 
"sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@cacheable/utils": "^2.5.0",
+        "@keyv/bigmap": "^1.3.1",
+        "hookified": "^1.15.1",
+        "keyv": "^5.6.0"
+      }
+    },
+    "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz";,
+      "integrity": 
"sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hashery": "^1.4.0",
+        "hookified": "^1.15.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "keyv": "^5.6.0"
+      }
+    },
+    "node_modules/@cacheable/memory/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz";,
+      "integrity": 
"sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@keyv/serialize": "^1.1.1"
+      }
+    },
+    "node_modules/@cacheable/utils": {
+      "version": "2.5.0",
+      "resolved": 
"https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz";,
+      "integrity": 
"sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hashery": "^1.5.1",
+        "keyv": "^5.6.0"
+      }
+    },
+    "node_modules/@cacheable/utils/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz";,
+      "integrity": 
"sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@keyv/serialize": "^1.1.1"
+      }
+    },
     "node_modules/@cspotcode/source-map-support": {
       "version": "0.8.1",
       "resolved": 
"https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz";,
@@ -2840,6 +2903,76 @@
         "node": ">=20.19.0"
       }
     },
+    "node_modules/@csstools/media-query-list-parser": {
+      "version": "5.0.0",
+      "resolved": 
"https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz";,
+      "integrity": 
"sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools";
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools";
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/@csstools/selector-resolve-nested": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz";,
+      "integrity": 
"sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools";
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools";
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "postcss-selector-parser": "^7.1.1"
+      }
+    },
+    "node_modules/@csstools/selector-specificity": {
+      "version": "6.0.0",
+      "resolved": 
"https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz";,
+      "integrity": 
"sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools";
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools";
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "postcss-selector-parser": "^7.1.1"
+      }
+    },
     "node_modules/@deck.gl/aggregation-layers": {
       "version": "9.2.11",
       "resolved": 
"https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.2.11.tgz";,
@@ -5861,6 +5994,13 @@
         "tslib": "2"
       }
     },
+    "node_modules/@keyv/serialize": {
+      "version": "1.1.1",
+      "resolved": 
"https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz";,
+      "integrity": 
"sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@kwsites/file-exists": {
       "version": "1.1.1",
       "resolved": 
"https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz";,
@@ -7318,20 +7458,6 @@
         "node": "^18.17.0 || >=20.5.0"
       }
     },
-    "node_modules/@npmcli/query/node_modules/postcss-selector-parser": {
-      "version": "7.1.1",
-      "resolved": 
"https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz";,
-      "integrity": 
"sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/@npmcli/redact": {
       "version": "3.2.2",
       "resolved": 
"https://registry.npmjs.org/@npmcli/redact/-/redact-3.2.2.tgz";,
@@ -8444,9 +8570,6 @@
         "arm64"
       ],
       "dev": true,
-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8464,9 +8587,6 @@
         "arm64"
       ],
       "dev": true,
-      "libc": [
-        "musl"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8484,9 +8604,6 @@
         "ppc64"
       ],
       "dev": true,
-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8504,9 +8621,6 @@
         "riscv64"
       ],
       "dev": true,
-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8524,9 +8638,6 @@
         "riscv64"
       ],
       "dev": true,
-      "libc": [
-        "musl"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8544,9 +8655,6 @@
         "s390x"
       ],
       "dev": true,
-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8564,9 +8672,6 @@
         "x64"
       ],
       "dev": true,
-      "libc": [
-        "glibc"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -8584,9 +8689,6 @@
         "x64"
       ],
       "dev": true,
-      "libc": [
-        "musl"
-      ],
       "license": "MIT",
       "optional": true,
       "os": [
@@ -14239,6 +14341,16 @@
         "node": ">=4"
       }
     },
+    "node_modules/astral-regex": {
+      "version": "2.0.0",
+      "resolved": 
"https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz";,
+      "integrity": 
"sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/async": {
       "version": "3.2.6",
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz";,
@@ -15270,6 +15382,30 @@
         "node": "^20.17.0 || >=22.9.0"
       }
     },
+    "node_modules/cacheable": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz";,
+      "integrity": 
"sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@cacheable/memory": "^2.2.0",
+        "@cacheable/utils": "^2.5.0",
+        "hookified": "^1.15.0",
+        "keyv": "^5.6.0",
+        "qified": "^0.10.1"
+      }
+    },
+    "node_modules/cacheable/node_modules/keyv": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz";,
+      "integrity": 
"sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@keyv/serialize": "^1.1.1"
+      }
+    },
     "node_modules/caching-transform": {
       "version": "4.0.0",
       "resolved": 
"https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz";,
@@ -15998,6 +16134,13 @@
         "color-support": "bin.js"
       }
     },
+    "node_modules/colord": {
+      "version": "2.9.3",
+      "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz";,
+      "integrity": 
"sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/colorette": {
       "version": "2.0.20",
       "resolved": 
"https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz";,
@@ -16758,6 +16901,16 @@
       "integrity": 
"sha512-C4aQOpCmQL/Arl68chQatNh7/Nfyty15kbLNZezGudjcKSqHHVoHQEeb9IJcjgQ6CiurrHZoEt47yce891vjGw==",
       "license": "BSD-3-Clause"
     },
+    "node_modules/css-functions-list": {
+      "version": "3.3.3",
+      "resolved": 
"https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz";,
+      "integrity": 
"sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/css-line-break": {
       "version": "2.1.0",
       "resolved": 
"https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz";,
@@ -19870,6 +20023,16 @@
         "fxparser": "src/cli/cli.js"
       }
     },
+    "node_modules/fastest-levenshtein": {
+      "version": "1.0.16",
+      "resolved": 
"https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz";,
+      "integrity": 
"sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4.9.1"
+      }
+    },
     "node_modules/fastq": {
       "version": "1.18.0",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz";,
@@ -21258,9 +21421,9 @@
       }
     },
     "node_modules/get-east-asian-width": {
-      "version": "1.3.0",
-      "resolved": 
"https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz";,
-      "integrity": 
"sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+      "version": "1.6.0",
+      "resolved": 
"https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz";,
+      "integrity": 
"sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
       "license": "MIT",
       "engines": {
         "node": ">=18"
@@ -21737,6 +21900,57 @@
         "url": "https://github.com/sponsors/ljharb";
       }
     },
+    "node_modules/globby": {
+      "version": "16.2.1",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.1.tgz";,
+      "integrity": 
"sha512-JmsqJalahxxgW8V2ecSQ2G7UjPlI9cpKdrkG9KoNiXhd/YslXOTEB0cViENWUznuovIuNT+FkMbraDGjr4FCUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/merge-streams": "^4.0.0",
+        "fast-glob": "^3.3.3",
+        "ignore": "^7.0.5",
+        "is-path-inside": "^4.0.0",
+        "slash": "^5.1.0",
+        "unicorn-magic": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/globby/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz";,
+      "integrity": 
"sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/globby/node_modules/slash": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz";,
+      "integrity": 
"sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/globjoin": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz";,
+      "integrity": 
"sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/google-auth-library": {
       "version": "10.9.0",
       "resolved": 
"https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.0.tgz";,
@@ -21992,6 +22206,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/hashery": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz";,
+      "integrity": 
"sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hookified": "^1.15.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
     "node_modules/hasown": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz";,
@@ -22254,6 +22481,13 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/hookified": {
+      "version": "1.15.1",
+      "resolved": 
"https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz";,
+      "integrity": 
"sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/hosted-git-info": {
       "version": "9.0.3",
       "resolved": 
"https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz";,
@@ -22397,6 +22631,19 @@
         "node": ">= 12"
       }
     },
+    "node_modules/html-tags": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz";,
+      "integrity": 
"sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
     "node_modules/html-url-attributes": {
       "version": "3.0.1",
       "resolved": 
"https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz";,
@@ -22914,6 +23161,17 @@
         "url": "https://github.com/sponsors/sindresorhus";
       }
     },
+    "node_modules/import-meta-resolve": {
+      "version": "4.2.0",
+      "resolved": 
"https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz";,
+      "integrity": 
"sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm";
+      }
+    },
     "node_modules/imports-loader": {
       "version": "5.0.0",
       "resolved": 
"https://registry.npmjs.org/imports-loader/-/imports-loader-5.0.0.tgz";,
@@ -23578,6 +23836,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-path-inside": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz";,
+      "integrity": 
"sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
     "node_modules/is-plain-obj": {
       "version": "1.1.0",
       "resolved": 
"https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz";,
@@ -25892,6 +26163,21 @@
         }
       }
     },
+    "node_modules/jsdom/node_modules/@noble/hashes": {
+      "version": "2.2.0",
+      "resolved": 
"https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz";,
+      "integrity": 
"sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/";
+      }
+    },
     "node_modules/jsdom/node_modules/css-tree": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz";,
@@ -27455,6 +27741,13 @@
       "integrity": 
"sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
       "license": "MIT"
     },
+    "node_modules/lodash.truncate": {
+      "version": "4.4.2",
+      "resolved": 
"https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz";,
+      "integrity": 
"sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/loglevel": {
       "version": "1.9.2",
       "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz";,
@@ -27823,6 +28116,17 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/mathml-tag-names": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz";,
+      "integrity": 
"sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm";
+      }
+    },
     "node_modules/md5": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz";,
@@ -32176,28 +32480,30 @@
         "postcss": "^8.1.0"
       }
     },
-    
"node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser":
 {
-      "version": "7.1.1",
-      "resolved": 
"https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz";,
-      "integrity": 
"sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+    "node_modules/postcss-modules-scope": {
+      "version": "3.2.1",
+      "resolved": 
"https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz";,
+      "integrity": 
"sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
       "dev": true,
-      "license": "MIT",
+      "license": "ISC",
       "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
+        "postcss-selector-parser": "^7.0.0"
       },
       "engines": {
-        "node": ">=4"
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
       }
     },
-    "node_modules/postcss-modules-scope": {
-      "version": "3.2.1",
-      "resolved": 
"https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz";,
-      "integrity": 
"sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+    "node_modules/postcss-modules-values": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz";,
+      "integrity": 
"sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "postcss-selector-parser": "^7.0.0"
+        "icss-utils": "^5.0.0"
       },
       "engines": {
         "node": "^10 || ^12 || >= 14"
@@ -32206,10 +32512,37 @@
         "postcss": "^8.1.0"
       }
     },
-    "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": 
{
-      "version": "7.1.1",
-      "resolved": 
"https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz";,
-      "integrity": 
"sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
+    "node_modules/postcss-safe-parser": {
+      "version": "7.0.1",
+      "resolved": 
"https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz";,
+      "integrity": 
"sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/";
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser";
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai";
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.31"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "7.1.4",
+      "resolved": 
"https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz";,
+      "integrity": 
"sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -32220,20 +32553,34 @@
         "node": ">=4"
       }
     },
-    "node_modules/postcss-modules-values": {
-      "version": "4.0.0",
-      "resolved": 
"https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz";,
-      "integrity": 
"sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+    "node_modules/postcss-styled-syntax": {
+      "version": "0.7.2",
+      "resolved": 
"https://registry.npmjs.org/postcss-styled-syntax/-/postcss-styled-syntax-0.7.2.tgz";,
+      "integrity": 
"sha512-PrxG8BC6yh6pmrHx7NrseYt5TiSN57LbGYZxmreg7co7g+zXe6BwUfsZ6r/gghxbtabde3Z0hinelXj4jlJF4Q==",
       "dev": true,
-      "license": "ISC",
+      "license": "MIT",
       "dependencies": {
-        "icss-utils": "^5.0.0"
+        "typescript": "^6.0.3"
       },
       "engines": {
-        "node": "^10 || ^12 || >= 14"
+        "node": ">=14.17"
       },
       "peerDependencies": {
-        "postcss": "^8.1.0"
+        "postcss": "^8.5.1"
+      }
+    },
+    "node_modules/postcss-styled-syntax/node_modules/typescript": {
+      "version": "6.0.3",
+      "resolved": 
"https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz";,
+      "integrity": 
"sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
       }
     },
     "node_modules/postcss-value-parser": {
@@ -32654,6 +33001,26 @@
         "node": ">=16.0.0"
       }
     },
+    "node_modules/qified": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz";,
+      "integrity": 
"sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hookified": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/qified/node_modules/hookified": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz";,
+      "integrity": 
"sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/qs": {
       "version": "6.15.2",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz";,
@@ -36198,6 +36565,24 @@
         "node": ">=6"
       }
     },
+    "node_modules/slice-ansi": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz";,
+      "integrity": 
"sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1";
+      }
+    },
     "node_modules/smart-buffer": {
       "version": "4.2.0",
       "resolved": 
"https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz";,
@@ -37449,6 +37834,334 @@
         "inline-style-parser": "0.2.7"
       }
     },
+    "node_modules/stylelint": {
+      "version": "17.14.0",
+      "resolved": 
"https://registry.npmjs.org/stylelint/-/stylelint-17.14.0.tgz";,
+      "integrity": 
"sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/stylelint";
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/stylelint";
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/css-calc": "^3.2.1",
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-syntax-patches-for-csstree": "^1.1.5",
+        "@csstools/css-tokenizer": "^4.0.0",
+        "@csstools/media-query-list-parser": "^5.0.0",
+        "@csstools/selector-resolve-nested": "^4.0.0",
+        "@csstools/selector-specificity": "^6.0.0",
+        "colord": "^2.9.3",
+        "cosmiconfig": "^9.0.2",
+        "css-functions-list": "^3.3.3",
+        "css-tree": "^3.2.1",
+        "debug": "^4.4.3",
+        "fast-glob": "^3.3.3",
+        "fastest-levenshtein": "^1.0.16",
+        "file-entry-cache": "^11.1.3",
+        "global-modules": "^2.0.0",
+        "globby": "^16.2.0",
+        "globjoin": "^0.1.4",
+        "html-tags": "^5.1.0",
+        "ignore": "^7.0.5",
+        "import-meta-resolve": "^4.2.0",
+        "mathml-tag-names": "^4.0.0",
+        "meow": "^14.1.0",
+        "micromatch": "^4.0.8",
+        "normalize-path": "^3.0.0",
+        "picocolors": "^1.1.1",
+        "postcss": "^8.5.15",
+        "postcss-safe-parser": "^7.0.1",
+        "postcss-selector-parser": "^7.1.4",
+        "postcss-value-parser": "^4.2.0",
+        "string-width": "^8.2.1",
+        "supports-hyperlinks": "^4.4.0",
+        "svg-tags": "^1.0.0",
+        "table": "^6.9.0",
+        "write-file-atomic": "^7.0.1"
+      },
+      "bin": {
+        "stylelint": "bin/stylelint.mjs"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    
"node_modules/stylelint/node_modules/@csstools/css-syntax-patches-for-csstree": 
{
+      "version": "1.1.6",
+      "resolved": 
"https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.6.tgz";,
+      "integrity": 
"sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools";
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools";
+        }
+      ],
+      "license": "MIT-0",
+      "peerDependencies": {
+        "css-tree": "^3.2.1"
+      },
+      "peerDependenciesMeta": {
+        "css-tree": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/stylelint/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": 
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz";,
+      "integrity": 
"sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1";
+      }
+    },
+    "node_modules/stylelint/node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz";,
+      "integrity": 
"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/stylelint/node_modules/cosmiconfig": {
+      "version": "9.0.2",
+      "resolved": 
"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz";,
+      "integrity": 
"sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "env-paths": "^2.2.1",
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer";
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/stylelint/node_modules/css-tree": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz";,
+      "integrity": 
"sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
+    "node_modules/stylelint/node_modules/file-entry-cache": {
+      "version": "11.1.5",
+      "resolved": 
"https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz";,
+      "integrity": 
"sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^6.1.23"
+      }
+    },
+    "node_modules/stylelint/node_modules/flat-cache": {
+      "version": "6.1.23",
+      "resolved": 
"https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz";,
+      "integrity": 
"sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cacheable": "^2.5.0",
+        "flatted": "^3.4.2",
+        "hookified": "^1.15.0"
+      }
+    },
+    "node_modules/stylelint/node_modules/global-modules": {
+      "version": "2.0.0",
+      "resolved": 
"https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz";,
+      "integrity": 
"sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "global-prefix": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/stylelint/node_modules/global-prefix": {
+      "version": "3.0.0",
+      "resolved": 
"https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz";,
+      "integrity": 
"sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ini": "^1.3.5",
+        "kind-of": "^6.0.2",
+        "which": "^1.3.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/stylelint/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz";,
+      "integrity": 
"sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/stylelint/node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz";,
+      "integrity": 
"sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/stylelint/node_modules/js-yaml": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz";,
+      "integrity": 
"sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin";
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca";
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/stylelint/node_modules/mdn-data": {
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz";,
+      "integrity": 
"sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/stylelint/node_modules/meow": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz";,
+      "integrity": 
"sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/stylelint/node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": 
"https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz";,
+      "integrity": 
"sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs";
+      }
+    },
+    "node_modules/stylelint/node_modules/string-width": {
+      "version": "8.2.2",
+      "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz";,
+      "integrity": 
"sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "get-east-asian-width": "^1.5.0",
+        "strip-ansi": "^7.1.2"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/stylelint/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz";,
+      "integrity": 
"sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1";
+      }
+    },
+    "node_modules/stylelint/node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz";,
+      "integrity": 
"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/stylelint/node_modules/write-file-atomic": {
+      "version": "7.0.1",
+      "resolved": 
"https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz";,
+      "integrity": 
"sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": "^20.17.0 || >=22.9.0"
+      }
+    },
     "node_modules/stylis": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz";,
@@ -37476,6 +38189,49 @@
         "node": ">=8"
       }
     },
+    "node_modules/supports-hyperlinks": {
+      "version": "4.5.0",
+      "resolved": 
"https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.5.0.tgz";,
+      "integrity": 
"sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^5.0.1",
+        "supports-color": "^10.2.2"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1";
+      }
+    },
+    "node_modules/supports-hyperlinks/node_modules/has-flag": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz";,
+      "integrity": 
"sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/supports-hyperlinks/node_modules/supports-color": {
+      "version": "10.2.2",
+      "resolved": 
"https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz";,
+      "integrity": 
"sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1";
+      }
+    },
     "node_modules/supports-preserve-symlinks-flag": {
       "version": "1.0.0",
       "resolved": 
"https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz";,
@@ -37505,6 +38261,12 @@
         "node": ">=12.0.0"
       }
     },
+    "node_modules/svg-tags": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz";,
+      "integrity": 
"sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+      "dev": true
+    },
     "node_modules/svgo": {
       "version": "3.3.3",
       "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz";,
@@ -37588,6 +38350,23 @@
         "url": "https://opencollective.com/synckit";
       }
     },
+    "node_modules/table": {
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz";,
+      "integrity": 
"sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "ajv": "^8.0.1",
+        "lodash.truncate": "^4.4.2",
+        "slice-ansi": "^4.0.0",
+        "string-width": "^4.2.3",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/tagged-tag": {
       "version": "1.0.0",
       "resolved": 
"https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz";,
@@ -40649,6 +41428,21 @@
         }
       }
     },
+    "node_modules/whatwg-url/node_modules/@noble/hashes": {
+      "version": "2.2.0",
+      "resolved": 
"https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz";,
+      "integrity": 
"sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "peer": true,
+      "engines": {
+        "node": ">= 20.19.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/";
+      }
+    },
     "node_modules/whatwg-url/node_modules/webidl-conversions": {
       "version": "8.0.1",
       "resolved": 
"https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz";,
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 8167496c282..230ef6ca3f2 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -57,7 +57,9 @@
     "lint:all": "npx oxlint --config oxlint.json && npm run type",
     "lint-fix": "npx oxlint --config oxlint.json --fix --quiet",
     "lint-fix:all": "npx oxlint --config oxlint.json --fix",
-    "lint:full": "npm run lint && npm run check:custom-rules",
+    "lint:full": "npm run lint && npm run check:custom-rules && npm run 
stylelint",
+    "stylelint": "stylelint 
\"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
+    "stylelint-fix": "stylelint --fix 
\"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
     "check:custom-rules": "node scripts/check-custom-rules.js",
     "check:storybook-coverage": "node scripts/check-storybook-coverage.js",
     "ensure-oxc": "echo 'OXC linter is ready' && npx oxlint --version",
@@ -346,6 +348,7 @@
     "open-cli": "^9.0.0",
     "oxlint": "^1.72.0",
     "po2json": "^0.4.5",
+    "postcss-styled-syntax": "^0.7.2",
     "prettier": "3.9.4",
     "prettier-plugin-packagejson": "^3.0.2",
     "process": "^0.11.10",
@@ -358,6 +361,7 @@
     "speed-measure-webpack-plugin": "^1.6.0",
     "storybook": "10.4.6",
     "style-loader": "^4.0.0",
+    "stylelint": "^17.14.0",
     "swc-loader": "^0.2.7",
     "ts-jest": "^29.4.11",
     "tscw-config": "^1.1.2",
diff --git 
a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx 
b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
index 75956133675..7b9b34b2b60 100644
--- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
+++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
@@ -38,7 +38,7 @@ export const GlobalStyles = () => {
     <Global
       key={`global-${theme.colorLink}`}
       styles={css`
-        // SPA
+        /* SPA */
         html {
           color-scheme: ${isDark ? 'dark' : 'light'};
         }
@@ -93,7 +93,7 @@ export const GlobalStyles = () => {
           }
         }
 
-        // Overriding bootstrap styles
+        /* Overriding bootstrap styles */
         #app {
           flex: 1 1 auto;
           position: relative;
@@ -105,8 +105,8 @@ export const GlobalStyles = () => {
           cursor: pointer;
         }
 
-        // Override geostyler CSS that hides AntD ColorPicker alpha input
-        // See: https://github.com/apache/superset/issues/34721
+        /* Override geostyler CSS that hides AntD ColorPicker alpha input */
+        /* See: https://github.com/apache/superset/issues/34721 */
         .ant-color-picker .ant-color-picker-alpha-input {
           display: block;
         }
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx
index 5d51b25f5d3..425db253f89 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/AsyncAceEditor/index.tsx
@@ -444,7 +444,6 @@ export function AsyncAceEditor(
                 /* Adjust tooltip styles */
                 .ace_tooltip {
                   margin-left: ${token.margin}px;
-                  padding: ${token.sizeUnit * 2}px;
                   background-color: ${token.colorBgElevated} !important;
                   color: ${token.colorText} !important;
                   border: 1px solid ${token.colorBorderSecondary};
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
index 4e7edc904f7..e42d3154826 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
@@ -339,8 +339,8 @@ export const DropdownContainer = forwardRef(
             <Global
               styles={css`
                 .ant-popover-inner {
-                  // Some OS versions only show the scroll when hovering.
-                  // These settings will make the scroll always visible.
+                  /* Some OS versions only show the scroll when hovering. */
+                  /* These settings will make the scroll always visible. */
                   ::-webkit-scrollbar {
                     -webkit-appearance: none;
                     width: 14px;
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Icons/Icons.stories.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/Icons/Icons.stories.tsx
index db107ef282d..a7a9f601294 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/Icons/Icons.stories.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/Icons/Icons.stories.tsx
@@ -63,11 +63,11 @@ const IconBlock = styled.div`
 
   span {
     margin-top: ${({ theme }) =>
-      2 * theme.sizeUnit}px; // Add spacing between icon and name
+      2 * theme.sizeUnit}px; /* Add spacing between icon and name */
     font-size: ${({ theme }) =>
-      theme.fontSizeSM}; // Optional: adjust font size for elegance
+      theme.fontSizeSM}; /* Optional: adjust font size for elegance */
     color: ${({ theme }) =>
-      theme.colorText}; // Optional: subtle color for the name
+      theme.colorText}; /* Optional: subtle color for the name */
   }
 `;
 
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx 
b/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
index 21216206f5a..f8768f29c35 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
@@ -120,8 +120,8 @@ export const StyledModal = 
styled(BaseModal)<StyledModalProps>`
         right: 0;
         display: flex;
         justify-content: center;
-        // Keep the close button clickable when modal body content uses
-        // position: sticky with elevated z-index (e.g. DatabaseModal header).
+        /* Keep the close button clickable when modal body content uses */
+        /* position: sticky with elevated z-index (e.g. DatabaseModal header). 
*/
         z-index: ${theme.zIndexPopupBase + 1};
       }
 
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/styles/index.tsx 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/styles/index.tsx
index 3bb714e03a2..892d571c143 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/styles/index.tsx
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/styles/index.tsx
@@ -74,7 +74,6 @@ export const FilterIconWrapper = styled.div<{ 
isFilterActive?: boolean }>`
 
   padding: 3px 4px;
   overflow: hidden;
-  cursor: pointer;
   border-radius: 4px;
 
   ${({ isFilterActive }) =>
diff --git a/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx 
b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
index a8d33009829..fc84a68df91 100644
--- a/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
+++ b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
@@ -32,14 +32,14 @@ export const SqlLabGlobalStyles = () => (
         min-height: max(
           100vh,
           ${theme.sizeUnit * 125}px
-        ); // Set a min height so the gutter is always visible when resizing
+        ); /* Set a min height so the gutter is always visible when resizing */
         overflow: hidden;
       }
 
-      // The tab label is a flex node (icon menu + title + status icon). antd's
-      // overflow dropdown styles each menu item for a plain-text label, so the
-      // nested flex defeats its ellipsis and very long names render blank. Cap
-      // the item width and let the title truncate inside it.
+      /* The tab label is a flex node (icon menu + title + status icon). 
antd's */
+      /* overflow dropdown styles each menu item for a plain-text label, so 
the */
+      /* nested flex defeats its ellipsis and very long names render blank. 
Cap */
+      /* the item width and let the title truncate inside it. */
       .${SQLLAB_TAB_OVERFLOW_POPUP_CLASS} {
         .ant-tabs-dropdown-menu-item {
           max-width: ${theme.sizeUnit * 80}px;
diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx 
b/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
index a11158c0942..9307b03ccce 100644
--- a/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
+++ b/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
@@ -344,8 +344,8 @@ const EditorWrapper = ({
         }
 
         .ace_autocomplete {
-          // Use !important because Ace Editor applies extra CSS at the last 
second
-          // when opening the autocomplete.
+          /* Use !important because Ace Editor applies extra CSS at the last 
second */
+          /* when opening the autocomplete. */
           width: ${theme.sizeUnit * 130}px !important;
         }
 
diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx 
b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index 8f8600a5343..50e72efa37b 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -138,7 +138,6 @@ const ResultlessStyles = styled.div`
 // but wrapping text too so text doesn't overflow
 const MonospaceDiv = styled.div`
   font-family: ${({ theme }) => theme.fontFamilyCode};
-  white-space: pre;
   word-break: break-word;
   overflow-x: auto;
   white-space: pre-wrap;
diff --git a/superset-frontend/src/components/MessageToasts/Toast.tsx 
b/superset-frontend/src/components/MessageToasts/Toast.tsx
index 97205384058..7884b615995 100644
--- a/superset-frontend/src/components/MessageToasts/Toast.tsx
+++ b/superset-frontend/src/components/MessageToasts/Toast.tsx
@@ -35,7 +35,7 @@ const ToastContainer = styled.div`
     align-items: flex-start;
     gap: ${theme.sizeUnit * 2}px;
 
-    // Content container for icon and text
+    /* Content container for icon and text */
     .toast__content {
       display: flex;
       align-items: flex-start;
diff --git a/superset-frontend/src/components/ResizableSidebar/index.tsx 
b/superset-frontend/src/components/ResizableSidebar/index.tsx
index 507d8e7c440..64d229fcc8c 100644
--- a/superset-frontend/src/components/ResizableSidebar/index.tsx
+++ b/superset-frontend/src/components/ResizableSidebar/index.tsx
@@ -30,7 +30,7 @@ const ResizableWrapper = styled.div`
   }
 
   .sidebar-resizer {
-    // @z-index-above-sticky-header (100) + 1 = 101
+    /* @z-index-above-sticky-header (100) + 1 = 101 */
     z-index: 101;
   }
 
diff --git 
a/superset-frontend/src/dashboard/components/AddSliceCard/AddSliceCard.tsx 
b/superset-frontend/src/dashboard/components/AddSliceCard/AddSliceCard.tsx
index 93992fbf3a1..5e8239c2123 100644
--- a/superset-frontend/src/dashboard/components/AddSliceCard/AddSliceCard.tsx
+++ b/superset-frontend/src/dashboard/components/AddSliceCard/AddSliceCard.tsx
@@ -218,7 +218,7 @@ const AddSliceCard: FC<{
           color: ${theme.colorText};
 
           &:hover {
-            //background: ${theme.colorFillTertiary};
+            /* background: ${theme.colorFillTertiary}; */
           }
 
           opacity: ${isSelected ? 0.4 : 'unset'};
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index cd7ebc414f0..e569d4099f7 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -124,7 +124,7 @@ const StyledContent = styled.div<{
 }>`
   grid-column: 2;
   grid-row: 2;
-  // @z-index-above-dashboard-header (100) + 1 = 101
+  /* @z-index-above-dashboard-header (100) + 1 = 101 */
   ${({ fullSizeChartId }) => fullSizeChartId && `z-index: 101;`}
 `;
 
@@ -327,7 +327,7 @@ const StyledDashboardContent = styled.div<{
       box-sizing: border-box;
       overflow-y: visible;
 
-      // transitionable traits to show filter relevance
+      /* transitionable traits to show filter relevance */
       transition:
         opacity ${theme.motionDurationMid} ease-in-out,
         border-color ${theme.motionDurationMid} ease-in-out,
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.tsx 
b/superset-frontend/src/dashboard/components/DashboardGrid.tsx
index 6cb5f1238ed..58f5eddd51f 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.tsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.tsx
@@ -28,7 +28,11 @@ import type { LayoutItem } from 'src/dashboard/types';
 import type { DropResult } from 
'src/dashboard/components/dnd/dragDroppableConfig';
 import DashboardComponent from '../containers/DashboardComponent';
 import { Droppable } from './dnd/DragDroppable';
-import { GRID_GUTTER_SIZE, GRID_COLUMN_COUNT, BOTTOM_RESIZE_DIRECTION } from 
'../util/constants';
+import {
+  GRID_GUTTER_SIZE,
+  GRID_COLUMN_COUNT,
+  BOTTOM_RESIZE_DIRECTION,
+} from '../util/constants';
 import { TAB_TYPE } from '../util/componentTypes';
 
 export interface DashboardGridProps {
@@ -113,7 +117,7 @@ const GridContent = styled.div<{ editMode?: boolean }>`
 
 const GridColumnGuide = styled.div`
   ${({ theme }) => css`
-    // /* Editing guides */
+    /* Editing guides */
     &.grid-column-guide {
       position: absolute;
       top: 0;
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx 
b/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
index 6366d51f9b4..e3fed7b86c0 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
@@ -24,7 +24,6 @@ export const Pill = styled.div`
     color: ${theme.colorBgBase};
     background: ${theme.colorText};
     border-radius: 1em;
-    vertical-align: text-top;
     padding: ${theme.sizeUnit}px ${theme.sizeUnit * 2}px;
     font-size: ${theme.fontSize}px;
     font-weight: ${theme.fontWeightStrong};
diff --git a/superset-frontend/src/dashboard/components/Header/index.tsx 
b/superset-frontend/src/dashboard/components/Header/index.tsx
index 3eb4d39376b..5a57f778cc4 100644
--- a/superset-frontend/src/dashboard/components/Header/index.tsx
+++ b/superset-frontend/src/dashboard/components/Header/index.tsx
@@ -177,7 +177,7 @@ const actionButtonsStyle = (theme: SupersetTheme) => css`
 `;
 
 const StyledUndoRedoButton = styled(Button)`
-  // TODO: check if we need this
+  /* TODO: check if we need this */
   padding: 0;
   &:hover {
     background: transparent;
diff --git 
a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.tsx
 
b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.tsx
index 9a6942cb38a..683e0b35279 100644
--- 
a/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.tsx
+++ 
b/superset-frontend/src/dashboard/components/filterscope/FilterScopeSelector.tsx
@@ -313,7 +313,7 @@ const ScopeSelector = styled.div`
           display: block;
         }
 
-        // disable style from react-checkbox-trees.css
+        /* disable style from react-checkbox-tree.css */
         .rct-node-clickable:hover,
         .rct-node-clickable:focus,
         label:hover,
diff --git a/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx 
b/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx
index 783b3323a88..58549b4bea2 100644
--- a/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx
+++ b/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx
@@ -32,7 +32,7 @@ const HoverStyleOverrides = styled.div`
   .hover-menu {
     opacity: 0;
     position: absolute;
-    z-index: 11; // one more than DragDroppable
+    z-index: 11; /* one more than DragDroppable */
     font-size: ${({ theme }) => theme.fontSize};
   }
 
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControlShared.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControlShared.tsx
index 68e407c4719..0d03c96cf0f 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControlShared.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControlShared.tsx
@@ -73,8 +73,8 @@ export const HorizontalOverflowFilterControlTitleBox = styled(
 `;
 
 export const AllFilterControlContainer = styled(Form)`
-  // TODO this is a hack related to having form items inside others which is 
not
-  // normal antd-expected usage
+  /* TODO this is a hack related to having form items inside others which is 
not */
+  /* normal antd-expected usage */
   .ant-form-item .ant-form-item {
     margin-bottom: 0 !important;
   }
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
index a835e58fe27..47ecde6fd6b 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
@@ -60,7 +60,7 @@ const BarWrapper = styled.div<{ width: number }>`
     margin: 0;
   }
   &.open {
-    width: ${({ width }) => width}px; // arbitrary...
+    width: ${({ width }) => width}px; /* arbitrary... */
   }
 `;
 
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/RemovedFilter.tsx
 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/RemovedFilter.tsx
index a861d980abc..7e958b68fc4 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/RemovedFilter.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/RemovedFilter.tsx
@@ -24,7 +24,7 @@ import { styled } from '@apache-superset/core/theme';
 const RemovedContent = styled.div`
   display: flex;
   flex-direction: column;
-  height: 400px; // arbitrary
+  height: 400px; /* arbitrary */
   text-align: center;
   justify-content: center;
   align-items: center;
diff --git 
a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx 
b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
index 427c34f67b1..9c8435a69ab 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -152,8 +152,8 @@ const Styles = styled.div`
   display: flex;
   flex-direction: column;
 
-  // Resizable add overflow-y: auto as a style to this div
-  // To override it, we need to use !important
+  /* Resizable adds overflow-y: auto as a style to this div */
+  /* To override it, we need to use !important */
   overflow: visible !important;
 
   #controlSections {
@@ -166,7 +166,7 @@ const Styles = styled.div`
     flex: 1 1 100%;
   }
 
-  // Ensure Ant Design tabs allow content to expand
+  /* Ensure Ant Design tabs allow content to expand */
   .ant-tabs-content {
     overflow: visible;
     height: auto;
@@ -182,7 +182,7 @@ const Styles = styled.div`
     height: auto;
   }
 
-  // Ensure collapse components can expand
+  /* Ensure collapse components can expand */
   .ant-collapse-content {
     overflow: visible;
   }
diff --git 
a/superset-frontend/src/explore/components/DataTableControl/index.tsx 
b/superset-frontend/src/explore/components/DataTableControl/index.tsx
index d7c1e507981..ffaa6eb4d78 100755
--- a/superset-frontend/src/explore/components/DataTableControl/index.tsx
+++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx
@@ -35,7 +35,7 @@ export const CellNull = styled('span')`
 export const CopyButton = styled(Button)`
   font-size: ${({ theme }) => theme.fontSizeSM}px;
 
-  // needed to override button's first-of-type margin: 0
+  /* needed to override button's first-of-type margin: 0 */
   && {
     margin: 0 ${({ theme }) => theme.sizeUnit * 2}px;
   }
diff --git 
a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanelDragOption/index.tsx
 
b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanelDragOption/index.tsx
index 204537c3292..cf8458e453c 100644
--- 
a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanelDragOption/index.tsx
+++ 
b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanelDragOption/index.tsx
@@ -41,7 +41,7 @@ const DatasourceItemContainer = styled.div<{ isDragging?: 
boolean }>`
     height: ${theme.sizeUnit * 6}px;
     padding: 0 ${theme.sizeUnit}px;
 
-    // hack to make the drag preview image corners rounded
+    /* hack to make the drag preview image corners rounded */
     transform: translate(0, 0);
     color: ${theme.colorText};
     background-color: ${theme.colorBgLayout};
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
index 895f8f190c2..e88814129fa 100644
--- 
a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
+++ 
b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeGallery.tsx
@@ -214,7 +214,7 @@ const IconsPane = styled.div`
   justify-content: space-evenly;
   grid-gap: ${({ theme }) => theme.sizeUnit * 2}px;
   justify-items: center;
-  // for some reason this padding doesn't seem to apply at the bottom of the 
container. Why is a mystery.
+  /* for some reason this padding doesn't seem to apply at the bottom of the 
container. Why is a mystery. */
   padding: ${({ theme }) => theme.sizeUnit * 2}px;
 `;
 
diff --git 
a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx 
b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
index a2e92b1dd75..ad11bd73da9 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/index.tsx
@@ -107,7 +107,6 @@ const VizTypeControl = ({
           display: flex;
           justify-content: flex-end;
           margin-top: ${theme.sizeUnit * 2}px;
-          color: ${theme.colorTextSecondary};
           text-decoration: underline;
           font-size: ${theme.fontSizeSM}px;
           color: ${theme.colorTextTertiary};
diff --git 
a/superset-frontend/src/features/databases/UploadDataModel/ColumnsPreview.tsx 
b/superset-frontend/src/features/databases/UploadDataModel/ColumnsPreview.tsx
index b079d39c525..f3ba17e10bb 100644
--- 
a/superset-frontend/src/features/databases/UploadDataModel/ColumnsPreview.tsx
+++ 
b/superset-frontend/src/features/databases/UploadDataModel/ColumnsPreview.tsx
@@ -28,8 +28,8 @@ interface ColumnsPreviewProps {
 }
 
 export const StyledDivContainer = styled.div`
-  //margin-top: 10px;
-  //margin-bottom: 10px;
+  /* margin-top: 10px; */
+  /* margin-bottom: 10px; */
 `;
 
 const ColumnsPreview: FC<ColumnsPreviewProps> = ({
diff --git a/superset-frontend/src/features/home/SavedQueries.tsx 
b/superset-frontend/src/features/home/SavedQueries.tsx
index 147978f1e60..aaa9eb6d602 100644
--- a/superset-frontend/src/features/home/SavedQueries.tsx
+++ b/superset-frontend/src/features/home/SavedQueries.tsx
@@ -88,7 +88,6 @@ export const CardStyles = styled.div`
     display: inline-block;
     width: 100%;
     height: 179px;
-    background-repeat: no-repeat;
     vertical-align: middle;
   }
 `;
diff --git a/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx 
b/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
index 31f4e6e47e4..132a5662979 100644
--- a/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
+++ b/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
@@ -30,7 +30,7 @@ const TimeTableStyles = styled.div<{ height?: number }>`
   overflow: auto;
 
   th {
-    z-index: 11 !important; // to cover sparkline
+    z-index: 11 !important; /* to cover sparkline */
   }
 `;
 
diff --git a/superset-frontend/stylelint.config.js 
b/superset-frontend/stylelint.config.js
new file mode 100644
index 00000000000..38ae5353ac5
--- /dev/null
+++ b/superset-frontend/stylelint.config.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module.exports = {
+  customSyntax: 'postcss-styled-syntax',
+  rules: {
+    'property-no-unknown': true,
+    'unit-no-unknown': true,
+    'function-no-unknown': true,
+    'color-no-invalid-hex': true,
+    'named-grid-areas-no-invalid': true,
+    'string-no-newline': true,
+    'no-invalid-double-slash-comments': true,
+    'declaration-block-no-duplicate-properties': [
+      true,
+      { ignore: ['consecutive-duplicates-with-different-values'] },
+    ],
+  },
+};

Reply via email to