├── .gitattributes ├── .gitignore ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── test.yml │ └── codeql.yml ├── LICENSE ├── README.md ├── eslint.config.mjs ├── package.json └── index.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bootstrap/ 2 | /node_modules/ 3 | /npm-debug.log 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | labels: 8 | - dependencies 9 | - github_actions 10 | groups: 11 | github-actions: 12 | patterns: 13 | - "*" 14 | - package-ecosystem: "npm" 15 | directory: "/" 16 | schedule: 17 | interval: monthly 18 | labels: 19 | - dependencies 20 | versioning-strategy: increase 21 | groups: 22 | production-dependencies: 23 | dependency-type: "production" 24 | development-dependencies: 25 | dependency-type: "development" 26 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | env: 11 | FORCE_COLOR: 2 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | run: 18 | name: Node ${{ matrix.node }} 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | node: [18, 20, 22, 24] 25 | 26 | steps: 27 | - name: Clone repository 28 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 29 | with: 30 | persist-credentials: false 31 | 32 | - name: Clone twbs/bootstrap repo 33 | run: npm run clone-repo 34 | 35 | - name: Set up Node.js 36 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 37 | with: 38 | node-version: ${{ matrix.node }} 39 | cache: npm 40 | 41 | - name: Install npm dependencies 42 | run: npm ci 43 | 44 | - name: Run tests 45 | run: npm test 46 | 47 | - name: Test against the bootstrap repo 48 | run: npm run test-ci 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 The Bootstrap Authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stylelint-config-twbs-bootstrap [![npm version](https://img.shields.io/npm/v/stylelint-config-twbs-bootstrap?logo=npm&logoColor=fff)](https://www.npmjs.com/package/stylelint-config-twbs-bootstrap) [![CI Status](https://img.shields.io/github/actions/workflow/status/twbs/stylelint-config-twbs-bootstrap/test.yml?branch=main&label=CI&logo=github)](https://github.com/twbs/stylelint-config-twbs-bootstrap/actions/workflows/test.yml?query=workflow%3ATests+branch%3Amain) 2 | 3 | > An opinionated Stylelint config used by Bootstrap across our projects. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install stylelint-config-twbs-bootstrap --save-dev 9 | # Or with yarn: 10 | yarn add stylelint-config-twbs-bootstrap --dev 11 | ``` 12 | 13 | ## Usage 14 | 15 | We provide a single config that covers both CSS and SCSS. It will automatically apply SCSS rules to files ending in `.scss`. 16 | 17 | You simply have to extend this config in your Stylelint config: 18 | 19 | ```json 20 | { 21 | "extends": "stylelint-config-twbs-bootstrap" 22 | } 23 | ``` 24 | 25 | To see the included rules, please check [index.js](index.js). 26 | 27 | ## License 28 | 29 | Released under the [MIT License](LICENSE). 30 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - "!dependabot/**" 8 | pull_request: 9 | branches: 10 | - main 11 | - "!dependabot/**" 12 | schedule: 13 | - cron: "0 0 * * 0" 14 | workflow_dispatch: 15 | 16 | jobs: 17 | analyze: 18 | name: Analyze 19 | runs-on: ubuntu-latest 20 | permissions: 21 | actions: read 22 | contents: read 23 | security-events: write 24 | 25 | steps: 26 | - name: Clone repository 27 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 28 | with: 29 | persist-credentials: false 30 | 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 33 | with: 34 | languages: "javascript" 35 | queries: +security-and-quality 36 | 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 39 | 40 | - name: Perform CodeQL Analysis 41 | uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 42 | with: 43 | category: "/language:javascript" 44 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | 4 | /** @type {import('eslint').Linter.RulesRecord} */ 5 | const RULES = { 6 | 'comma-dangle': [ 7 | 'error', 8 | 'never' 9 | ], 10 | 'quotes': [ 11 | 'error', 12 | 'single' 13 | ], 14 | 'quote-props': [ 15 | 'error', 16 | 'always' 17 | ] 18 | }; 19 | 20 | /** @type {import('eslint').Linter.FlatConfig[]} */ 21 | export default [ 22 | // global ignores 23 | { 24 | 'ignores': [ 25 | 'bootstrap', 26 | 'node_modules/**' 27 | ] 28 | }, 29 | { 30 | 'languageOptions': { 31 | 'ecmaVersion': 2022, 32 | 'sourceType': 'script', 33 | 'globals': { 34 | ...globals.node 35 | } 36 | }, 37 | 'linterOptions': { 38 | 'reportUnusedDisableDirectives': 'error' 39 | } 40 | }, 41 | js.configs.recommended, 42 | { 43 | 'files': [ 44 | '**/*.js' 45 | ], 46 | 'rules': { 47 | ...RULES 48 | } 49 | }, 50 | { 51 | 'files': [ 52 | '**/*.mjs' 53 | ], 54 | 'languageOptions': { 55 | 'sourceType': 'module', 56 | 'globals': { 57 | ...globals.nodeBuiltin 58 | } 59 | }, 60 | 'rules': { 61 | ...RULES 62 | } 63 | } 64 | ] 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stylelint-config-twbs-bootstrap", 3 | "version": "16.1.0", 4 | "description": "An opinionated Stylelint config used by Bootstrap across our projects.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/twbs/stylelint-config-twbs-bootstrap.git" 9 | }, 10 | "keywords": [ 11 | "bootstrap", 12 | "stylelint", 13 | "stylelint-config", 14 | "config", 15 | "lint" 16 | ], 17 | "author": "The Bootstrap Authors (https://github.com/twbs/stylelint-config-twbs-bootstrap/graphs/contributors)", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/twbs/stylelint-config-twbs-bootstrap/issues" 21 | }, 22 | "homepage": "https://github.com/twbs/stylelint-config-twbs-bootstrap#readme", 23 | "funding": [ 24 | { 25 | "type": "github", 26 | "url": "https://github.com/sponsors/twbs" 27 | }, 28 | { 29 | "type": "opencollective", 30 | "url": "https://opencollective.com/bootstrap" 31 | } 32 | ], 33 | "files": [ 34 | "index.js" 35 | ], 36 | "engines": { 37 | "node": ">=18.12.0" 38 | }, 39 | "peerDependencies": { 40 | "stylelint": "^16.22.0" 41 | }, 42 | "dependencies": { 43 | "@stylistic/stylelint-config": "^3.0.1", 44 | "@stylistic/stylelint-plugin": "^4.0.0", 45 | "stylelint-config-recess-order": "^5.1.1", 46 | "stylelint-config-standard": "^36.0.1", 47 | "stylelint-config-standard-scss": "^14.0.0", 48 | "stylelint-scss": "^6.12.1" 49 | }, 50 | "devDependencies": { 51 | "@eslint/js": "^9.39.1", 52 | "eslint": "^9.29.0", 53 | "globals": "^14.0.0", 54 | "stylelint": "^16.26.1" 55 | }, 56 | "scripts": { 57 | "lint": "eslint", 58 | "clone-repo": "git clone https://github.com/twbs/bootstrap.git --branch main --depth 1 bootstrap", 59 | "test": "npm run lint", 60 | "test-ci": "stylelint \"bootstrap/scss/**/*.scss\" --config index.js --ignore-pattern \"**/vendor/\" --ignore-pattern \"**/tests/\" --formatter verbose" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 'extends': [ 5 | 'stylelint-config-standard', 6 | 'stylelint-config-recess-order', 7 | '@stylistic/stylelint-config' 8 | ], 9 | 'rules': { 10 | 'alpha-value-notation': null, 11 | 'at-rule-empty-line-before': null, 12 | 'color-function-notation': null, 13 | 'color-named': 'never', 14 | 'custom-property-empty-line-before': null, 15 | 'custom-property-pattern': null, 16 | 'declaration-block-no-redundant-longhand-properties': null, 17 | 'declaration-empty-line-before': null, 18 | 'declaration-no-important': true, 19 | 'font-weight-notation': [ 20 | 'numeric', 21 | { 22 | 'ignore': [ 23 | 'relative' 24 | ] 25 | } 26 | ], 27 | 'function-url-no-scheme-relative': true, 28 | 'media-feature-range-notation': null, 29 | 'media-query-no-invalid': null, 30 | 'no-descending-specificity': null, 31 | 'number-max-precision': null, 32 | 'rule-empty-line-before': null, 33 | 'selector-max-attribute': 2, 34 | 'selector-max-class': 4, 35 | 'selector-max-combinators': 4, 36 | 'selector-max-compound-selectors': 4, 37 | 'selector-max-id': 0, 38 | 'selector-max-specificity': null, 39 | 'selector-max-type': 2, 40 | 'selector-max-universal': 1, 41 | 'selector-no-qualifying-type': true, 42 | 'selector-not-notation': null, // TODO enable this later 43 | '@stylistic/at-rule-name-space-after': 'always', 44 | '@stylistic/at-rule-semicolon-space-before': 'never', 45 | '@stylistic/block-closing-brace-empty-line-before': null, 46 | '@stylistic/block-closing-brace-newline-after': null, 47 | '@stylistic/block-opening-brace-space-before': null, 48 | '@stylistic/declaration-block-semicolon-newline-before': 'never-multi-line', 49 | '@stylistic/max-empty-lines': 2, 50 | '@stylistic/max-line-length': null, 51 | '@stylistic/number-leading-zero': 'never', 52 | '@stylistic/selector-list-comma-newline-before': 'never-multi-line', 53 | '@stylistic/selector-list-comma-space-after': 'always-single-line', 54 | '@stylistic/selector-list-comma-space-before': 'never-single-line', 55 | '@stylistic/unicode-bom': 'never', 56 | '@stylistic/value-list-comma-newline-after': 'never-multi-line', 57 | '@stylistic/value-list-comma-newline-before': 'never-multi-line', 58 | '@stylistic/value-list-comma-space-after': 'always' 59 | }, 60 | 'overrides': [ 61 | { 62 | 'files': '**/*.scss', 63 | 'extends': [ 64 | 'stylelint-config-standard-scss', 65 | 'stylelint-config-recess-order' 66 | ], 67 | 'rules': { 68 | 'no-invalid-position-at-import-rule': null, 69 | 'scss/at-extend-no-missing-placeholder': null, 70 | 'scss/at-function-named-arguments': 'never', 71 | 'scss/at-if-closing-brace-newline-after': null, 72 | 'scss/at-if-closing-brace-space-after': null, 73 | 'scss/at-if-no-null': null, 74 | 'scss/at-mixin-argumentless-call-parentheses': 'always', 75 | 'scss/at-mixin-pattern': null, 76 | 'scss/at-rule-conditional-no-parentheses': null, 77 | 'scss/comment-no-empty': null, 78 | 'scss/dimension-no-non-numeric-values': true, 79 | 'scss/dollar-variable-colon-space-after': 'at-least-one-space', 80 | 'scss/dollar-variable-empty-line-before': null, 81 | 'scss/double-slash-comment-empty-line-before': null, 82 | 'scss/double-slash-comment-whitespace-inside': null, 83 | 'scss/function-quote-no-quoted-strings-inside': null, 84 | 'scss/media-feature-value-dollar-variable': null, 85 | // Disable since node-sass is still supported as a builder and it doesn't support the @use syntax 86 | 'scss/no-global-function-names': null, 87 | 'scss/selector-no-redundant-nesting-selector': true 88 | } 89 | } 90 | ] 91 | }; 92 | --------------------------------------------------------------------------------