diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 6f680664ca67..359e9ed23efc 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -2,35 +2,35 @@ "files": [ { "path": "./dist/css/bootstrap-grid.css", - "maxSize": "6.5 kB" + "maxSize": "9.00 kB" }, { "path": "./dist/css/bootstrap-grid.min.css", - "maxSize": "6.0 kB" + "maxSize": "8.25 kB" }, { "path": "./dist/css/bootstrap-reboot.css", - "maxSize": "3.5 kB" + "maxSize": "5.0 kB" }, { "path": "./dist/css/bootstrap-reboot.min.css", - "maxSize": "3.25 kB" + "maxSize": "4.5 kB" }, { "path": "./dist/css/bootstrap-utilities.css", - "maxSize": "11.75 kB" + "maxSize": "13.5 kB" }, { "path": "./dist/css/bootstrap-utilities.min.css", - "maxSize": "10.75 kB" + "maxSize": "12.0 kB" }, { "path": "./dist/css/bootstrap.css", - "maxSize": "32.5 kB" + "maxSize": "36.5 kB" }, { "path": "./dist/css/bootstrap.min.css", - "maxSize": "30.25 kB" + "maxSize": "33.0 kB" }, { "path": "./dist/js/bootstrap.bundle.js", diff --git a/.cspell.json b/.cspell.json index d2434c30a608..872c34b23c39 100644 --- a/.cspell.json +++ b/.cspell.json @@ -21,6 +21,7 @@ "callout", "callouts", "camelCase", + "checkgroup", "clearfix", "Codesniffer", "combinator", @@ -29,6 +30,7 @@ "Crossfade", "crossfading", "cssgrid", + "csstricks", "Csvg", "Datalists", "Deque", diff --git a/.github/workflows/build-css.yml b/.github/workflows/build-css.yml new file mode 100644 index 000000000000..23304db08390 --- /dev/null +++ b/.github/workflows/build-css.yml @@ -0,0 +1,46 @@ +name: Build CSS + +on: + pull_request: + paths: + - 'scss/**' + - 'build/**' + - 'package.json' + - 'package-lock.json' + workflow_dispatch: + +env: + FORCE_COLOR: 2 + NODE: 22 + +permissions: + contents: read + +jobs: + build-css: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Upload CSS artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: css-dist + path: dist/css/ + retention-days: 1 diff --git a/.github/workflows/build-js.yml b/.github/workflows/build-js.yml new file mode 100644 index 000000000000..fd3fc8f13fa9 --- /dev/null +++ b/.github/workflows/build-js.yml @@ -0,0 +1,46 @@ +name: Build JS + +on: + pull_request: + paths: + - 'js/**' + - 'build/**' + - 'package.json' + - 'package-lock.json' + workflow_dispatch: + +env: + FORCE_COLOR: 2 + NODE: 22 + +permissions: + contents: read + +jobs: + build-js: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build JS + run: npm run js + + - name: Upload JS artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: js-dist + path: dist/js/ + retention-days: 1 diff --git a/.github/workflows/bundlewatch.yml b/.github/workflows/bundlewatch.yml index 9a1d81679e89..d2c3df265394 100644 --- a/.github/workflows/bundlewatch.yml +++ b/.github/workflows/bundlewatch.yml @@ -15,7 +15,48 @@ permissions: contents: read jobs: + build-dist: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Build JS + run: npm run js + + - name: Upload CSS artifacts + id: css-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: css-dist + path: dist/css/ + retention-days: 1 + + - name: Upload JS artifacts + id: js-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: js-dist + path: dist/js/ + retention-days: 1 + bundlewatch: + needs: build-dist runs-on: ubuntu-latest steps: @@ -33,8 +74,17 @@ jobs: - name: Install npm dependencies run: npm ci - - name: Run dist - run: npm run dist + - name: Download CSS artifacts + uses: actions/download-artifact@v4 + with: + name: css-dist + path: dist/css/ + + - name: Download JS artifacts + uses: actions/download-artifact@v4 + with: + name: js-dist + path: dist/js/ - name: Run bundlewatch run: npm run bundlewatch diff --git a/.github/workflows/cdn.yml b/.github/workflows/cdn.yml new file mode 100644 index 000000000000..06941a71b4db --- /dev/null +++ b/.github/workflows/cdn.yml @@ -0,0 +1,51 @@ +name: CDN Distribution + +on: + release: + types: [published] + workflow_dispatch: + +env: + FORCE_COLOR: 2 + NODE: 22 + +permissions: + contents: read + +jobs: + build-cdn: + runs-on: ubuntu-latest + if: ${{ github.repository == 'twbs/bootstrap' && startsWith(github.event.release.tag_name, 'v') }} + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Build JS + run: npm run js + + - name: Generate SRI hashes + run: npm run release-sri + + - name: Upload to CDN + # This would typically upload to a CDN service like jsDelivr, unpkg, etc. + # The actual implementation would depend on the CDN service used + run: | + echo "CDN upload would happen here" + echo "Files ready for CDN distribution:" + ls -la dist/css/ + ls -la dist/js/ diff --git a/.github/workflows/css.yml b/.github/workflows/css.yml index 8fd608c430e9..5aa89a29e85f 100644 --- a/.github/workflows/css.yml +++ b/.github/workflows/css.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - css: + build-css: runs-on: ubuntu-latest steps: @@ -36,5 +36,38 @@ jobs: - name: Build CSS run: npm run css + - name: Upload CSS artifacts + id: css-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: css-dist + path: dist/css/ + retention-days: 1 + + css: + needs: build-css + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Download CSS artifacts + uses: actions/download-artifact@v4 + with: + name: css-dist + path: dist/css/ + - name: Run CSS tests run: npm run css-test diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 59abb9111ff5..199943506d65 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - docs: + build-dist: runs-on: ubuntu-latest steps: @@ -30,16 +30,67 @@ jobs: node-version: "${{ env.NODE }}" cache: npm - - run: java -version + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Build JS + run: npm run js + + - name: Upload CSS artifacts + id: css-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: css-dist + path: dist/css/ + retention-days: 1 + + - name: Upload JS artifacts + id: js-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: js-dist + path: dist/js/ + retention-days: 1 + + docs: + needs: build-dist + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm - name: Install npm dependencies run: npm ci + - name: Download CSS artifacts + uses: actions/download-artifact@v4 + with: + name: css-dist + path: dist/css/ + + - name: Download JS artifacts + uses: actions/download-artifact@v4 + with: + name: js-dist + path: dist/js/ + - name: Build docs run: npm run docs-build - name: Validate HTML - run: npm run docs-vnu + run: npm run docs-html-validate - name: Run linkinator uses: JustinBeckwith/linkinator-action@3d5ba091319fa7b0ac14703761eebb7d100e6f6d # v1.11.0 diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 5606ecf7fd0b..460a09a43b12 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -15,7 +15,37 @@ permissions: contents: read jobs: + build-js: + runs-on: ubuntu-latest + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: ${{ env.NODE }} + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build JS + run: npm run js + + - name: Upload JS artifacts + id: js-artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: js-dist + path: dist/js/ + retention-days: 1 + run: + needs: build-js permissions: # allow coverallsapp/github-action to create new checks issues and fetch code checks: write @@ -38,8 +68,11 @@ jobs: - name: Install npm dependencies run: npm ci - - name: Run dist - run: npm run js + - name: Download JS artifacts + uses: actions/download-artifact@v4 + with: + name: js-dist + path: dist/js/ - name: Run JS tests run: npm run js-test diff --git a/.github/workflows/node-sass.yml b/.github/workflows/node-sass.yml deleted file mode 100644 index daa6c6be979e..000000000000 --- a/.github/workflows/node-sass.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: CSS (node-sass) - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -env: - FORCE_COLOR: 2 - NODE: 22 - -permissions: - contents: read - -jobs: - css: - runs-on: ubuntu-latest - - steps: - - name: Clone repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - persist-credentials: false - - - name: Set up Node.js - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 - with: - node-version: "${{ env.NODE }}" - - - name: Build CSS with node-sass - run: | - npx --package node-sass@latest node-sass --version - npx --package node-sass@latest node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/ -o dist-sass/css/ - ls -Al dist-sass/css - - - name: Check built CSS files for Sass variables - shell: bash - run: | - SASS_VARS_FOUND=$(find "dist-sass/css/" -type f -name "*.css" -print0 | xargs -0 --no-run-if-empty grep -F "\$" || true) - if [[ -z "$SASS_VARS_FOUND" ]]; then - echo "All good, no Sass variables found!" - exit 0 - else - echo "Found $(echo "$SASS_VARS_FOUND" | wc -l | bc) Sass variables:" - echo "$SASS_VARS_FOUND" - exit 1 - fi diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index b78023b92aa5..ad5c554e59cf 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -13,11 +13,28 @@ jobs: if: ${{ github.repository == 'twbs/bootstrap' && startsWith(github.event.release.tag_name, 'v') }} env: GITHUB_REF_NAME: ${{ github.ref_name }} + FORCE_COLOR: 2 + NODE: 22 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Build JS + run: npm run js + - name: Set up NuGet uses: nuget/setup-nuget@323ab0502cd38fdc493335025a96c8fdb0edc71f # v2.0.1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000000..07b9655f7103 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: Release + +on: + release: + types: [published] + workflow_dispatch: + +env: + FORCE_COLOR: 2 + NODE: 22 + +permissions: + contents: write + +jobs: + build-and-release: + runs-on: ubuntu-latest + if: ${{ github.repository == 'twbs/bootstrap' && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') }} + + steps: + - name: Clone repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + with: + node-version: "${{ env.NODE }}" + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Build CSS + run: npm run css + + - name: Build JS + run: npm run js + + - name: Generate SRI hashes + run: npm run release-sri + + - name: Build docs + run: npm run docs-build + + - name: Create release zip + run: npm run release-zip + + - name: Create examples zip + run: npm run release-zip-examples + + - name: Upload dist files to release + uses: actions/upload-release-asset@v1 + if: ${{ github.event_name == 'release' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./bootstrap-${{ github.event.release.tag_name }}-dist.zip + asset_name: bootstrap-${{ github.event.release.tag_name }}-dist.zip + asset_content_type: application/zip + + - name: Upload examples to release + uses: actions/upload-release-asset@v1 + if: ${{ github.event_name == 'release' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./bootstrap-${{ github.event.release.tag_name }}-examples.zip + asset_name: bootstrap-${{ github.event.release.tag_name }}-examples.zip + asset_content_type: application/zip + + - name: Commit dist files for npm + if: ${{ github.event_name == 'release' }} + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add dist/ + git commit -m "Add dist files for release ${{ github.event.release.tag_name }}" || exit 0 + git push + + - name: Publish to npm + if: ${{ github.event_name == 'release' }} + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 235ad54948dd..6d2c89f96632 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ Thumbs.db *.komodoproject # Folders to ignore +/dist/ /dist-sass/ /js/coverage/ /node_modules/ diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000000..6b09841cb070 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,18 @@ +{ + "default": true, + "MD004": { "style": "dash" }, + "MD011": false, + "MD013": false, + "MD024": false, + "MD025": false, + "MD026": false, + "MD031": false, + "MD033": false, + "MD034": false, + "MD037": false, + "MD038": false, + "MD041": false, + "MD046": false, + "line-length": false, + "no-inline-html": false +} diff --git a/.stylelintrc.json b/.stylelintrc.json index 589884aae7ab..045dbeff4e70 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -13,7 +13,6 @@ "outline": "none" }, "function-disallowed-list": [ - "calc", "lighten", "darken" ], diff --git a/build/html-validate.mjs b/build/html-validate.mjs new file mode 100644 index 000000000000..09b9ad66f8de --- /dev/null +++ b/build/html-validate.mjs @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +/*! + * Script to run html-validate for HTML validation. + * + * This replaces the Java-based vnu-jar validator with a faster, Node.js-only solution. + * Benefits: + * - No Java dependency required + * - Faster execution (no JVM startup time) + * - Easy to configure with rule-based system + * - Better integration with Node.js build tools + * + * Copyright 2017-2025 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ + +import { HtmlValidate } from 'html-validate' +import { globby } from 'globby' + +const htmlValidate = new HtmlValidate({ + rules: { + // Allow autocomplete on buttons (Bootstrap specific) + 'attribute-allowed-values': 'off', + // Allow aria-disabled on links (Bootstrap specific) + 'aria-label-misuse': 'off', + // Allow modern CSS syntax + 'valid-id': 'off', + // Allow void elements with trailing slashes (Astro) + 'void-style': 'off', + // Allow custom attributes + 'no-unknown-elements': 'off', + 'attribute-boolean-style': 'off', + 'no-inline-style': 'off', + // KEEP duplicate ID checking enabled (this is important for HTML validity) + 'no-dup-id': 'error' + }, + elements: [ + 'html5', + { + // Allow custom attributes for Astro/framework compatibility + '*': { + attributes: { + 'is:raw': { boolean: true }, + switch: { boolean: true }, + autocomplete: { enum: ['on', 'off', 'new-password', 'current-password'] } + } + } + } + ] +}) + +async function validateHTML() { + try { + console.log('Running html-validate validation...') + + // Find all HTML files + const files = await globby([ + '_site/**/*.html', + 'js/tests/**/*.html' + ], { + ignore: ['**/node_modules/**'] + }) + + console.log(`Validating ${files.length} HTML files...`) + + let hasErrors = false + + // Validate all files in parallel to avoid await-in-loop + const validationPromises = files.map(file => + htmlValidate.validateFile(file).then(report => ({ file, report })) + ) + + const validationResults = await Promise.all(validationPromises) + + // Process results and check for errors + for (const { file, report } of validationResults) { + if (!report.valid) { + hasErrors = true + console.error(`\nErrors in ${file}:`) + + // Extract error messages with reduced nesting + const errorMessages = report.results.flatMap(result => + result.messages.filter(message => message.severity === 2) + ) + + for (const message of errorMessages) { + console.error(` Line ${message.line}:${message.column} - ${message.message} (${message.ruleId})`) + } + } + } + + if (hasErrors) { + console.error('\nHTML validation failed!') + process.exit(1) + } else { + console.log('✓ All HTML files are valid!') + } + } catch (error) { + console.error('HTML validation error:', error) + process.exit(1) + } +} + +validateHTML() diff --git a/build/vnu-jar.mjs b/build/vnu-jar.mjs deleted file mode 100644 index 4eedb1bedc42..000000000000 --- a/build/vnu-jar.mjs +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env node - -/*! - * Script to run vnu-jar if Java is available. - * Copyright 2017-2025 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ - -import { execFile, spawn } from 'node:child_process' -import vnu from 'vnu-jar' - -execFile('java', ['-version'], (error, stdout, stderr) => { - if (error) { - console.error('Skipping vnu-jar test; Java is probably missing.') - console.error(error) - return - } - - console.log('Running vnu-jar validation...') - - const is32bitJava = !/64-Bit/.test(stderr) - - // vnu-jar accepts multiple ignores joined with a `|`. - // Also note that the ignores are string regular expressions. - const ignores = [ - // "autocomplete" is included in ' - - const btnEl = fixtureEl.querySelector('.btn') - const button = new Button(btnEl) - - const spy = spyOn(button, 'toggle') - - jQueryMock.fn.button = Button.jQueryInterface - jQueryMock.elements = [btnEl] - - jQueryMock.fn.button.call(jQueryMock, 'toggle') - - expect(spy).toHaveBeenCalled() - }) - - it('should create new button instance and call toggle', () => { - fixtureEl.innerHTML = '' - - const btnEl = fixtureEl.querySelector('.btn') - - jQueryMock.fn.button = Button.jQueryInterface - jQueryMock.elements = [btnEl] - - jQueryMock.fn.button.call(jQueryMock, 'toggle') - - expect(Button.getInstance(btnEl)).not.toBeNull() - expect(btnEl).toHaveClass('active') - }) - - it('should just create a button instance without calling toggle', () => { - fixtureEl.innerHTML = '' - - const btnEl = fixtureEl.querySelector('.btn') - - jQueryMock.fn.button = Button.jQueryInterface - jQueryMock.elements = [btnEl] - - jQueryMock.fn.button.call(jQueryMock) - - expect(Button.getInstance(btnEl)).not.toBeNull() - expect(btnEl).not.toHaveClass('active') - }) - }) - describe('getInstance', () => { it('should return button instance', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js index 2960eb5ce59f..3204fa66e191 100644 --- a/js/tests/unit/carousel.spec.js +++ b/js/tests/unit/carousel.spec.js @@ -3,7 +3,7 @@ import EventHandler from '../../src/dom/event-handler.js' import { isRTL, noop } from '../../src/util/index.js' import Swipe from '../../src/util/swipe.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Carousel', () => { @@ -1379,66 +1379,6 @@ describe('Carousel', () => { }) }) - describe('jQueryInterface', () => { - it('should create a carousel', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.carousel = Carousel.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.carousel.call(jQueryMock) - - expect(Carousel.getInstance(div)).not.toBeNull() - }) - - it('should not re create a carousel', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const carousel = new Carousel(div) - - jQueryMock.fn.carousel = Carousel.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.carousel.call(jQueryMock) - - expect(Carousel.getInstance(div)).toEqual(carousel) - }) - - it('should call to if the config is a number', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const carousel = new Carousel(div) - const slideTo = 2 - - const spy = spyOn(carousel, 'to') - - jQueryMock.fn.carousel = Carousel.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.carousel.call(jQueryMock, slideTo) - - expect(spy).toHaveBeenCalledWith(slideTo) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.carousel = Carousel.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.carousel.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('data-api', () => { it('should init carousels with data-bs-ride="carousel" on load', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js index 58c5367526b9..4662fc7b48de 100644 --- a/js/tests/unit/collapse.spec.js +++ b/js/tests/unit/collapse.spec.js @@ -1,6 +1,6 @@ import Collapse from '../../src/collapse.js' import EventHandler from '../../src/dom/event-handler.js' -import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js' +import { clearFixture, getFixture } from '../helpers/fixture.js' describe('Collapse', () => { let fixtureEl @@ -43,30 +43,7 @@ describe('Collapse', () => { expect(collapseByElement._element).toEqual(collapseEl) }) - it('should allow jquery object in parent config', () => { - fixtureEl.innerHTML = [ - '
', - '
', - ' Toggle item', - '
Lorem ipsum
', - '
', - '
' - ].join('') - - const collapseEl = fixtureEl.querySelector('div.collapse') - const myCollapseEl = fixtureEl.querySelector('.my-collapse') - const fakejQueryObject = { - 0: myCollapseEl, - jquery: 'foo' - } - const collapse = new Collapse(collapseEl, { - parent: fakejQueryObject - }) - - expect(collapse._config.parent).toEqual(myCollapseEl) - }) - - it('should allow non jquery object in parent config', () => { + it('should allow object in parent config', () => { fixtureEl.innerHTML = [ '
', '
', @@ -943,49 +920,6 @@ describe('Collapse', () => { }) }) - describe('jQueryInterface', () => { - it('should create a collapse', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.collapse = Collapse.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.collapse.call(jQueryMock) - - expect(Collapse.getInstance(div)).not.toBeNull() - }) - - it('should not re create a collapse', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const collapse = new Collapse(div) - - jQueryMock.fn.collapse = Collapse.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.collapse.call(jQueryMock) - - expect(Collapse.getInstance(div)).toEqual(collapse) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.collapse = Collapse.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.collapse.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('getInstance', () => { it('should return collapse instance', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index 63ae4bd102bc..3f8698c2f94f 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -2,7 +2,7 @@ import EventHandler from '../../src/dom/event-handler.js' import Dropdown from '../../src/dropdown.js' import { noop } from '../../src/util/index.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Dropdown', () => { @@ -508,32 +508,6 @@ describe('Dropdown', () => { }) }) - it('should toggle a dropdown with a jquery object reference', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') - const dropdown = new Dropdown(btnDropdown, { - reference: { 0: fixtureEl, jquery: 'jQuery' } - }) - - btnDropdown.addEventListener('shown.bs.dropdown', () => { - expect(btnDropdown).toHaveClass('show') - expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true') - resolve() - }) - - dropdown.toggle() - }) - }) - it('should toggle a dropdown with a valid virtual element reference', () => { return new Promise(resolve => { fixtureEl.innerHTML = [ @@ -2218,49 +2192,6 @@ describe('Dropdown', () => { }) }) - describe('jQueryInterface', () => { - it('should create a dropdown', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.dropdown = Dropdown.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.dropdown.call(jQueryMock) - - expect(Dropdown.getInstance(div)).not.toBeNull() - }) - - it('should not re create a dropdown', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const dropdown = new Dropdown(div) - - jQueryMock.fn.dropdown = Dropdown.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.dropdown.call(jQueryMock) - - expect(Dropdown.getInstance(div)).toEqual(dropdown) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.dropdown = Dropdown.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.dropdown.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('getInstance', () => { it('should return dropdown instance', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/jquery.spec.js b/js/tests/unit/jquery.spec.js deleted file mode 100644 index 7d7f29dc7288..000000000000 --- a/js/tests/unit/jquery.spec.js +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-env jquery */ - -import Alert from '../../src/alert.js' -import Button from '../../src/button.js' -import Carousel from '../../src/carousel.js' -import Collapse from '../../src/collapse.js' -import Dropdown from '../../src/dropdown.js' -import Modal from '../../src/modal.js' -import Offcanvas from '../../src/offcanvas.js' -import Popover from '../../src/popover.js' -import ScrollSpy from '../../src/scrollspy.js' -import Tab from '../../src/tab.js' -import Toast from '../../src/toast.js' -import Tooltip from '../../src/tooltip.js' -import { clearFixture, getFixture } from '../helpers/fixture.js' - -describe('jQuery', () => { - let fixtureEl - - beforeAll(() => { - fixtureEl = getFixture() - }) - - afterEach(() => { - clearFixture() - }) - - it('should add all plugins in jQuery', () => { - expect(Alert.jQueryInterface).toEqual(jQuery.fn.alert) - expect(Button.jQueryInterface).toEqual(jQuery.fn.button) - expect(Carousel.jQueryInterface).toEqual(jQuery.fn.carousel) - expect(Collapse.jQueryInterface).toEqual(jQuery.fn.collapse) - expect(Dropdown.jQueryInterface).toEqual(jQuery.fn.dropdown) - expect(Modal.jQueryInterface).toEqual(jQuery.fn.modal) - expect(Offcanvas.jQueryInterface).toEqual(jQuery.fn.offcanvas) - expect(Popover.jQueryInterface).toEqual(jQuery.fn.popover) - expect(ScrollSpy.jQueryInterface).toEqual(jQuery.fn.scrollspy) - expect(Tab.jQueryInterface).toEqual(jQuery.fn.tab) - expect(Toast.jQueryInterface).toEqual(jQuery.fn.toast) - expect(Tooltip.jQueryInterface).toEqual(jQuery.fn.tooltip) - }) - - it('should use jQuery event system', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '
', - ' ', - '
' - ].join('') - - $(fixtureEl).find('.alert') - .one('closed.bs.alert', () => { - expect($(fixtureEl).find('.alert')).toHaveSize(0) - resolve() - }) - - $(fixtureEl).find('button').trigger('click') - }) - }) -}) diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js index 2aa0b7655c14..148052120616 100644 --- a/js/tests/unit/modal.spec.js +++ b/js/tests/unit/modal.spec.js @@ -2,7 +2,7 @@ import EventHandler from '../../src/dom/event-handler.js' import Modal from '../../src/modal.js' import ScrollBarHelper from '../../src/util/scrollbar.js' import { - clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock + clearBodyAndDocument, clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Modal', () => { @@ -1162,96 +1162,6 @@ describe('Modal', () => { }) }) }) - describe('jQueryInterface', () => { - it('should create a modal', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.modal.call(jQueryMock) - - expect(Modal.getInstance(div)).not.toBeNull() - }) - - it('should create a modal with given config', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.modal.call(jQueryMock, { keyboard: false }) - const spy = spyOn(Modal.prototype, 'constructor') - expect(spy).not.toHaveBeenCalledWith(div, { keyboard: false }) - - const modal = Modal.getInstance(div) - expect(modal).not.toBeNull() - expect(modal._config.keyboard).toBeFalse() - }) - - it('should not re create a modal', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - const modal = new Modal(div) - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.modal.call(jQueryMock) - - expect(Modal.getInstance(div)).toEqual(modal) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.modal.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should call show method', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - const modal = new Modal(div) - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - const spy = spyOn(modal, 'show') - - jQueryMock.fn.modal.call(jQueryMock, 'show') - - expect(spy).toHaveBeenCalled() - }) - - it('should not call show method', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.modal = Modal.jQueryInterface - jQueryMock.elements = [div] - - const spy = spyOn(Modal.prototype, 'show') - - jQueryMock.fn.modal.call(jQueryMock) - - expect(spy).not.toHaveBeenCalled() - }) - }) describe('getInstance', () => { it('should return modal instance', () => { diff --git a/js/tests/unit/offcanvas.spec.js b/js/tests/unit/offcanvas.spec.js index 3b6c98c1004c..c93117b654a8 100644 --- a/js/tests/unit/offcanvas.spec.js +++ b/js/tests/unit/offcanvas.spec.js @@ -3,7 +3,7 @@ import Offcanvas from '../../src/offcanvas.js' import { isVisible } from '../../src/util/index.js' import ScrollBarHelper from '../../src/util/scrollbar.js' import { - clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock + clearBodyAndDocument, clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Offcanvas', () => { @@ -738,106 +738,6 @@ describe('Offcanvas', () => { }) }) - describe('jQueryInterface', () => { - it('should create an offcanvas', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.offcanvas.call(jQueryMock) - - expect(Offcanvas.getInstance(div)).not.toBeNull() - }) - - it('should not re create an offcanvas', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const offCanvas = new Offcanvas(div) - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.offcanvas.call(jQueryMock) - - expect(Offcanvas.getInstance(div)).toEqual(offCanvas) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.offcanvas.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should throw error on protected method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = '_getConfig' - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.offcanvas.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should throw error if method "constructor" is being called', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'constructor' - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.offcanvas.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should call offcanvas method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - const spy = spyOn(Offcanvas.prototype, 'show') - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.offcanvas.call(jQueryMock, 'show') - expect(spy).toHaveBeenCalled() - }) - - it('should create a offcanvas with given config', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.offcanvas.call(jQueryMock, { scroll: true }) - - const offcanvas = Offcanvas.getInstance(div) - expect(offcanvas).not.toBeNull() - expect(offcanvas._config.scroll).toBeTrue() - }) - }) - describe('getInstance', () => { it('should return offcanvas instance', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/popover.spec.js b/js/tests/unit/popover.spec.js index 1338821bc86d..717fd4f51ae7 100644 --- a/js/tests/unit/popover.spec.js +++ b/js/tests/unit/popover.spec.js @@ -1,7 +1,7 @@ import EventHandler from '../../src/dom/event-handler.js' import Popover from '../../src/popover.js' import { - clearFixture, getFixture, jQueryMock, createEvent + clearFixture, getFixture, createEvent } from '../helpers/fixture.js' describe('Popover', () => { @@ -361,80 +361,6 @@ describe('Popover', () => { }) }) - describe('jQueryInterface', () => { - it('should create a popover', () => { - fixtureEl.innerHTML = 'BS X' - - const popoverEl = fixtureEl.querySelector('a') - - jQueryMock.fn.popover = Popover.jQueryInterface - jQueryMock.elements = [popoverEl] - - jQueryMock.fn.popover.call(jQueryMock) - - expect(Popover.getInstance(popoverEl)).not.toBeNull() - }) - - it('should create a popover with a config object', () => { - fixtureEl.innerHTML = 'BS X' - - const popoverEl = fixtureEl.querySelector('a') - - jQueryMock.fn.popover = Popover.jQueryInterface - jQueryMock.elements = [popoverEl] - - jQueryMock.fn.popover.call(jQueryMock, { - content: 'Popover content' - }) - - expect(Popover.getInstance(popoverEl)).not.toBeNull() - }) - - it('should not re create a popover', () => { - fixtureEl.innerHTML = 'BS X' - - const popoverEl = fixtureEl.querySelector('a') - const popover = new Popover(popoverEl) - - jQueryMock.fn.popover = Popover.jQueryInterface - jQueryMock.elements = [popoverEl] - - jQueryMock.fn.popover.call(jQueryMock) - - expect(Popover.getInstance(popoverEl)).toEqual(popover) - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = 'BS X' - - const popoverEl = fixtureEl.querySelector('a') - const action = 'undefinedMethod' - - jQueryMock.fn.popover = Popover.jQueryInterface - jQueryMock.elements = [popoverEl] - - expect(() => { - jQueryMock.fn.popover.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should should call show method', () => { - fixtureEl.innerHTML = 'BS X' - - const popoverEl = fixtureEl.querySelector('a') - const popover = new Popover(popoverEl) - - jQueryMock.fn.popover = Popover.jQueryInterface - jQueryMock.elements = [popoverEl] - - const spy = spyOn(popover, 'show') - - jQueryMock.fn.popover.call(jQueryMock, 'show') - - expect(spy).toHaveBeenCalled() - }) - }) - describe('getInstance', () => { it('should return popover instance', () => { fixtureEl.innerHTML = 'BS X' diff --git a/js/tests/unit/scrollspy.spec.js b/js/tests/unit/scrollspy.spec.js index fc44471c42da..f5e75029377d 100644 --- a/js/tests/unit/scrollspy.spec.js +++ b/js/tests/unit/scrollspy.spec.js @@ -1,7 +1,7 @@ import EventHandler from '../../src/dom/event-handler.js' import ScrollSpy from '../../src/scrollspy.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('ScrollSpy', () => { @@ -648,111 +648,6 @@ describe('ScrollSpy', () => { }) }) - describe('jQueryInterface', () => { - it('should create a scrollspy', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.scrollspy.call(jQueryMock, { target: '#navBar' }) - - expect(ScrollSpy.getInstance(div)).not.toBeNull() - }) - - it('should create a scrollspy with given config', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.scrollspy.call(jQueryMock, { rootMargin: '100px' }) - const spy = spyOn(ScrollSpy.prototype, 'constructor') - expect(spy).not.toHaveBeenCalledWith(div, { rootMargin: '100px' }) - - const scrollspy = ScrollSpy.getInstance(div) - expect(scrollspy).not.toBeNull() - expect(scrollspy._config.rootMargin).toEqual('100px') - }) - - it('should not re create a scrollspy', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - const scrollSpy = new ScrollSpy(div) - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.scrollspy.call(jQueryMock) - - expect(ScrollSpy.getInstance(div)).toEqual(scrollSpy) - }) - - it('should call a scrollspy method', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - const scrollSpy = new ScrollSpy(div) - - const spy = spyOn(scrollSpy, 'refresh') - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.scrollspy.call(jQueryMock, 'refresh') - - expect(ScrollSpy.getInstance(div)).toEqual(scrollSpy) - expect(spy).toHaveBeenCalled() - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - const action = 'undefinedMethod' - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.scrollspy.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should throw error on protected method', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - const action = '_getConfig' - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.scrollspy.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - - it('should throw error if method "constructor" is being called', () => { - fixtureEl.innerHTML = getDummyFixture() - - const div = fixtureEl.querySelector('.content') - const action = 'constructor' - - jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.scrollspy.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('getInstance', () => { it('should return scrollspy instance', () => { fixtureEl.innerHTML = getDummyFixture() diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js index 4fcf8ee0f0b4..781086010fa3 100644 --- a/js/tests/unit/tab.spec.js +++ b/js/tests/unit/tab.spec.js @@ -1,6 +1,6 @@ import Tab from '../../src/tab.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Tab', () => { @@ -827,66 +827,6 @@ describe('Tab', () => { }) }) - describe('jQueryInterface', () => { - it('should create a tab', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('.nav > div') - - jQueryMock.fn.tab = Tab.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tab.call(jQueryMock) - - expect(Tab.getInstance(div)).not.toBeNull() - }) - - it('should not re create a tab', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('.nav > div') - const tab = new Tab(div) - - jQueryMock.fn.tab = Tab.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tab.call(jQueryMock) - - expect(Tab.getInstance(div)).toEqual(tab) - }) - - it('should call a tab method', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('.nav > div') - const tab = new Tab(div) - - const spy = spyOn(tab, 'show') - - jQueryMock.fn.tab = Tab.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tab.call(jQueryMock, 'show') - - expect(Tab.getInstance(div)).toEqual(tab) - expect(spy).toHaveBeenCalled() - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('.nav > div') - const action = 'undefinedMethod' - - jQueryMock.fn.tab = Tab.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.tab.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('getInstance', () => { it('should return null if there is no instance', () => { expect(Tab.getInstance(fixtureEl)).toBeNull() diff --git a/js/tests/unit/toast.spec.js b/js/tests/unit/toast.spec.js index 200fe3e40cf7..7dcf82de89e6 100644 --- a/js/tests/unit/toast.spec.js +++ b/js/tests/unit/toast.spec.js @@ -1,6 +1,6 @@ import Toast from '../../src/toast.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Toast', () => { @@ -536,66 +536,6 @@ describe('Toast', () => { }) }) - describe('jQueryInterface', () => { - it('should create a toast', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.toast = Toast.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.toast.call(jQueryMock) - - expect(Toast.getInstance(div)).not.toBeNull() - }) - - it('should not re create a toast', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const toast = new Toast(div) - - jQueryMock.fn.toast = Toast.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.toast.call(jQueryMock) - - expect(Toast.getInstance(div)).toEqual(toast) - }) - - it('should call a toast method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const toast = new Toast(div) - - const spy = spyOn(toast, 'show') - - jQueryMock.fn.toast = Toast.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.toast.call(jQueryMock, 'show') - - expect(Toast.getInstance(div)).toEqual(toast) - expect(spy).toHaveBeenCalled() - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.toast = Toast.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.toast.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) - describe('getInstance', () => { it('should return a toast instance', () => { fixtureEl.innerHTML = '
' diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js index 37f2c230d037..13d3c235f5a8 100644 --- a/js/tests/unit/tooltip.spec.js +++ b/js/tests/unit/tooltip.spec.js @@ -2,7 +2,7 @@ import EventHandler from '../../src/dom/event-handler.js' import Tooltip from '../../src/tooltip.js' import { noop } from '../../src/util/index.js' import { - clearFixture, createEvent, getFixture, jQueryMock + clearFixture, createEvent, getFixture } from '../helpers/fixture.js' describe('Tooltip', () => { @@ -582,27 +582,6 @@ describe('Tooltip', () => { }) }) - it('should show a tooltip with a jquery element container', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const tooltipEl = fixtureEl.querySelector('a') - const tooltip = new Tooltip(tooltipEl, { - container: { - 0: fixtureEl, - jquery: 'jQuery' - } - }) - - tooltipEl.addEventListener('shown.bs.tooltip', () => { - expect(fixtureEl.querySelector('.tooltip')).not.toBeNull() - resolve() - }) - - tooltip.show() - }) - }) - it('should show a tooltip with a selector in container', () => { return new Promise(resolve => { fixtureEl.innerHTML = '' @@ -1243,25 +1222,6 @@ describe('Tooltip', () => { expect().nothing() }) - it('should add the content as a child of the element for jQuery elements', () => { - fixtureEl.innerHTML = [ - '', - '
', - '
' - ].join('') - - const tooltipEl = fixtureEl.querySelector('a') - const childContent = fixtureEl.querySelector('div') - const tooltip = new Tooltip(tooltipEl, { - html: true - }) - - tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } }) - tooltip.show() - - expect(childContent.parentNode).toEqual(tooltip._getTipElement()) - }) - it('should add the child text content in the element', () => { fixtureEl.innerHTML = [ '', @@ -1522,64 +1482,4 @@ describe('Tooltip', () => { expect(tooltip2._getTitle()).toEqual('nothing') }) }) - - describe('jQueryInterface', () => { - it('should create a tooltip', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - jQueryMock.fn.tooltip = Tooltip.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tooltip.call(jQueryMock) - - expect(Tooltip.getInstance(div)).not.toBeNull() - }) - - it('should not re create a tooltip', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const tooltip = new Tooltip(div) - - jQueryMock.fn.tooltip = Tooltip.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tooltip.call(jQueryMock) - - expect(Tooltip.getInstance(div)).toEqual(tooltip) - }) - - it('should call a tooltip method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const tooltip = new Tooltip(div) - - const spy = spyOn(tooltip, 'show') - - jQueryMock.fn.tooltip = Tooltip.jQueryInterface - jQueryMock.elements = [div] - - jQueryMock.fn.tooltip.call(jQueryMock, 'show') - - expect(Tooltip.getInstance(div)).toEqual(tooltip) - expect(spy).toHaveBeenCalled() - }) - - it('should throw error on undefined method', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const action = 'undefinedMethod' - - jQueryMock.fn.tooltip = Tooltip.jQueryInterface - jQueryMock.elements = [div] - - expect(() => { - jQueryMock.fn.tooltip.call(jQueryMock, action) - }).toThrowError(TypeError, `No method named "${action}"`) - }) - }) }) diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index 9e154818f288..7fecbf7f4947 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -72,18 +72,6 @@ describe('Util', () => { expect(Util.isElement({})).toBeFalse() expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toBeFalse() }) - - it('should detect jQuery element', () => { - fixtureEl.innerHTML = '
' - - const el = fixtureEl.querySelector('div') - const fakejQuery = { - 0: el, - jquery: 'foo' - } - - expect(Util.isElement(fakejQuery)).toBeTrue() - }) }) describe('getElement', () => { @@ -103,13 +91,6 @@ describe('Util', () => { expect(Util.getElement()).toBeNull() expect(Util.getElement(null)).toBeNull() expect(Util.getElement(fixtureEl.querySelectorAll('.test'))).toBeNull() - - const fakejQueryObject = { - 0: el, - jquery: 'foo' - } - - expect(Util.getElement(fakejQueryObject)).toEqual(el) }) }) @@ -425,39 +406,6 @@ describe('Util', () => { }) }) - describe('getjQuery', () => { - const fakejQuery = { trigger() {} } - - beforeEach(() => { - Object.defineProperty(window, 'jQuery', { - value: fakejQuery, - writable: true - }) - }) - - afterEach(() => { - window.jQuery = undefined - }) - - it('should return jQuery object when present', () => { - expect(Util.getjQuery()).toEqual(fakejQuery) - }) - - it('should not return jQuery object when present if data-bs-no-jquery', () => { - document.body.setAttribute('data-bs-no-jquery', '') - - expect(window.jQuery).toEqual(fakejQuery) - expect(Util.getjQuery()).toBeNull() - - document.body.removeAttribute('data-bs-no-jquery') - }) - - it('should not return jQuery if not present', () => { - window.jQuery = undefined - expect(Util.getjQuery()).toBeNull() - }) - }) - describe('onDOMContentLoaded', () => { it('should execute callbacks when DOMContentLoaded is fired and should not add more than one listener', () => { const spy = jasmine.createSpy() @@ -486,32 +434,6 @@ describe('Util', () => { }) }) - describe('defineJQueryPlugin', () => { - const fakejQuery = { fn: {} } - - beforeEach(() => { - Object.defineProperty(window, 'jQuery', { - value: fakejQuery, - writable: true - }) - }) - - afterEach(() => { - window.jQuery = undefined - }) - - it('should define a plugin on the jQuery instance', () => { - const pluginMock = Util.noop - pluginMock.NAME = 'test' - pluginMock.jQueryInterface = Util.noop - - Util.defineJQueryPlugin(pluginMock) - expect(fakejQuery.fn.test).toEqual(pluginMock.jQueryInterface) - expect(fakejQuery.fn.test.Constructor).toEqual(pluginMock) - expect(fakejQuery.fn.test.noConflict).toEqual(jasmine.any(Function)) - }) - }) - describe('execute', () => { it('should execute if arg is function', () => { const spy = jasmine.createSpy('spy') diff --git a/package-lock.json b/package-lock.json index bbf7fedd8447..a964ceb02971 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,12 +54,13 @@ "github-slugger": "^2.0.0", "globby": "^14.1.0", "hammer-simulator": "0.0.1", + "html-validate": "^8.29.0", "htmlparser2": "^10.0.0", "image-size": "^2.0.2", "ip": "^2.0.1", "jasmine": "^5.10.0", - "jquery": "^3.7.1", "js-yaml": "^4.1.0", + "jsdom": "^25.0.1", "karma": "^6.4.4", "karma-browserstack-launcher": "1.4.0", "karma-chrome-launcher": "^3.2.0", @@ -70,6 +71,7 @@ "karma-jasmine-html-reporter": "^2.1.0", "karma-rollup-preprocessor": "7.0.7", "lockfile-lint": "^4.14.1", + "markdownlint-cli": "^0.45.0", "mime": "^4.1.0", "nodemon": "^3.1.10", "npm-run-all2": "^8.0.4", @@ -83,7 +85,7 @@ "rollup": "^4.52.0", "rollup-plugin-istanbul": "^5.0.0", "rtlcss": "^4.3.0", - "sass": "1.78.0", + "sass": "^1.90.0", "sass-true": "^9.0.0", "shelljs": "^0.10.0", "stylelint": "^16.24.0", @@ -362,6 +364,25 @@ "node": ">= 14.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/@astrojs/check": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.4.tgz", @@ -2234,6 +2255,75 @@ "node": ">=0.1.90" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", @@ -2971,6 +3061,18 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@html-validate/stylish": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-4.3.0.tgz", + "integrity": "sha512-eUfvKpRJg5TvzSfTf2EovrQoTKjkRnPUOUnXVJ2cQ4GbC/bQw98oxN+DdSf+HxOBK00YOhsP52xWdJPV1o4n5w==", + "dev": true, + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3565,7 +3667,6 @@ "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, - "license": "MIT", "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -3575,22 +3676,22 @@ "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -3765,149 +3866,472 @@ "dev": true, "license": "MIT" }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "dev": true, + "hasInstallScript": true, "license": "MIT", "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, "engines": { - "node": ">=14" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rollup/plugin-babel": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", - "integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@rollup/pluginutils": "^5.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - }, - "rollup": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.6", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", - "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.2.0", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^4.0.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", - "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rollup/plugin-replace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", - "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "magic-string": "^0.30.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", + "integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", + "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" @@ -4314,12 +4738,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@sidvind/better-ajv-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-3.0.1.tgz", + "integrity": "sha512-++1mEYIeozfnwWI9P1ECvOPoacy+CgDASrmGvXPMCcqgx0YUzB01vZ78uHdQ443V6sTY+e9MzHqmN9DOls02aw==", + "dev": true, + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 16.14" + }, + "peerDependencies": { + "ajv": "^6.12.3 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, - "license": "MIT" + "optional": true, + "peer": true }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", @@ -4506,6 +4946,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "dev": true + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -6601,12 +7047,87 @@ "node": ">=4" } }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, - "license": "MIT" + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } }, "node_modules/data-view-buffer": { "version": "1.0.2", @@ -6697,6 +7218,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true + }, "node_modules/decode-named-character-reference": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", @@ -6711,6 +7238,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -6841,7 +7377,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "dev": true, - "license": "Apache-2.0", "optional": true, "engines": { "node": ">=8" @@ -6905,6 +7440,17 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -9357,6 +9903,18 @@ "dev": true, "license": "ISC" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", @@ -9377,6 +9935,132 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-validate": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-8.29.0.tgz", + "integrity": "sha512-RFfFIWaUB9SN8iETL2GoPvjWEX1gcbz0+m+vao7xkPl0cnlgMDu9RcjdQz6T3n6LgT/LENPkvxHzVkqY/Qs3Tg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "dependencies": { + "@html-validate/stylish": "^4.1.0", + "@sidvind/better-ajv-errors": "3.0.1", + "ajv": "^8.0.0", + "glob": "^10.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.js" + }, + "engines": { + "node": ">= 16.14" + }, + "peerDependencies": { + "jest": "^27.1 || ^28.1.3 || ^29.0.3", + "jest-diff": "^27.1 || ^28.1.3 || ^29.0.3", + "jest-snapshot": "^27.1 || ^28.1.3 || ^29.0.3", + "vitest": "^0.34.0 || ^1.0.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/html-validate/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/html-validate/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/html-validate/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -9457,6 +10141,28 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/https-proxy-agent": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", @@ -9535,9 +10241,9 @@ } }, "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", "dev": true, "license": "MIT" }, @@ -10069,6 +10775,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -10521,19 +11233,20 @@ } }, "node_modules/jest-diff": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", - "integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.0.5" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-diff/node_modules/ansi-styles": { @@ -10542,6 +11255,8 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -10558,6 +11273,8 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -10569,12 +11286,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "license": "MIT" + "optional": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, "node_modules/js-tokens": { "version": "4.0.0", @@ -10596,6 +11317,132 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -10676,6 +11523,15 @@ "integrity": "sha512-d2vwomK605ks7Q+uCpbwGyoIF5j+UZuJjlYcugISBt3CxM+eBo/W6y63yVPIyIvbYON+pvJYsYZjCYbzqJj/xQ==", "dev": true }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/karma": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", @@ -11144,9 +12000,34 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "license": "ISC", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/katex": { + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, "engines": { - "node": ">=10" + "node": ">= 12" } }, "node_modules/keyv": { @@ -11220,6 +12101,15 @@ "dev": true, "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -11403,6 +12293,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -11414,6 +12333,211 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/markdownlint": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", + "dev": true, + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz", + "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==", + "dev": true, + "dependencies": { + "commander": "~13.1.0", + "glob": "~11.0.2", + "ignore": "~7.0.4", + "js-yaml": "~4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.1.0", + "markdownlint": "~0.38.0", + "minimatch": "~10.0.1", + "run-con": "~1.3.2", + "smol-toml": "~1.3.4" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/markdownlint-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/markdownlint-cli/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/markdownlint-cli/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/smol-toml": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", + "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/markdownlint/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -12581,6 +13705,12 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -12669,21 +13799,129 @@ "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-directive/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true + }, + "node_modules/micromark-extension-directive/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/micromark-extension-directive/node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, "node_modules/micromark-extension-gfm": { @@ -12814,6 +14052,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-extension-mdx-expression": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", @@ -13583,6 +14840,14 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -13873,6 +15138,12 @@ "node": ">=8" } }, + "node_modules/nwsapi": { + "version": "2.2.22", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", + "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -14834,18 +16105,19 @@ } }, "node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { @@ -14853,7 +16125,8 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -14944,6 +16217,15 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -15040,8 +16322,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/read-cache": { "version": "1.0.0", @@ -16023,6 +17304,12 @@ } } }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true + }, "node_modules/rtlcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", @@ -16042,6 +17329,30 @@ "node": ">=12.0.0" } }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/run-con/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -16143,14 +17454,13 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.78.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", - "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", + "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", "dev": true, - "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -16158,6 +17468,9 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-formatter": { @@ -16197,68 +17510,94 @@ } } }, - "node_modules/sass/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/sass-true/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, - "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/sass/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/sass-true/node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "dev": true + }, + "node_modules/sass-true/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/sass/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/sass-true/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=8.6" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/sass-true/node_modules/jest-diff": { + "version": "30.1.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", + "integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", "dev": true, - "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.0.5" }, "engines": { - "node": ">=8.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/sass-true/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/sass-true/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/sax": { @@ -16268,6 +17607,18 @@ "dev": true, "license": "ISC" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/search-insights": { "version": "2.17.3", "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", @@ -17648,6 +18999,12 @@ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -17789,6 +19146,24 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true + }, "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", @@ -17832,6 +19207,18 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -18125,6 +19512,12 @@ "node": "*" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, "node_modules/ufo": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", @@ -19095,6 +20488,18 @@ "dev": true, "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -19113,6 +20518,39 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -19425,6 +20863,21 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", diff --git a/package.json b/package.json index a5423933ea73..19560a5600ce 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "start": "npm-run-all --parallel watch docs-serve", "bundlewatch": "bundlewatch --config .bundlewatch.config.json", "css": "npm-run-all css-compile css-prefix css-rtl css-minify", - "css-compile": "sass --style expanded --source-map --embed-sources --no-error-css scss/:dist/css/", + "css-compile": "sass --style expanded --source-map --embed-sources --no-error-css scss/bootstrap.scss:dist/css/bootstrap.css scss/bootstrap-grid.scss:dist/css/bootstrap-grid.css scss/bootstrap-reboot.scss:dist/css/bootstrap-reboot.css scss/bootstrap-utilities.scss:dist/css/bootstrap-utilities.css", "css-rtl": "cross-env NODE_ENV=RTL postcss --config build/postcss.config.mjs --dir \"dist/css\" --ext \".rtl.css\" \"dist/css/*.css\" \"!dist/css/*.min.css\" \"!dist/css/*.rtl.css\"", "css-lint": "npm-run-all --aggregate-output --continue-on-error --parallel css-lint-*", "css-lint-stylelint": "stylelint \"**/*.{css,scss}\" --cache --cache-location .cache/.stylelintcache", @@ -66,19 +66,19 @@ "js-minify-standalone": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js", "js-minify-standalone-esm": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js", "js-minify-bundle": "terser --compress passes=2 --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js", - "js-test": "npm-run-all --aggregate-output --parallel js-test-karma js-test-jquery js-test-integration-*", + "js-test": "npm-run-all --aggregate-output --parallel js-test-karma js-test-integration-*", "js-debug": "cross-env DEBUG=true npm run js-test-karma", "js-test-karma": "karma start js/tests/karma.conf.js", "js-test-integration-bundle": "rollup --config js/tests/integration/rollup.bundle.js", "js-test-integration-modularity": "rollup --config js/tests/integration/rollup.bundle-modularity.js", "js-test-cloud": "cross-env BROWSERSTACK=true npm run js-test-karma", - "js-test-jquery": "cross-env JQUERY=true npm run js-test-karma", - "lint": "npm-run-all --aggregate-output --continue-on-error --parallel js-lint css-lint lockfile-lint", + "lint": "npm-run-all --aggregate-output --continue-on-error --parallel js-lint css-lint lockfile-lint lint-mdx", + "lint-mdx": "markdownlint \"site/src/content/**/*.mdx\"", "docs": "npm-run-all docs-build docs-lint", "docs-build": "npm run astro-build", "docs-compile": "npm run docs-build", - "docs-vnu": "node build/vnu-jar.mjs", - "docs-lint": "npm-run-all docs-prettier-check docs-vnu", + "docs-html-validate": "node build/html-validate.mjs", + "docs-lint": "npm-run-all docs-prettier-check docs-html-validate", "docs-prettier-check": "prettier --config site/.prettierrc.json -c --cache site", "docs-prettier-format": "prettier --config site/.prettierrc.json --write --cache site", "docs-serve": "npm run astro-dev", @@ -143,11 +143,11 @@ "github-slugger": "^2.0.0", "globby": "^14.1.0", "hammer-simulator": "0.0.1", + "html-validate": "^8.24.1", "htmlparser2": "^10.0.0", "image-size": "^2.0.2", "ip": "^2.0.1", "jasmine": "^5.10.0", - "jquery": "^3.7.1", "js-yaml": "^4.1.0", "karma": "^6.4.4", "karma-browserstack-launcher": "1.4.0", @@ -159,6 +159,7 @@ "karma-jasmine-html-reporter": "^2.1.0", "karma-rollup-preprocessor": "7.0.7", "lockfile-lint": "^4.14.1", + "markdownlint-cli": "^0.45.0", "mime": "^4.1.0", "nodemon": "^3.1.10", "npm-run-all2": "^8.0.4", @@ -172,14 +173,13 @@ "rollup": "^4.52.0", "rollup-plugin-istanbul": "^5.0.0", "rtlcss": "^4.3.0", - "sass": "1.78.0", + "sass": "^1.90.0", "sass-true": "^9.0.0", "shelljs": "^0.10.0", "stylelint": "^16.24.0", "stylelint-config-twbs-bootstrap": "^16.1.0", "terser": "^5.44.0", "unist-util-visit": "^5.0.0", - "vnu-jar": "24.10.17", "zod": "^4.1.9" }, "files": [ diff --git a/scss/_accordion.scss b/scss/_accordion.scss index e9f267fba328..4f3dc7b8f062 100644 --- a/scss/_accordion.scss +++ b/scss/_accordion.scss @@ -1,153 +1,187 @@ -// -// Base styles -// - -.accordion { - // scss-docs-start accordion-css-vars - --#{$prefix}accordion-color: #{$accordion-color}; - --#{$prefix}accordion-bg: #{$accordion-bg}; - --#{$prefix}accordion-transition: #{$accordion-transition}; - --#{$prefix}accordion-border-color: #{$accordion-border-color}; - --#{$prefix}accordion-border-width: #{$accordion-border-width}; - --#{$prefix}accordion-border-radius: #{$accordion-border-radius}; - --#{$prefix}accordion-inner-border-radius: #{$accordion-inner-border-radius}; - --#{$prefix}accordion-btn-padding-x: #{$accordion-button-padding-x}; - --#{$prefix}accordion-btn-padding-y: #{$accordion-button-padding-y}; - --#{$prefix}accordion-btn-color: #{$accordion-button-color}; - --#{$prefix}accordion-btn-bg: #{$accordion-button-bg}; - --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon)}; - --#{$prefix}accordion-btn-icon-width: #{$accordion-icon-width}; - --#{$prefix}accordion-btn-icon-transform: #{$accordion-icon-transform}; - --#{$prefix}accordion-btn-icon-transition: #{$accordion-icon-transition}; - --#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon)}; - --#{$prefix}accordion-btn-focus-box-shadow: #{$accordion-button-focus-box-shadow}; - --#{$prefix}accordion-body-padding-x: #{$accordion-body-padding-x}; - --#{$prefix}accordion-body-padding-y: #{$accordion-body-padding-y}; - --#{$prefix}accordion-active-color: #{$accordion-button-active-color}; - --#{$prefix}accordion-active-bg: #{$accordion-button-active-bg}; - // scss-docs-end accordion-css-vars -} +@use "config" as *; +@use "variables" as *; +@use "functions" as *; +@use "vendor/rfs" as *; +@use "mixins/border-radius" as *; +@use "mixins/transition" as *; +@use "mixins/box-shadow" as *; +@use "mixins/color-mode" as *; + +// scss-docs-start accordion-variables +$accordion-padding-y: 1rem !default; +$accordion-padding-x: 1.25rem !default; +$accordion-color: var(--#{$prefix}body-color) !default; +$accordion-bg: var(--#{$prefix}body-bg) !default; +$accordion-border-width: var(--#{$prefix}border-width) !default; +$accordion-border-color: var(--#{$prefix}border-color) !default; +$accordion-border-radius: var(--#{$prefix}border-radius-lg) !default; +$accordion-inner-border-radius: calc(#{$accordion-border-radius} - #{$accordion-border-width}) !default; + +$accordion-body-padding-y: $accordion-padding-y !default; +$accordion-body-padding-x: $accordion-padding-x !default; + +$accordion-button-padding-y: $accordion-padding-y !default; +$accordion-button-padding-x: $accordion-padding-x !default; +$accordion-button-color: var(--#{$prefix}fg-2) !default; +$accordion-button-bg: var(--#{$prefix}accordion-bg) !default; +$accordion-transition: $btn-transition, border-radius .15s ease !default; +$accordion-button-active-bg: var(--#{$prefix}bg-2) !default; +$accordion-button-active-color: var(--#{$prefix}fg) !default; + +$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default; + +$accordion-icon-width: 1.25rem !default; +$accordion-icon-transition: transform .2s ease-in-out !default; +$accordion-icon-transform: rotate(-180deg) !default; +$accordion-button-icon: url("data:image/svg+xml,") !default; +// scss-docs-end accordion-variables + +@layer componenents { + .accordion { + // scss-docs-start accordion-css-vars + --#{$prefix}accordion-color: #{$accordion-color}; + --#{$prefix}accordion-bg: #{$accordion-bg}; + --#{$prefix}accordion-transition: #{$accordion-transition}; + --#{$prefix}accordion-border-color: #{$accordion-border-color}; + --#{$prefix}accordion-border-width: #{$accordion-border-width}; + --#{$prefix}accordion-border-radius: #{$accordion-border-radius}; + --#{$prefix}accordion-inner-border-radius: #{$accordion-inner-border-radius}; + --#{$prefix}accordion-btn-padding-x: #{$accordion-button-padding-x}; + --#{$prefix}accordion-btn-padding-y: #{$accordion-button-padding-y}; + --#{$prefix}accordion-btn-color: #{$accordion-button-color}; + --#{$prefix}accordion-btn-bg: #{$accordion-button-bg}; + --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon)}; + --#{$prefix}accordion-btn-icon-width: #{$accordion-icon-width}; + --#{$prefix}accordion-btn-icon-transform: #{$accordion-icon-transform}; + --#{$prefix}accordion-btn-icon-transition: #{$accordion-icon-transition}; + --#{$prefix}accordion-btn-focus-box-shadow: #{$accordion-button-focus-box-shadow}; + --#{$prefix}accordion-body-padding-x: #{$accordion-body-padding-x}; + --#{$prefix}accordion-body-padding-y: #{$accordion-body-padding-y}; + --#{$prefix}accordion-active-color: #{$accordion-button-active-color}; + --#{$prefix}accordion-active-bg: #{$accordion-button-active-bg}; + // scss-docs-end accordion-css-vars + } + + .accordion-button { + position: relative; + display: flex; + align-items: center; + width: 100%; + padding: var(--#{$prefix}accordion-btn-padding-y) var(--#{$prefix}accordion-btn-padding-x); + @include font-size($font-size-base); + color: var(--#{$prefix}accordion-btn-color); + text-align: left; // Reset button style + background-color: var(--#{$prefix}accordion-btn-bg); + border: 0; + @include border-radius(0); + overflow-anchor: none; + @include transition(var(--#{$prefix}accordion-transition)); -.accordion-button { - position: relative; - display: flex; - align-items: center; - width: 100%; - padding: var(--#{$prefix}accordion-btn-padding-y) var(--#{$prefix}accordion-btn-padding-x); - @include font-size($font-size-base); - color: var(--#{$prefix}accordion-btn-color); - text-align: left; // Reset button style - background-color: var(--#{$prefix}accordion-btn-bg); - border: 0; - @include border-radius(0); - overflow-anchor: none; - @include transition(var(--#{$prefix}accordion-transition)); - - &:not(.collapsed) { - color: var(--#{$prefix}accordion-active-color); - background-color: var(--#{$prefix}accordion-active-bg); - box-shadow: inset 0 calc(-1 * var(--#{$prefix}accordion-border-width)) 0 var(--#{$prefix}accordion-border-color); // stylelint-disable-line function-disallowed-list + &:not(.collapsed) { + color: var(--#{$prefix}accordion-active-color); + background-color: var(--#{$prefix}accordion-active-bg); + box-shadow: inset 0 calc(-1 * var(--#{$prefix}accordion-border-width)) 0 var(--#{$prefix}accordion-border-color); + &::after { + background-image: var(--#{$prefix}accordion-btn-active-icon); + transform: var(--#{$prefix}accordion-btn-icon-transform); + } + } + + // Accordion icon &::after { - background-image: var(--#{$prefix}accordion-btn-active-icon); - transform: var(--#{$prefix}accordion-btn-icon-transform); + flex-shrink: 0; + width: var(--#{$prefix}accordion-btn-icon-width); + height: var(--#{$prefix}accordion-btn-icon-width); + margin-left: auto; + content: ""; + background-color: var(--#{$prefix}accordion-btn-color); + mask: var(--#{$prefix}accordion-btn-icon) no-repeat center 100%; + @include transition(var(--#{$prefix}accordion-btn-icon-transition)); } - } - // Accordion icon - &::after { - flex-shrink: 0; - width: var(--#{$prefix}accordion-btn-icon-width); - height: var(--#{$prefix}accordion-btn-icon-width); - margin-left: auto; - content: ""; - background-image: var(--#{$prefix}accordion-btn-icon); - background-repeat: no-repeat; - background-size: var(--#{$prefix}accordion-btn-icon-width); - @include transition(var(--#{$prefix}accordion-btn-icon-transition)); - } + &:hover { + z-index: 2; + } - &:hover { - z-index: 2; + &:focus { + z-index: 3; + outline: 0; + box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow); + } } - &:focus { - z-index: 3; - outline: 0; - box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow); + .accordion-header { + margin-bottom: 0; } -} -.accordion-header { - margin-bottom: 0; -} - -.accordion-item { - color: var(--#{$prefix}accordion-color); - background-color: var(--#{$prefix}accordion-bg); - border: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color); + .accordion-item { + color: var(--#{$prefix}accordion-color); + background-color: var(--#{$prefix}accordion-bg); + border: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color); - &:first-of-type { - @include border-top-radius(var(--#{$prefix}accordion-border-radius)); + &:first-of-type { + @include border-top-radius(var(--#{$prefix}accordion-border-radius)); - > .accordion-header .accordion-button { - @include border-top-radius(var(--#{$prefix}accordion-inner-border-radius)); + > .accordion-header .accordion-button { + @include border-top-radius(var(--#{$prefix}accordion-inner-border-radius)); + } } - } - &:not(:first-of-type) { - border-top: 0; - } + &:not(:first-of-type) { + border-top: 0; + } - // Only set a border-radius on the last item if the accordion is collapsed - &:last-of-type { - @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); + // Only set a border-radius on the last item if the accordion is collapsed + &:last-of-type { + @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); - > .accordion-header .accordion-button { - &.collapsed { - @include border-bottom-radius(var(--#{$prefix}accordion-inner-border-radius)); + > .accordion-header .accordion-button { + &.collapsed { + @include border-bottom-radius(var(--#{$prefix}accordion-inner-border-radius)); + } } - } - > .accordion-collapse { - @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); + > .accordion-collapse { + @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); + } } } -} -.accordion-body { - padding: var(--#{$prefix}accordion-body-padding-y) var(--#{$prefix}accordion-body-padding-x); -} + .accordion-body { + padding: var(--#{$prefix}accordion-body-padding-y) var(--#{$prefix}accordion-body-padding-x); + } -// Flush accordion items -// -// Remove borders and border-radius to keep accordion items edge-to-edge. + // Flush accordion items + // + // Remove borders and border-radius to keep accordion items edge-to-edge. -.accordion-flush { - > .accordion-item { - border-right: 0; - border-left: 0; - @include border-radius(0); + .accordion-flush { + > .accordion-item { + border-right: 0; + border-left: 0; + @include border-radius(0); - &:first-child { border-top: 0; } - &:last-child { border-bottom: 0; } + &:first-child { border-top: 0; } + &:last-child { border-bottom: 0; } - // stylelint-disable selector-max-class - > .accordion-collapse, - > .accordion-header .accordion-button, - > .accordion-header .accordion-button.collapsed { - @include border-radius(0); + // stylelint-disable selector-max-class + > .accordion-collapse, + > .accordion-header .accordion-button, + > .accordion-header .accordion-button.collapsed { + @include border-radius(0); + } + // stylelint-enable selector-max-class } - // stylelint-enable selector-max-class } -} -@if $enable-dark-mode { - @include color-mode(dark) { - .accordion-button::after { - --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon-dark)}; - --#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon-dark)}; - } - } + // @if $enable-dark-mode { + // @include color-mode(dark) { + // .accordion-button::after { + // --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon-dark)}; + // --#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon-dark)}; + // } + // } + // } } diff --git a/scss/_alert.scss b/scss/_alert.scss index b8cff9b71edf..a4b88a1a4648 100644 --- a/scss/_alert.scss +++ b/scss/_alert.scss @@ -1,68 +1,82 @@ -// -// Base styles -// +@use "sass:map"; +@use "config" as *; +@use "theme" as *; +@use "variables" as *; +@use "mixins/border-radius" as *; -.alert { - // scss-docs-start alert-css-vars - --#{$prefix}alert-bg: transparent; - --#{$prefix}alert-padding-x: #{$alert-padding-x}; - --#{$prefix}alert-padding-y: #{$alert-padding-y}; - --#{$prefix}alert-margin-bottom: #{$alert-margin-bottom}; - --#{$prefix}alert-color: inherit; - --#{$prefix}alert-border-color: transparent; - --#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color); - --#{$prefix}alert-border-radius: #{$alert-border-radius}; - --#{$prefix}alert-link-color: inherit; - // scss-docs-end alert-css-vars +// scss-docs-start alert-variables +$alert-padding-y: $spacer !default; +$alert-padding-x: $spacer !default; +$alert-margin-bottom: 1rem !default; +$alert-border-radius: var(--#{$prefix}border-radius) !default; +$alert-link-font-weight: $font-weight-bold !default; +$alert-border-width: var(--#{$prefix}border-width) !default; +$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side +// scss-docs-end alert-variables - position: relative; - padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x); - margin-bottom: var(--#{$prefix}alert-margin-bottom); - color: var(--#{$prefix}alert-color); - background-color: var(--#{$prefix}alert-bg); - border: var(--#{$prefix}alert-border); - @include border-radius(var(--#{$prefix}alert-border-radius)); -} +@layer components { + .alert { + // scss-docs-start alert-css-vars + --#{$prefix}alert-bg: transparent; + --#{$prefix}alert-padding-x: #{$alert-padding-x}; + --#{$prefix}alert-padding-y: #{$alert-padding-y}; + --#{$prefix}alert-margin-bottom: #{$alert-margin-bottom}; + --#{$prefix}alert-color: inherit; + --#{$prefix}alert-border-color: transparent; + --#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color); + --#{$prefix}alert-border-radius: #{$alert-border-radius}; + --#{$prefix}alert-link-color: inherit; + // scss-docs-end alert-css-vars -// Headings for larger alerts -.alert-heading { - // Specified to prevent conflicts of changing $headings-color - color: inherit; -} + position: relative; + padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x); + margin-bottom: var(--#{$prefix}alert-margin-bottom); + color: var(--#{$prefix}alert-color); + background-color: var(--#{$prefix}alert-bg); + border: var(--#{$prefix}alert-border); + @include border-radius(var(--#{$prefix}alert-border-radius)); + } -// Provide class for links that match alerts -.alert-link { - font-weight: $alert-link-font-weight; - color: var(--#{$prefix}alert-link-color); -} + // Headings for larger alerts + .alert-heading { + // Specified to prevent conflicts of changing $headings-color + color: inherit; + } + + // Provide class for links that match alerts + .alert-link { + font-weight: $alert-link-font-weight; + color: var(--#{$prefix}alert-link-color); + } -// Dismissible alerts -// -// Expand the right padding and account for the close button's positioning. + // Dismissible alerts + // + // Expand the right padding and account for the close button's positioning. -.alert-dismissible { - padding-right: $alert-dismissible-padding-r; + .alert-dismissible { + padding-right: $alert-dismissible-padding-r; - // Adjust close link position - .btn-close { - position: absolute; - top: 0; - right: 0; - z-index: $stretched-link-z-index + 1; - padding: $alert-padding-y * 1.25 $alert-padding-x; + // Adjust close link position + .btn-close { + position: absolute; + top: 0; + right: 0; + z-index: $stretched-link-z-index + 1; + padding: $alert-padding-y * 1.25 $alert-padding-x; + } } -} -// scss-docs-start alert-modifiers -// Generate contextual modifier classes for colorizing the alert -@each $state in map-keys($theme-colors) { - .alert-#{$state} { - --#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis); - --#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle); - --#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle); - --#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis); + // scss-docs-start alert-modifiers + // Generate contextual modifier classes for colorizing the alert + @each $state in map.keys($new-theme-colors) { + .alert-#{$state} { + --#{$prefix}alert-color: var(--#{$prefix}#{$state}-text); + --#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle); + --#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle); + --#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis); + } } + // scss-docs-end alert-modifiers } -// scss-docs-end alert-modifiers diff --git a/scss/_badge.scss b/scss/_badge.scss index cc3d2695566a..0e575012e75f 100644 --- a/scss/_badge.scss +++ b/scss/_badge.scss @@ -1,38 +1,51 @@ -// Base class -// -// Requires one of the contextual, color modifier classes for `color` and -// `background-color`. +@use "config" as *; +@use "colors" as *; +@use "variables" as *; +@use "mixins/border-radius" as *; +@use "mixins/gradients" as *; +@use "vendor/rfs" as *; -.badge { - // scss-docs-start badge-css-vars - --#{$prefix}badge-padding-x: #{$badge-padding-x}; - --#{$prefix}badge-padding-y: #{$badge-padding-y}; - @include rfs($badge-font-size, --#{$prefix}badge-font-size); - --#{$prefix}badge-font-weight: #{$badge-font-weight}; - --#{$prefix}badge-color: #{$badge-color}; - --#{$prefix}badge-border-radius: #{$badge-border-radius}; - // scss-docs-end badge-css-vars +// scss-docs-start badge-variables +$badge-font-size: .75em !default; +$badge-font-weight: $font-weight-bold !default; +$badge-color: $white !default; +$badge-padding-y: .35em !default; +$badge-padding-x: .65em !default; +$badge-border-radius: var(--#{$prefix}border-radius) !default; +// scss-docs-end badge-variables - display: inline-block; - padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x); - @include font-size(var(--#{$prefix}badge-font-size)); - font-weight: var(--#{$prefix}badge-font-weight); - line-height: 1; - color: var(--#{$prefix}badge-color); - text-align: center; - white-space: nowrap; - vertical-align: baseline; - @include border-radius(var(--#{$prefix}badge-border-radius)); - @include gradient-bg(); +@layer components { + .badge { + // scss-docs-start badge-css-vars + --#{$prefix}badge-padding-x: #{$badge-padding-x}; + --#{$prefix}badge-padding-y: #{$badge-padding-y}; + @include rfs($badge-font-size, --#{$prefix}badge-font-size); + --#{$prefix}badge-font-weight: #{$badge-font-weight}; + --#{$prefix}badge-color: #{$badge-color}; + --#{$prefix}badge-border-radius: #{$badge-border-radius}; + // scss-docs-end badge-css-vars - // Empty badges collapse automatically - &:empty { - display: none; + display: inline-block; + padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x); + @include font-size(var(--#{$prefix}badge-font-size)); + font-weight: var(--#{$prefix}badge-font-weight); + line-height: 1; + color: var(--#{$prefix}badge-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + @include border-radius(var(--#{$prefix}badge-border-radius)); + @include gradient-bg(); + + // Empty badges collapse automatically + &:empty { + display: none; + } } -} -// Quick fix for badges in buttons -.btn .badge { - position: relative; - top: -1px; + // Quick fix for badges in buttons + .btn .badge { + position: relative; + top: -1px; + } } diff --git a/scss/_banner.scss b/scss/_banner.scss new file mode 100644 index 000000000000..858d062e4f9b --- /dev/null +++ b/scss/_banner.scss @@ -0,0 +1,7 @@ +$file: "" !default; + +/*! + * Bootstrap #{$file} v6.0.0-dev (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/scss/_breadcrumb.scss b/scss/_breadcrumb.scss index b8252ff2152a..efbfe37e39c5 100644 --- a/scss/_breadcrumb.scss +++ b/scss/_breadcrumb.scss @@ -1,40 +1,63 @@ -.breadcrumb { - // scss-docs-start breadcrumb-css-vars - --#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x}; - --#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y}; - --#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom}; - @include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size); - --#{$prefix}breadcrumb-bg: #{$breadcrumb-bg}; - --#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius}; - --#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color}; - --#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x}; - --#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color}; - // scss-docs-end breadcrumb-css-vars +@use "sass:string"; +@use "config" as *; +@use "variables" as *; +@use "functions" as *; +@use "mixins/border-radius" as *; +@use "vendor/rfs" as *; - display: flex; - flex-wrap: wrap; - padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x); - margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom); - @include font-size(var(--#{$prefix}breadcrumb-font-size)); - list-style: none; - background-color: var(--#{$prefix}breadcrumb-bg); - @include border-radius(var(--#{$prefix}breadcrumb-border-radius)); -} +// scss-docs-start breadcrumb-variables +$breadcrumb-font-size: null !default; +$breadcrumb-padding-y: 0 !default; +$breadcrumb-padding-x: 0 !default; +$breadcrumb-item-padding-x: .5rem !default; +$breadcrumb-margin-bottom: 1rem !default; +$breadcrumb-bg: null !default; +$breadcrumb-divider-color: var(--#{$prefix}secondary-color) !default; +$breadcrumb-active-color: var(--#{$prefix}secondary-color) !default; +$breadcrumb-divider: string.quote("/") !default; +$breadcrumb-divider-flipped: $breadcrumb-divider !default; +$breadcrumb-border-radius: null !default; +// scss-docs-end breadcrumb-variables -.breadcrumb-item { - // The separator between breadcrumbs (by default, a forward-slash: "/") - + .breadcrumb-item { - padding-left: var(--#{$prefix}breadcrumb-item-padding-x); +@layer components { + .breadcrumb { + // scss-docs-start breadcrumb-css-vars + --#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x}; + --#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y}; + --#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom}; + @include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size); + --#{$prefix}breadcrumb-bg: #{$breadcrumb-bg}; + --#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius}; + --#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color}; + --#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x}; + --#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color}; + // scss-docs-end breadcrumb-css-vars - &::before { - float: left; // Suppress inline spacings and underlining of the separator - padding-right: var(--#{$prefix}breadcrumb-item-padding-x); - color: var(--#{$prefix}breadcrumb-divider-color); - content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"}; - } + display: flex; + flex-wrap: wrap; + padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x); + margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom); + @include font-size(var(--#{$prefix}breadcrumb-font-size)); + list-style: none; + background-color: var(--#{$prefix}breadcrumb-bg); + @include border-radius(var(--#{$prefix}breadcrumb-border-radius)); } - &.active { - color: var(--#{$prefix}breadcrumb-item-active-color); + .breadcrumb-item { + // The separator between breadcrumbs (by default, a forward-slash: "/") + + .breadcrumb-item { + padding-left: var(--#{$prefix}breadcrumb-item-padding-x); + + &::before { + float: left; // Suppress inline spacings and underlining of the separator + padding-right: var(--#{$prefix}breadcrumb-item-padding-x); + color: var(--#{$prefix}breadcrumb-divider-color); + content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"}; + } + } + + &.active { + color: var(--#{$prefix}breadcrumb-item-active-color); + } } } diff --git a/scss/_button-group.scss b/scss/_button-group.scss deleted file mode 100644 index 78e125224f93..000000000000 --- a/scss/_button-group.scss +++ /dev/null @@ -1,147 +0,0 @@ -// Make the div behave like a button -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-flex; - vertical-align: middle; // match .btn alignment given font-size hack above - - > .btn { - position: relative; - flex: 1 1 auto; - } - - // Bring the hover, focused, and "active" buttons to the front to overlay - // the borders properly - > .btn-check:checked + .btn, - > .btn-check:focus + .btn, - > .btn:hover, - > .btn:focus, - > .btn:active, - > .btn.active { - z-index: 1; - } -} - -// Optional: Group multiple button groups together for a toolbar -.btn-toolbar { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - - .input-group { - width: auto; - } -} - -.btn-group { - @include border-radius($btn-border-radius); - - // Prevent double borders when buttons are next to each other - > :not(.btn-check:first-child) + .btn, - > .btn-group:not(:first-child) { - margin-left: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list - } - - // Reset rounded corners - > .btn:not(:last-child):not(.dropdown-toggle), - > .btn.dropdown-toggle-split:first-child, - > .btn-group:not(:last-child) > .btn { - @include border-end-radius(0); - } - - // The left radius should be 0 if the button is: - // - the "third or more" child - // - the second child and the previous element isn't `.btn-check` (making it the first child visually) - // - part of a btn-group which isn't the first child - > .btn:nth-child(n + 3), - > :not(.btn-check) + .btn, - > .btn-group:not(:first-child) > .btn { - @include border-start-radius(0); - } -} - -// Sizing -// -// Remix the default button sizing classes into new ones for easier manipulation. - -.btn-group-sm > .btn { @extend .btn-sm; } -.btn-group-lg > .btn { @extend .btn-lg; } - - -// -// Split button dropdowns -// - -.dropdown-toggle-split { - padding-right: $btn-padding-x * .75; - padding-left: $btn-padding-x * .75; - - &::after, - .dropup &::after, - .dropend &::after { - margin-left: 0; - } - - .dropstart &::before { - margin-right: 0; - } -} - -.btn-sm + .dropdown-toggle-split { - padding-right: $btn-padding-x-sm * .75; - padding-left: $btn-padding-x-sm * .75; -} - -.btn-lg + .dropdown-toggle-split { - padding-right: $btn-padding-x-lg * .75; - padding-left: $btn-padding-x-lg * .75; -} - - -// The clickable button for toggling the menu -// Set the same inset shadow as the :active state -.btn-group.show .dropdown-toggle { - @include box-shadow($btn-active-box-shadow); - - // Show no shadow for `.btn-link` since it has no other button styles. - &.btn-link { - @include box-shadow(none); - } -} - - -// -// Vertical button groups -// - -.btn-group-vertical { - flex-direction: column; - align-items: flex-start; - justify-content: center; - - > .btn, - > .btn-group { - width: 100%; - } - - > .btn:not(:first-child), - > .btn-group:not(:first-child) { - margin-top: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list - } - - // Reset rounded corners - > .btn:not(:last-child):not(.dropdown-toggle), - > .btn-group:not(:last-child) > .btn { - @include border-bottom-radius(0); - } - - // The top radius should be 0 if the button is: - // - the "third or more" child - // - the second child and the previous element isn't `.btn-check` (making it the first child visually) - // - part of a btn-group which isn't the first child - > .btn:nth-child(n + 3), - > :not(.btn-check) + .btn, - > .btn-group:not(:first-child) > .btn { - @include border-top-radius(0); - } -} diff --git a/scss/_buttons.scss b/scss/_buttons.scss deleted file mode 100644 index caa4518ac8fc..000000000000 --- a/scss/_buttons.scss +++ /dev/null @@ -1,216 +0,0 @@ -// -// Base styles -// - -.btn { - // scss-docs-start btn-css-vars - --#{$prefix}btn-padding-x: #{$btn-padding-x}; - --#{$prefix}btn-padding-y: #{$btn-padding-y}; - --#{$prefix}btn-font-family: #{$btn-font-family}; - @include rfs($btn-font-size, --#{$prefix}btn-font-size); - --#{$prefix}btn-font-weight: #{$btn-font-weight}; - --#{$prefix}btn-line-height: #{$btn-line-height}; - --#{$prefix}btn-color: #{$btn-color}; - --#{$prefix}btn-bg: transparent; - --#{$prefix}btn-border-width: #{$btn-border-width}; - --#{$prefix}btn-border-color: transparent; - --#{$prefix}btn-border-radius: #{$btn-border-radius}; - --#{$prefix}btn-hover-border-color: transparent; - --#{$prefix}btn-box-shadow: #{$btn-box-shadow}; - --#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity}; - --#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5); - // scss-docs-end btn-css-vars - - display: inline-block; - padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x); - font-family: var(--#{$prefix}btn-font-family); - @include font-size(var(--#{$prefix}btn-font-size)); - font-weight: var(--#{$prefix}btn-font-weight); - line-height: var(--#{$prefix}btn-line-height); - color: var(--#{$prefix}btn-color); - text-align: center; - text-decoration: if($link-decoration == none, null, none); - white-space: $btn-white-space; - vertical-align: middle; - cursor: if($enable-button-pointers, pointer, null); - user-select: none; - border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color); - @include border-radius(var(--#{$prefix}btn-border-radius)); - @include gradient-bg(var(--#{$prefix}btn-bg)); - @include box-shadow(var(--#{$prefix}btn-box-shadow)); - @include transition($btn-transition); - - &:hover { - color: var(--#{$prefix}btn-hover-color); - text-decoration: if($link-hover-decoration == underline, none, null); - background-color: var(--#{$prefix}btn-hover-bg); - border-color: var(--#{$prefix}btn-hover-border-color); - } - - .btn-check + &:hover { - // override for the checkbox/radio buttons - color: var(--#{$prefix}btn-color); - background-color: var(--#{$prefix}btn-bg); - border-color: var(--#{$prefix}btn-border-color); - } - - &:focus-visible { - color: var(--#{$prefix}btn-hover-color); - @include gradient-bg(var(--#{$prefix}btn-hover-bg)); - border-color: var(--#{$prefix}btn-hover-border-color); - outline: 0; - // Avoid using mixin so we can pass custom focus shadow properly - @if $enable-shadows { - box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); - } @else { - box-shadow: var(--#{$prefix}btn-focus-box-shadow); - } - } - - .btn-check:focus-visible + & { - border-color: var(--#{$prefix}btn-hover-border-color); - outline: 0; - // Avoid using mixin so we can pass custom focus shadow properly - @if $enable-shadows { - box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); - } @else { - box-shadow: var(--#{$prefix}btn-focus-box-shadow); - } - } - - .btn-check:checked + &, - :not(.btn-check) + &:active, - &:first-child:active, - &.active, - &.show { - color: var(--#{$prefix}btn-active-color); - background-color: var(--#{$prefix}btn-active-bg); - // Remove CSS gradients if they're enabled - background-image: if($enable-gradients, none, null); - border-color: var(--#{$prefix}btn-active-border-color); - @include box-shadow(var(--#{$prefix}btn-active-shadow)); - - &:focus-visible { - // Avoid using mixin so we can pass custom focus shadow properly - @if $enable-shadows { - box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); - } @else { - box-shadow: var(--#{$prefix}btn-focus-box-shadow); - } - } - } - - .btn-check:checked:focus-visible + & { - // Avoid using mixin so we can pass custom focus shadow properly - @if $enable-shadows { - box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); - } @else { - box-shadow: var(--#{$prefix}btn-focus-box-shadow); - } - } - - &:disabled, - &.disabled, - fieldset:disabled & { - color: var(--#{$prefix}btn-disabled-color); - pointer-events: none; - background-color: var(--#{$prefix}btn-disabled-bg); - background-image: if($enable-gradients, none, null); - border-color: var(--#{$prefix}btn-disabled-border-color); - opacity: var(--#{$prefix}btn-disabled-opacity); - @include box-shadow(none); - } -} - - -// -// Alternate buttons -// - -// scss-docs-start btn-variant-loops -@each $color, $value in $theme-colors { - .btn-#{$color} { - @if $color == "light" { - @include button-variant( - $value, - $value, - $hover-background: shade-color($value, $btn-hover-bg-shade-amount), - $hover-border: shade-color($value, $btn-hover-border-shade-amount), - $active-background: shade-color($value, $btn-active-bg-shade-amount), - $active-border: shade-color($value, $btn-active-border-shade-amount) - ); - } @else if $color == "dark" { - @include button-variant( - $value, - $value, - $hover-background: tint-color($value, $btn-hover-bg-tint-amount), - $hover-border: tint-color($value, $btn-hover-border-tint-amount), - $active-background: tint-color($value, $btn-active-bg-tint-amount), - $active-border: tint-color($value, $btn-active-border-tint-amount) - ); - } @else { - @include button-variant($value, $value); - } - } -} - -@each $color, $value in $theme-colors { - .btn-outline-#{$color} { - @include button-outline-variant($value); - } -} -// scss-docs-end btn-variant-loops - - -// -// Link buttons -// - -// Make a button look and behave like a link -.btn-link { - --#{$prefix}btn-font-weight: #{$font-weight-normal}; - --#{$prefix}btn-color: #{$btn-link-color}; - --#{$prefix}btn-bg: transparent; - --#{$prefix}btn-border-color: transparent; - --#{$prefix}btn-hover-color: #{$btn-link-hover-color}; - --#{$prefix}btn-hover-border-color: transparent; - --#{$prefix}btn-active-color: #{$btn-link-hover-color}; - --#{$prefix}btn-active-border-color: transparent; - --#{$prefix}btn-disabled-color: #{$btn-link-disabled-color}; - --#{$prefix}btn-disabled-border-color: transparent; - --#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows - --#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb}; - - text-decoration: $link-decoration; - @if $enable-gradients { - background-image: none; - } - - &:hover, - &:focus-visible { - text-decoration: $link-hover-decoration; - } - - &:focus-visible { - color: var(--#{$prefix}btn-color); - } - - &:hover { - color: var(--#{$prefix}btn-hover-color); - } - - // No need for an active state here -} - - -// -// Button Sizes -// - -.btn-lg { - @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg); -} - -.btn-sm { - @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm); -} diff --git a/scss/_card.scss b/scss/_card.scss index dcebe6ac28c0..8221a3b4cc03 100644 --- a/scss/_card.scss +++ b/scss/_card.scss @@ -1,235 +1,261 @@ -// -// Base styles -// - -.card { - // scss-docs-start card-css-vars - --#{$prefix}card-spacer-y: #{$card-spacer-y}; - --#{$prefix}card-spacer-x: #{$card-spacer-x}; - --#{$prefix}card-title-spacer-y: #{$card-title-spacer-y}; - --#{$prefix}card-title-color: #{$card-title-color}; - --#{$prefix}card-subtitle-color: #{$card-subtitle-color}; - --#{$prefix}card-border-width: #{$card-border-width}; - --#{$prefix}card-border-color: #{$card-border-color}; - --#{$prefix}card-border-radius: #{$card-border-radius}; - --#{$prefix}card-box-shadow: #{$card-box-shadow}; - --#{$prefix}card-inner-border-radius: #{$card-inner-border-radius}; - --#{$prefix}card-cap-padding-y: #{$card-cap-padding-y}; - --#{$prefix}card-cap-padding-x: #{$card-cap-padding-x}; - --#{$prefix}card-cap-bg: #{$card-cap-bg}; - --#{$prefix}card-cap-color: #{$card-cap-color}; - --#{$prefix}card-height: #{$card-height}; - --#{$prefix}card-color: #{$card-color}; - --#{$prefix}card-bg: #{$card-bg}; - --#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding}; - --#{$prefix}card-group-margin: #{$card-group-margin}; - // scss-docs-end card-css-vars - - position: relative; - display: flex; - flex-direction: column; - min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106 - height: var(--#{$prefix}card-height); - color: var(--#{$prefix}body-color); - word-wrap: break-word; - background-color: var(--#{$prefix}card-bg); - background-clip: border-box; - border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); - @include border-radius(var(--#{$prefix}card-border-radius)); - @include box-shadow(var(--#{$prefix}card-box-shadow)); - - > hr { - margin-right: 0; - margin-left: 0; - } +@use "config" as *; +@use "variables" as *; +@use "mixins/border-radius" as *; +@use "mixins/box-shadow" as *; +@use "layout/breakpoints" as *; + +// scss-docs-start card-variables +$card-spacer-y: $spacer !default; +$card-spacer-x: $spacer !default; +$card-title-spacer-y: $spacer * .5 !default; +$card-title-color: null !default; +$card-subtitle-color: null !default; +$card-border-width: var(--#{$prefix}border-width) !default; +$card-border-color: var(--#{$prefix}border-color-translucent) !default; +$card-border-radius: var(--#{$prefix}border-radius) !default; +$card-box-shadow: null !default; +$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default; +$card-cap-padding-y: $card-spacer-y * .5 !default; +$card-cap-padding-x: $card-spacer-x !default; +$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), .03) !default; +$card-cap-color: null !default; +$card-height: null !default; +$card-color: null !default; +$card-bg: var(--#{$prefix}body-bg) !default; +$card-img-overlay-padding: $spacer !default; +$card-group-margin: $grid-gutter-width * .5 !default; +// scss-docs-end card-variables + +@layer components { + .card { + // scss-docs-start card-css-vars + --#{$prefix}card-spacer-y: #{$card-spacer-y}; + --#{$prefix}card-spacer-x: #{$card-spacer-x}; + --#{$prefix}card-title-spacer-y: #{$card-title-spacer-y}; + --#{$prefix}card-title-color: #{$card-title-color}; + --#{$prefix}card-subtitle-color: #{$card-subtitle-color}; + --#{$prefix}card-border-width: #{$card-border-width}; + --#{$prefix}card-border-color: #{$card-border-color}; + --#{$prefix}card-border-radius: #{$card-border-radius}; + --#{$prefix}card-box-shadow: #{$card-box-shadow}; + --#{$prefix}card-inner-border-radius: #{$card-inner-border-radius}; + --#{$prefix}card-cap-padding-y: #{$card-cap-padding-y}; + --#{$prefix}card-cap-padding-x: #{$card-cap-padding-x}; + --#{$prefix}card-cap-bg: #{$card-cap-bg}; + --#{$prefix}card-cap-color: #{$card-cap-color}; + --#{$prefix}card-height: #{$card-height}; + --#{$prefix}card-color: #{$card-color}; + --#{$prefix}card-bg: #{$card-bg}; + --#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding}; + --#{$prefix}card-group-margin: #{$card-group-margin}; + // scss-docs-end card-css-vars + + position: relative; + display: flex; + flex-direction: column; + min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106 + height: var(--#{$prefix}card-height); + color: var(--#{$prefix}body-color); + word-wrap: break-word; + background-color: var(--#{$prefix}card-bg); + background-clip: border-box; + border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + @include border-radius(var(--#{$prefix}card-border-radius)); + @include box-shadow(var(--#{$prefix}card-box-shadow)); + + > hr { + margin-right: 0; + margin-left: 0; + } - > .list-group { - border-top: inherit; - border-bottom: inherit; + > .list-group { + border-top: inherit; + border-bottom: inherit; - &:first-child { - border-top-width: 0; - @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); + &:first-child { + border-top-width: 0; + @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); + } + + &:last-child { + border-bottom-width: 0; + @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); + } } - &:last-child { - border-bottom-width: 0; - @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); + // Due to specificity of the above selector (`.card > .list-group`), we must + // use a child selector here to prevent double borders. + > .card-header + .list-group, + > .list-group + .card-footer { + border-top: 0; } } - // Due to specificity of the above selector (`.card > .list-group`), we must - // use a child selector here to prevent double borders. - > .card-header + .list-group, - > .list-group + .card-footer { - border-top: 0; + .card-body { + // Enable `flex-grow: 1` for decks and groups so that card blocks take up + // as much space as possible, ensuring footers are aligned to the bottom. + flex: 1 1 auto; + padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x); + color: var(--#{$prefix}card-color); } -} - -.card-body { - // Enable `flex-grow: 1` for decks and groups so that card blocks take up - // as much space as possible, ensuring footers are aligned to the bottom. - flex: 1 1 auto; - padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x); - color: var(--#{$prefix}card-color); -} - -.card-title { - margin-bottom: var(--#{$prefix}card-title-spacer-y); - color: var(--#{$prefix}card-title-color); -} -.card-subtitle { - margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); // stylelint-disable-line function-disallowed-list - margin-bottom: 0; - color: var(--#{$prefix}card-subtitle-color); -} + .card-title { + margin-bottom: var(--#{$prefix}card-title-spacer-y); + color: var(--#{$prefix}card-title-color); + } -.card-text:last-child { - margin-bottom: 0; -} + .card-subtitle { + margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); + margin-bottom: 0; + color: var(--#{$prefix}card-subtitle-color); + } -.card-link { - &:hover { - text-decoration: if($link-hover-decoration == underline, none, null); + .card-text:last-child { + margin-bottom: 0; } - + .card-link { - margin-left: var(--#{$prefix}card-spacer-x); + .card-link { + &:hover { + text-decoration: if($link-hover-decoration == underline, none, null); + } + + + .card-link { + margin-left: var(--#{$prefix}card-spacer-x); + } } -} -// -// Optional textual caps -// + // + // Optional textual caps + // -.card-header { - padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); - margin-bottom: 0; // Removes the default margin-bottom of - color: var(--#{$prefix}card-cap-color); - background-color: var(--#{$prefix}card-cap-bg); - border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + .card-header { + padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); + margin-bottom: 0; // Removes the default margin-bottom of + color: var(--#{$prefix}card-cap-color); + background-color: var(--#{$prefix}card-cap-bg); + border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); - &:first-child { - @include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0); + &:first-child { + @include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0); + } } -} -.card-footer { - padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); - color: var(--#{$prefix}card-cap-color); - background-color: var(--#{$prefix}card-cap-bg); - border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + .card-footer { + padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); + color: var(--#{$prefix}card-cap-color); + background-color: var(--#{$prefix}card-cap-bg); + border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); - &:last-child { - @include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius)); + &:last-child { + @include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius)); + } } -} -// -// Header navs -// + // + // Header navs + // -.card-header-tabs { - margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list - margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); // stylelint-disable-line function-disallowed-list - margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list - border-bottom: 0; + .card-header-tabs { + margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); + margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); + margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); + border-bottom: 0; - .nav-link.active { - background-color: var(--#{$prefix}card-bg); - border-bottom-color: var(--#{$prefix}card-bg); + .nav-link.active { + background-color: var(--#{$prefix}card-bg); + border-bottom-color: var(--#{$prefix}card-bg); + } } -} -.card-header-pills { - margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list - margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list -} - -// Card image -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: var(--#{$prefix}card-img-overlay-padding); - @include border-radius(var(--#{$prefix}card-inner-border-radius)); -} + .card-header-pills { + margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); + margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); + } -.card-img, -.card-img-top, -.card-img-bottom { - width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch -} + // Card image + .card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: var(--#{$prefix}card-img-overlay-padding); + @include border-radius(var(--#{$prefix}card-inner-border-radius)); + } -.card-img, -.card-img-top { - @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); -} + .card-img, + .card-img-top, + .card-img-bottom { + width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch + } -.card-img, -.card-img-bottom { - @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); -} + .card-img, + .card-img-top { + @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); + } + .card-img, + .card-img-bottom { + @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); + } -// -// Card groups -// -.card-group { - // The child selector allows nested `.card` within `.card-group` - // to display properly. - > .card { - margin-bottom: var(--#{$prefix}card-group-margin); - } + // + // Card groups + // - @include media-breakpoint-up(sm) { - display: flex; - flex-flow: row wrap; + .card-group { // The child selector allows nested `.card` within `.card-group` // to display properly. > .card { - flex: 1 0 0; - margin-bottom: 0; - - + .card { - margin-left: 0; - border-left: 0; - } - - // Handle rounded corners - @if $enable-rounded { - &:not(:last-child) { - @include border-end-radius(0); + margin-bottom: var(--#{$prefix}card-group-margin); + } - > .card-img-top, - > .card-header { - // stylelint-disable-next-line property-disallowed-list - border-top-right-radius: 0; - } - > .card-img-bottom, - > .card-footer { - // stylelint-disable-next-line property-disallowed-list - border-bottom-right-radius: 0; - } + @include media-breakpoint-up(sm) { + display: flex; + flex-flow: row wrap; + // The child selector allows nested `.card` within `.card-group` + // to display properly. + > .card { + flex: 1 0 0; + margin-bottom: 0; + + + .card { + margin-left: 0; + border-left: 0; } - &:not(:first-child) { - @include border-start-radius(0); - - > .card-img-top, - > .card-header { - // stylelint-disable-next-line property-disallowed-list - border-top-left-radius: 0; + // Handle rounded corners + @if $enable-rounded { + &:not(:last-child) { + @include border-end-radius(0); + + > .card-img-top, + > .card-header { + // stylelint-disable-next-line property-disallowed-list + border-top-right-radius: 0; + } + > .card-img-bottom, + > .card-footer { + // stylelint-disable-next-line property-disallowed-list + border-bottom-right-radius: 0; + } } - > .card-img-bottom, - > .card-footer { - // stylelint-disable-next-line property-disallowed-list - border-bottom-left-radius: 0; + + &:not(:first-child) { + @include border-start-radius(0); + + > .card-img-top, + > .card-header { + // stylelint-disable-next-line property-disallowed-list + border-top-left-radius: 0; + } + > .card-img-bottom, + > .card-footer { + // stylelint-disable-next-line property-disallowed-list + border-bottom-left-radius: 0; + } } } } diff --git a/scss/_carousel.scss b/scss/_carousel.scss index 5ebf6b15dcb5..d70c117bf689 100644 --- a/scss/_carousel.scss +++ b/scss/_carousel.scss @@ -1,3 +1,48 @@ +@use "config" as *; +@use "colors" as *; +@use "variables" as *; +@use "mixins/transition" as *; +@use "mixins/gradients" as *; +@use "mixins/color-mode" as *; +@use "vendor/rfs" as *; + +// scss-docs-start carousel-variables +$carousel-control-color: $white !default; +$carousel-control-width: 15% !default; +$carousel-control-opacity: .5 !default; +$carousel-control-hover-opacity: .9 !default; +$carousel-control-transition: opacity .15s ease !default; +$carousel-control-icon-filter: null !default; + +$carousel-indicator-width: 30px !default; +$carousel-indicator-height: 3px !default; +$carousel-indicator-hit-area-height: 10px !default; +$carousel-indicator-spacer: 3px !default; +$carousel-indicator-opacity: .5 !default; +$carousel-indicator-active-bg: $white !default; +$carousel-indicator-active-opacity: 1 !default; +$carousel-indicator-transition: opacity .6s ease !default; + +$carousel-caption-width: 70% !default; +$carousel-caption-color: $white !default; +$carousel-caption-padding-y: 1.25rem !default; +$carousel-caption-spacer: 1.25rem !default; + +$carousel-control-icon-width: 2rem !default; + +$carousel-control-prev-icon-bg: url("data:image/svg+xml,") !default; +$carousel-control-next-icon-bg: url("data:image/svg+xml,") !default; + +$carousel-transition-duration: .6s !default; +$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`) +// scss-docs-end carousel-variables + +// scss-docs-start carousel-dark-variables +$carousel-indicator-active-bg-dark: $black !default; +$carousel-caption-color-dark: $black !default; +$carousel-control-icon-filter-dark: invert(1) grayscale(100) !default; +// scss-docs-end carousel-dark-variables + // Notes on the classes: // // 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically) @@ -11,216 +56,218 @@ // 5. .carousel-item-next.carousel-item-start and .carousel-item-prev.carousel-item-end // is the upcoming slide in transition. -.carousel { - position: relative; -} - -.carousel.pointer-event { - touch-action: pan-y; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; - @include clearfix(); -} - -.carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - backface-visibility: hidden; - @include transition($carousel-transition); -} - -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; -} - -.carousel-item-next:not(.carousel-item-start), -.active.carousel-item-end { - transform: translateX(100%); -} - -.carousel-item-prev:not(.carousel-item-end), -.active.carousel-item-start { - transform: translateX(-100%); -} +@layer components { + .carousel { + position: relative; + } + .carousel.pointer-event { + touch-action: pan-y; + } -// -// Alternate transitions -// + .carousel-inner { + position: relative; + display: flow-root; + width: 100%; + overflow: hidden; + } -.carousel-fade { .carousel-item { - opacity: 0; - transition-property: opacity; - transform: none; + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + backface-visibility: hidden; + @include transition($carousel-transition); } .carousel-item.active, - .carousel-item-next.carousel-item-start, - .carousel-item-prev.carousel-item-end { - z-index: 1; - opacity: 1; + .carousel-item-next, + .carousel-item-prev { + display: block; } - .active.carousel-item-start, + .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { - z-index: 0; - opacity: 0; - @include transition(opacity 0s $carousel-transition-duration); + transform: translateX(100%); } -} + .carousel-item-prev:not(.carousel-item-end), + .active.carousel-item-start { + transform: translateX(-100%); + } -// -// Left/right controls for nav -// -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - // Use flex for alignment (1-3) - display: flex; // 1. allow flex styles - align-items: center; // 2. vertically center contents - justify-content: center; // 3. horizontally center contents - width: $carousel-control-width; - padding: 0; - color: $carousel-control-color; - text-align: center; - background: none; - filter: var(--#{$prefix}carousel-control-icon-filter); - border: 0; - opacity: $carousel-control-opacity; - @include transition($carousel-control-transition); - - // Hover/focus state - &:hover, - &:focus { - color: $carousel-control-color; - text-decoration: none; - outline: 0; - opacity: $carousel-control-hover-opacity; + // + // Alternate transitions + // + + .carousel-fade { + .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; + } + + .carousel-item.active, + .carousel-item-next.carousel-item-start, + .carousel-item-prev.carousel-item-end { + z-index: 1; + opacity: 1; + } + + .active.carousel-item-start, + .active.carousel-item-end { + z-index: 0; + opacity: 0; + @include transition(opacity 0s $carousel-transition-duration); + } } -} -.carousel-control-prev { - left: 0; - background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null); -} -.carousel-control-next { - right: 0; - background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null); -} -// Icons for within -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: $carousel-control-icon-width; - height: $carousel-control-icon-width; - background-repeat: no-repeat; - background-position: 50%; - background-size: 100% 100%; -} -.carousel-control-prev-icon { - background-image: escape-svg($carousel-control-prev-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-next-icon-bg) + "*/"}; -} -.carousel-control-next-icon { - background-image: escape-svg($carousel-control-next-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-prev-icon-bg) + "*/"}; -} + // + // Left/right controls for nav + // -// Optional indicator pips/controls -// -// Add a container (such as a list) with the following class and add an item (ideally a focusable control, -// like a button) with data-bs-target for each slide your carousel holds. - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 2; - display: flex; - justify-content: center; - padding: 0; - // Use the .carousel-control's width as margin so we don't overlay those - margin-right: $carousel-control-width; - margin-bottom: 1rem; - margin-left: $carousel-control-width; - - [data-bs-target] { - box-sizing: content-box; - flex: 0 1 auto; - width: $carousel-indicator-width; - height: $carousel-indicator-height; + .carousel-control-prev, + .carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + // Use flex for alignment (1-3) + display: flex; // 1. allow flex styles + align-items: center; // 2. vertically center contents + justify-content: center; // 3. horizontally center contents + width: $carousel-control-width; padding: 0; - margin-right: $carousel-indicator-spacer; - margin-left: $carousel-indicator-spacer; - text-indent: -999px; - cursor: pointer; - background-color: var(--#{$prefix}carousel-indicator-active-bg); - background-clip: padding-box; + color: $carousel-control-color; + text-align: center; + background: none; + filter: var(--#{$prefix}carousel-control-icon-filter); border: 0; - // Use transparent borders to increase the hit area by 10px on top and bottom. - border-top: $carousel-indicator-hit-area-height solid transparent; - border-bottom: $carousel-indicator-hit-area-height solid transparent; - opacity: $carousel-indicator-opacity; - @include transition($carousel-indicator-transition); + opacity: $carousel-control-opacity; + @include transition($carousel-control-transition); + + // Hover/focus state + &:hover, + &:focus { + color: $carousel-control-color; + text-decoration: none; + outline: 0; + opacity: $carousel-control-hover-opacity; + } + } + .carousel-control-prev { + left: 0; + background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null); + } + .carousel-control-next { + right: 0; + background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null); } - .active { - opacity: $carousel-indicator-active-opacity; + // Icons for within + .carousel-control-prev-icon, + .carousel-control-next-icon { + display: inline-block; + width: $carousel-control-icon-width; + height: $carousel-control-icon-width; + background-repeat: no-repeat; + background-position: 50%; + background-size: 100% 100%; } -} + .carousel-control-prev-icon { + background-image: escape-svg($carousel-control-prev-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-next-icon-bg) + "*/"}; + } + .carousel-control-next-icon { + background-image: escape-svg($carousel-control-next-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-prev-icon-bg) + "*/"}; + } -// Optional captions -// -// + // Optional indicator pips/controls + // + // Add a container (such as a list) with the following class and add an item (ideally a focusable control, + // like a button) with data-bs-target for each slide your carousel holds. -.carousel-caption { - position: absolute; - right: (100% - $carousel-caption-width) * .5; - bottom: $carousel-caption-spacer; - left: (100% - $carousel-caption-width) * .5; - padding-top: $carousel-caption-padding-y; - padding-bottom: $carousel-caption-padding-y; - color: var(--#{$prefix}carousel-caption-color); - text-align: center; -} + .carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 2; + display: flex; + justify-content: center; + padding: 0; + // Use the .carousel-control's width as margin so we don't overlay those + margin-right: $carousel-control-width; + margin-bottom: 1rem; + margin-left: $carousel-control-width; -// Dark mode carousel + [data-bs-target] { + box-sizing: content-box; + flex: 0 1 auto; + width: $carousel-indicator-width; + height: $carousel-indicator-height; + padding: 0; + margin-right: $carousel-indicator-spacer; + margin-left: $carousel-indicator-spacer; + text-indent: -999px; + cursor: pointer; + background-color: var(--#{$prefix}carousel-indicator-active-bg); + background-clip: padding-box; + border: 0; + // Use transparent borders to increase the hit area by 10px on top and bottom. + border-top: $carousel-indicator-hit-area-height solid transparent; + border-bottom: $carousel-indicator-hit-area-height solid transparent; + opacity: $carousel-indicator-opacity; + @include transition($carousel-indicator-transition); + } -@mixin carousel-dark() { - --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark}; - --#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark}; - --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark}; -} + .active { + opacity: $carousel-indicator-active-opacity; + } + } -.carousel-dark { - @include carousel-dark(); -} -:root, -[data-bs-theme="light"] { - --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg}; - --#{$prefix}carousel-caption-color: #{$carousel-caption-color}; - --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter}; -} + // Optional captions + // + // + + .carousel-caption { + position: absolute; + right: (100% - $carousel-caption-width) * .5; + bottom: $carousel-caption-spacer; + left: (100% - $carousel-caption-width) * .5; + padding-top: $carousel-caption-padding-y; + padding-bottom: $carousel-caption-padding-y; + color: var(--#{$prefix}carousel-caption-color); + text-align: center; + } + + // Dark mode carousel + + @mixin carousel-dark() { + --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark}; + --#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark}; + --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark}; + } -@if $enable-dark-mode { - @include color-mode(dark, true) { + .carousel-dark { @include carousel-dark(); } + + :root, + [data-bs-theme="light"] { + --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg}; + --#{$prefix}carousel-caption-color: #{$carousel-caption-color}; + --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter}; + } + + @if $enable-dark-mode { + @include color-mode(dark, true) { + @include carousel-dark(); + } + } } diff --git a/scss/_close.scss b/scss/_close.scss deleted file mode 100644 index d53c96fbff66..000000000000 --- a/scss/_close.scss +++ /dev/null @@ -1,66 +0,0 @@ -// Transparent background and border properties included for button version. -// iOS requires the button element instead of an anchor tag. -// If you want the anchor version, it requires `href="#"`. -// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile - -.btn-close { - // scss-docs-start close-css-vars - --#{$prefix}btn-close-color: #{$btn-close-color}; - --#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg) }; - --#{$prefix}btn-close-opacity: #{$btn-close-opacity}; - --#{$prefix}btn-close-hover-opacity: #{$btn-close-hover-opacity}; - --#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow}; - --#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity}; - --#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity}; - // scss-docs-end close-css-vars - - box-sizing: content-box; - width: $btn-close-width; - height: $btn-close-height; - padding: $btn-close-padding-y $btn-close-padding-x; - color: var(--#{$prefix}btn-close-color); - background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements - filter: var(--#{$prefix}btn-close-filter); - border: 0; // for button elements - @include border-radius(); - opacity: var(--#{$prefix}btn-close-opacity); - - // Override
's hover style - &:hover { - color: var(--#{$prefix}btn-close-color); - text-decoration: none; - opacity: var(--#{$prefix}btn-close-hover-opacity); - } - - &:focus { - outline: 0; - box-shadow: var(--#{$prefix}btn-close-focus-shadow); - opacity: var(--#{$prefix}btn-close-focus-opacity); - } - - &:disabled, - &.disabled { - pointer-events: none; - user-select: none; - opacity: var(--#{$prefix}btn-close-disabled-opacity); - } -} - -@mixin btn-close-white() { - --#{$prefix}btn-close-filter: #{$btn-close-filter-dark}; -} - -.btn-close-white { - @include btn-close-white(); -} - -:root, -[data-bs-theme="light"] { - --#{$prefix}btn-close-filter: #{$btn-close-filter}; -} - -@if $enable-dark-mode { - @include color-mode(dark, true) { - @include btn-close-white(); - } -} diff --git a/scss/_colors.scss b/scss/_colors.scss new file mode 100644 index 000000000000..2030a7194626 --- /dev/null +++ b/scss/_colors.scss @@ -0,0 +1,61 @@ +@use "config" as *; + +// stylelint-disable hue-degree-notation, @stylistic/number-leading-zero + +// Easily convert colors to oklch() with https://oklch.com/ +$new-blue: oklch(60% 0.24 258) !default; +$new-indigo: oklch(56% 0.26 288) !default; +$new-violet: oklch(56% 0.24 300) !default; +$new-purple: oklch(56% 0.24 320) !default; +$new-pink: oklch(60% 0.22 4) !default; +$new-red: oklch(60% 0.22 20) !default; +$new-orange: oklch(70% 0.22 52) !default; +$new-amber: oklch(79% 0.2 78) !default; +$new-yellow: oklch(88% 0.24 88) !default; +$new-lime: oklch(65% 0.24 135) !default; +$new-green: oklch(64% 0.22 160) !default; +$new-teal: oklch(68% 0.22 190) !default; +$new-cyan: oklch(69% 0.22 220) !default; +$new-brown: oklch(60% 0.12 54) !default; +$new-gray: oklch(60% 0.02 245) !default; +$new-pewter: oklch(65% 0.01 290) !default; + +$hues: ( + "blue": $new-blue, + "indigo": $new-indigo, + "violet": $new-violet, + "purple": $new-purple, + "pink": $new-pink, + "red": $new-red, + "orange": $new-orange, + "amber": $new-amber, + "yellow": $new-yellow, + "lime": $new-lime, + "green": $new-green, + "teal": $new-teal, + "cyan": $new-cyan, + "brown": $new-brown, + "gray": $new-gray, + "pewter": $new-pewter +) !default; + +:root { + @each $color, $hue in $hues { + --#{$prefix}#{$color}-025: color-mix(in srgb, #fff 94%, #{$hue}); + --#{$prefix}#{$color}-050: color-mix(in srgb, #fff 90%, #{$hue}); + --#{$prefix}#{$color}-100: color-mix(in srgb, #fff 80%, #{$hue}); + --#{$prefix}#{$color}-200: color-mix(in srgb, #fff 60%, #{$hue}); + --#{$prefix}#{$color}-300: color-mix(in srgb, #fff 40%, #{$hue}); + --#{$prefix}#{$color}-400: color-mix(in srgb, #fff 20%, #{$hue}); + --#{$prefix}#{$color}-500: #{$hue}; + --#{$prefix}#{$color}-600: color-mix(in srgb, #000 16%, #{$hue}); + --#{$prefix}#{$color}-700: color-mix(in srgb, #000 32%, #{$hue}); + --#{$prefix}#{$color}-800: color-mix(in srgb, #000 48%, #{$hue}); + --#{$prefix}#{$color}-900: color-mix(in srgb, #000 64%, #{$hue}); + --#{$prefix}#{$color}-950: color-mix(in srgb, #000 76%, #{$hue}); + --#{$prefix}#{$color}-975: color-mix(in srgb, #000 88%, #{$hue}); + } +} + +$white: #fff !default; +$black: #000 !default; diff --git a/scss/_config.scss b/scss/_config.scss new file mode 100644 index 000000000000..0bc966bc7685 --- /dev/null +++ b/scss/_config.scss @@ -0,0 +1,99 @@ +// Configuration +// +// Variables and settings not related to theme, components, and more go here. It does include layout. + +$prefix: bs- !default; + +$enable-caret: true !default; +$enable-rounded: true !default; +$enable-shadows: false !default; +$enable-gradients: false !default; +$enable-transitions: true !default; +$enable-reduced-motion: true !default; +$enable-smooth-scroll: true !default; +$enable-grid-classes: true !default; +$enable-container-classes: true !default; +$enable-cssgrid: true !default; +$enable-button-pointers: true !default; +$enable-rfs: true !default; +$enable-validation-icons: true !default; +$enable-negative-margins: false !default; +$enable-deprecation-messages: true !default; + +$enable-dark-mode: true !default; +$color-mode-type: data !default; // `data` or `media-query` + +// more to come here… + +$color-mode-type: "media-query" !default; +$color-contrast-dark: #000 !default; +$color-contrast-light: #fff !default; +$min-contrast-ratio: 4.5 !default; + +// scss-docs-start spacer-variables-maps +$spacer: 1rem !default; +$spacers: ( + 0: 0, + 1: $spacer * .25, + 2: $spacer * .5, + 3: $spacer, + 4: $spacer * 1.5, + 5: $spacer * 3, +) !default; +// scss-docs-end spacer-variables-maps + +// Grid breakpoints +// +// Define the minimum dimensions at which your layout will change, +// adapting to different screen sizes, for use in media queries. + +// scss-docs-start grid-breakpoints +$grid-breakpoints: ( + xs: 0, + sm: 576px, + md: 768px, + lg: 1024px, + xl: 1280px, + 2xl: 1536px +) !default; +// scss-docs-end grid-breakpoints + +// @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); +// @include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints"); + +// Grid columns +// +// Set the number of columns and specify the width of the gutters. + +$grid-columns: 12 !default; +$grid-gutter-width: 1.5rem !default; +$grid-row-columns: 6 !default; + +$gutters: $spacers !default; + +// Grid containers +// +// Define the maximum width of `.container` for different screen sizes. + +// scss-docs-start container-max-widths +$container-max-widths: ( + sm: 540px, + md: 720px, + lg: 960px, + xl: 1200px, + 2xl: 1440px +) !default; +// scss-docs-end container-max-widths + +$container-padding-x: $grid-gutter-width !default; + +$utilities: () !default; + +// Characters which are escaped by the escape-svg function +$escaped-characters: ( + ("<", "%3c"), + (">", "%3e"), + ("#", "%23"), + ("(", "%28"), + (")", "%29"), +) !default; diff --git a/scss/_containers.scss b/scss/_containers.scss deleted file mode 100644 index 83b31381bf89..000000000000 --- a/scss/_containers.scss +++ /dev/null @@ -1,41 +0,0 @@ -// Container widths -// -// Set the container width, and override it for fixed navbars in media queries. - -@if $enable-container-classes { - // Single container class with breakpoint max-widths - .container, - // 100% wide container at all breakpoints - .container-fluid { - @include make-container(); - } - - // Responsive containers that are 100% wide until a breakpoint - @each $breakpoint, $container-max-width in $container-max-widths { - .container-#{$breakpoint} { - @extend .container-fluid; - } - - @include media-breakpoint-up($breakpoint, $grid-breakpoints) { - %responsive-container-#{$breakpoint} { - max-width: $container-max-width; - } - - // Extend each breakpoint which is smaller or equal to the current breakpoint - $extend-breakpoint: true; - - @each $name, $width in $grid-breakpoints { - @if ($extend-breakpoint) { - .container#{breakpoint-infix($name, $grid-breakpoints)} { - @extend %responsive-container-#{$breakpoint}; - } - - // Once the current breakpoint is reached, stop extending - @if ($breakpoint == $name) { - $extend-breakpoint: false; - } - } - } - } - } -} diff --git a/scss/_dropdown.scss b/scss/_dropdown.scss index 587ebb487cf4..1f212b2bcf9e 100644 --- a/scss/_dropdown.scss +++ b/scss/_dropdown.scss @@ -1,250 +1,312 @@ -// The dropdown wrapper (`
`) -.dropup, -.dropend, -.dropdown, -.dropstart, -.dropup-center, -.dropdown-center { - position: relative; -} +@use "sass:map"; +@use "config" as *; +@use "colors" as *; +@use "variables" as *; +@use "mixins/caret" as *; +@use "mixins/border-radius" as *; +@use "mixins/box-shadow" as *; +@use "mixins/gradients" as *; +@use "mixins/caret" as *; +@use "vendor/rfs" as *; +@use "layout/breakpoints" as *; -.dropdown-toggle { - white-space: nowrap; +// scss-docs-start dropdown-variables +$dropdown-min-width: 10rem !default; +$dropdown-padding-x: 0 !default; +$dropdown-padding-y: .5rem !default; +$dropdown-spacer: .125rem !default; +$dropdown-font-size: $font-size-base !default; +$dropdown-color: var(--#{$prefix}body-color) !default; +$dropdown-bg: var(--#{$prefix}body-bg) !default; +$dropdown-border-color: var(--#{$prefix}border-color-translucent) !default; +$dropdown-border-radius: var(--#{$prefix}border-radius) !default; +$dropdown-border-width: var(--#{$prefix}border-width) !default; +$dropdown-inner-border-radius: calc(#{$dropdown-border-radius} - #{$dropdown-border-width}) !default; +$dropdown-divider-bg: $dropdown-border-color !default; +$dropdown-divider-margin-y: $spacer * .5 !default; +$dropdown-box-shadow: var(--#{$prefix}box-shadow) !default; - // Generate the caret automatically - @include caret(); -} +$dropdown-link-color: var(--#{$prefix}body-color) !default; +$dropdown-link-hover-color: $dropdown-link-color !default; +$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg) !default; + +$dropdown-link-active-color: $component-active-color !default; +$dropdown-link-active-bg: $component-active-bg !default; + +$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color) !default; + +$dropdown-item-padding-y: $spacer * .25 !default; +$dropdown-item-padding-x: $spacer !default; + +$dropdown-header-color: var(--#{$prefix}gray-600) !default; +$dropdown-header-padding-x: $dropdown-item-padding-x !default; +$dropdown-header-padding-y: $dropdown-padding-y !default; +// scss-docs-end dropdown-variables + +// scss-docs-start dropdown-dark-variables +$dropdown-dark-color: var(--#{$prefix}gray-300) !default; +$dropdown-dark-bg: var(--#{$prefix}gray-800) !default; +$dropdown-dark-border-color: $dropdown-border-color !default; +$dropdown-dark-divider-bg: $dropdown-divider-bg !default; +$dropdown-dark-box-shadow: null !default; +$dropdown-dark-link-color: $dropdown-dark-color !default; +$dropdown-dark-link-hover-color: $white !default; +$dropdown-dark-link-hover-bg: rgba($white, .15) !default; +$dropdown-dark-link-active-color: $dropdown-link-active-color !default; +$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default; +$dropdown-dark-link-disabled-color: var(--#{$prefix}gray-500) !default; +$dropdown-dark-header-color: var(--#{$prefix}gray-500) !default; +// scss-docs-end dropdown-dark-variables -// The dropdown menu -.dropdown-menu { - // scss-docs-start dropdown-css-vars - --#{$prefix}dropdown-zindex: #{$zindex-dropdown}; - --#{$prefix}dropdown-min-width: #{$dropdown-min-width}; - --#{$prefix}dropdown-padding-x: #{$dropdown-padding-x}; - --#{$prefix}dropdown-padding-y: #{$dropdown-padding-y}; - --#{$prefix}dropdown-spacer: #{$dropdown-spacer}; - @include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size); - --#{$prefix}dropdown-color: #{$dropdown-color}; - --#{$prefix}dropdown-bg: #{$dropdown-bg}; - --#{$prefix}dropdown-border-color: #{$dropdown-border-color}; - --#{$prefix}dropdown-border-radius: #{$dropdown-border-radius}; - --#{$prefix}dropdown-border-width: #{$dropdown-border-width}; - --#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius}; - --#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg}; - --#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y}; - --#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow}; - --#{$prefix}dropdown-link-color: #{$dropdown-link-color}; - --#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color}; - --#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg}; - --#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color}; - --#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg}; - --#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color}; - --#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x}; - --#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y}; - --#{$prefix}dropdown-header-color: #{$dropdown-header-color}; - --#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x}; - --#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y}; - // scss-docs-end dropdown-css-vars - - position: absolute; - z-index: var(--#{$prefix}dropdown-zindex); - display: none; // none by default, but block on "open" of the menu - min-width: var(--#{$prefix}dropdown-min-width); - padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x); - margin: 0; // Override default margin of ul - @include font-size(var(--#{$prefix}dropdown-font-size)); - color: var(--#{$prefix}dropdown-color); - text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) - list-style: none; - background-color: var(--#{$prefix}dropdown-bg); - background-clip: padding-box; - border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color); - @include border-radius(var(--#{$prefix}dropdown-border-radius)); - @include box-shadow(var(--#{$prefix}dropdown-box-shadow)); - - &[data-bs-popper] { - top: 100%; - left: 0; - margin-top: var(--#{$prefix}dropdown-spacer); +@layer components { + // The dropdown wrapper (`
`) + .dropup, + .dropend, + .dropdown, + .dropstart, + .dropup-center, + .dropdown-center { + position: relative; } - @if $dropdown-padding-y == 0 { - > .dropdown-item:first-child, - > li:first-child .dropdown-item { - @include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius)); - } - > .dropdown-item:last-child, - > li:last-child .dropdown-item { - @include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius)); - } + .dropdown-toggle { + white-space: nowrap; + // Generate the caret automatically + @include caret(); } -} -// scss-docs-start responsive-breakpoints -// We deliberately hardcode the `bs-` prefix because we check -// this custom property in JS to determine Popper's positioning + // The dropdown menu + .dropdown-menu { + // scss-docs-start dropdown-css-vars + --#{$prefix}dropdown-zindex: #{$zindex-dropdown}; + --#{$prefix}dropdown-min-width: #{$dropdown-min-width}; + --#{$prefix}dropdown-padding-x: #{$dropdown-padding-x}; + --#{$prefix}dropdown-padding-y: #{$dropdown-padding-y}; + --#{$prefix}dropdown-spacer: #{$dropdown-spacer}; + @include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size); + --#{$prefix}dropdown-color: #{$dropdown-color}; + --#{$prefix}dropdown-bg: #{$dropdown-bg}; + --#{$prefix}dropdown-border-color: #{$dropdown-border-color}; + --#{$prefix}dropdown-border-radius: #{$dropdown-border-radius}; + --#{$prefix}dropdown-border-width: #{$dropdown-border-width}; + --#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius}; + --#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg}; + --#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y}; + --#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow}; + --#{$prefix}dropdown-link-color: #{$dropdown-link-color}; + --#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color}; + --#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg}; + --#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color}; + --#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg}; + --#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color}; + --#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x}; + --#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y}; + --#{$prefix}dropdown-header-color: #{$dropdown-header-color}; + --#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x}; + --#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y}; + // scss-docs-end dropdown-css-vars -@each $breakpoint in map-keys($grid-breakpoints) { - @include media-breakpoint-up($breakpoint) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); + position: absolute; + z-index: var(--#{$prefix}dropdown-zindex); + display: none; // none by default, but block on "open" of the menu + min-width: var(--#{$prefix}dropdown-min-width); + padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x); + margin: 0; // Override default margin of ul + @include font-size(var(--#{$prefix}dropdown-font-size)); + color: var(--#{$prefix}dropdown-color); + text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) + list-style: none; + background-color: var(--#{$prefix}dropdown-bg); + background-clip: padding-box; + border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color); + @include border-radius(var(--#{$prefix}dropdown-border-radius)); + @include box-shadow(var(--#{$prefix}dropdown-box-shadow)); - .dropdown-menu#{$infix}-start { - --bs-position: start; + &[data-bs-popper] { + top: 100%; + left: 0; + margin-top: var(--#{$prefix}dropdown-spacer); + } - &[data-bs-popper] { - right: auto; - left: 0; + @if $dropdown-padding-y == 0 { + > .dropdown-item:first-child, + > li:first-child .dropdown-item { + @include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius)); + } + > .dropdown-item:last-child, + > li:last-child .dropdown-item { + @include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius)); } + } + } + + // scss-docs-start responsive-breakpoints + // We deliberately hardcode the `bs-` prefix because we check + // this custom property in JS to determine Popper's positioning - .dropdown-menu#{$infix}-end { - --bs-position: end; + @each $breakpoint in map.keys($grid-breakpoints) { + @include media-breakpoint-up($breakpoint) { + $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - &[data-bs-popper] { - right: 0; - left: auto; + .dropdown-menu#{$infix}-start { + --bs-position: start; + + &[data-bs-popper] { + right: auto; + left: 0; + } + } + + .dropdown-menu#{$infix}-end { + --bs-position: end; + + &[data-bs-popper] { + right: 0; + left: auto; + } } } } -} -// scss-docs-end responsive-breakpoints - -// Allow for dropdowns to go bottom up (aka, dropup-menu) -// Just add .dropup after the standard .dropdown class and you're set. -.dropup { - .dropdown-menu[data-bs-popper] { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: var(--#{$prefix}dropdown-spacer); - } + // scss-docs-end responsive-breakpoints - .dropdown-toggle { - @include caret(up); - } -} + // Allow for dropdowns to go bottom up (aka, dropup-menu) + // Just add .dropup after the standard .dropdown class and you're set. + .dropup { + .dropdown-menu[data-bs-popper] { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: var(--#{$prefix}dropdown-spacer); + } -.dropend { - .dropdown-menu[data-bs-popper] { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: var(--#{$prefix}dropdown-spacer); + .dropdown-toggle { + @include caret(up); + } } - .dropdown-toggle { - @include caret(end); - &::after { - vertical-align: 0; + .dropend { + .dropdown-menu[data-bs-popper] { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: var(--#{$prefix}dropdown-spacer); } - } -} -.dropstart { - .dropdown-menu[data-bs-popper] { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: var(--#{$prefix}dropdown-spacer); + .dropdown-toggle { + @include caret(end); + &::after { + vertical-align: 0; + } + } } - .dropdown-toggle { - @include caret(start); - &::before { - vertical-align: 0; + .dropstart { + .dropdown-menu[data-bs-popper] { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: var(--#{$prefix}dropdown-spacer); } - } -} + .dropdown-toggle { + @include caret(start); + &::before { + vertical-align: 0; + } + } + } -// Dividers (basically an `
`) within the dropdown -.dropdown-divider { - height: 0; - margin: var(--#{$prefix}dropdown-divider-margin-y) 0; - overflow: hidden; - border-top: 1px solid var(--#{$prefix}dropdown-divider-bg); - opacity: 1; // Revisit in v6 to de-dupe styles that conflict with
element -} -// Links, buttons, and more within the dropdown menu -// -// `
diff --git a/site/src/assets/examples/grid/index.astro b/site/src/assets/examples/grid/index.astro index 2c01d8de9dbf..499e887fe206 100644 --- a/site/src/assets/examples/grid/index.astro +++ b/site/src/assets/examples/grid/index.astro @@ -45,9 +45,9 @@ export const body_class = 'py-4'
-
.col-xxl-4
-
.col-xxl-4
-
.col-xxl-4
+
.col-2xl-4
+
.col-2xl-4
+
.col-2xl-4

Three equal columns

@@ -107,8 +107,8 @@ export const body_class = 'py-4'

Mixed: mobile and desktop

-

The Bootstrap v5 grid system has six tiers of classes: xs (extra small, this class infix is not used), sm (small), md (medium), lg (large), xl (x-large), and xxl (xx-large). You can use nearly any combination of these classes to create more dynamic and flexible layouts.

-

Each tier of classes scales up, meaning if you plan on setting the same widths for md, lg, xl and xxl, you only need to specify md.

+

The Bootstrap v5 grid system has six tiers of classes: xs (extra small, this class infix is not used), sm (small), md (medium), lg (large), xl (x-large), and 2xl (xx-large). You can use nearly any combination of these classes to create more dynamic and flexible layouts.

+

Each tier of classes scales up, meaning if you plan on setting the same widths for md, lg, xl and 2xl, you only need to specify md.

.col-md-8
.col-6 .col-md-4
@@ -172,7 +172,7 @@ export const body_class = 'py-4'

Containers

-

Additional classes added in Bootstrap v4.4 allow containers that are 100% wide until a particular breakpoint. v5 adds a new xxl breakpoint.

+

Additional classes added in Bootstrap v4.4 allow containers that are 100% wide until a particular breakpoint. v5 adds a new 2xl breakpoint.

.container
@@ -180,6 +180,6 @@ export const body_class = 'py-4'
.container-md
.container-lg
.container-xl
-
.container-xxl
+
.container-2xl
.container-fluid
diff --git a/site/src/assets/examples/heroes/index.astro b/site/src/assets/examples/heroes/index.astro index 853776e7bc90..8ac2a70ac38d 100644 --- a/site/src/assets/examples/heroes/index.astro +++ b/site/src/assets/examples/heroes/index.astro @@ -40,7 +40,7 @@ export const extra_css = ['heroes.css']
-
+
Bootstrap Themes @@ -58,7 +58,7 @@ export const extra_css = ['heroes.css']
-
+

Vertically centered hero sign-up form

diff --git a/site/src/assets/examples/navbars/index.astro b/site/src/assets/examples/navbars/index.astro index 63d65ffb4c50..43c73f87ca3f 100644 --- a/site/src/assets/examples/navbars/index.astro +++ b/site/src/assets/examples/navbars/index.astro @@ -199,14 +199,14 @@ export const extra_css = ['navbars.css']
-
-
- - -
-
- - -
`} /> - -## Inline - -Group checkboxes or radios on the same horizontal row by adding `.form-check-inline` to any `.form-check`. - - - - -
-
- - -
-
- - -
`} /> - - - - -
-
- - -
-
- - -
`} /> - -## Reverse - -Put your checkboxes, radios, and switches on the opposite side with the `.form-check-reverse` modifier class. - - - - -
-
- - -
- -
- - -
`} /> - -## Without labels - -Omit the wrapping `.form-check` for checkboxes and radios that have no label text. Remember to still provide some form of accessible name for assistive technologies (for instance, using `aria-label`). See the [forms overview accessibility]([[docsref:/forms/overview/#accessibility]]) section for details. - - - -
- -
- -
`} /> - -## Toggle buttons - -Create button-like checkboxes and radio buttons by using `.btn` styles rather than `.form-check-label` on the `
`} /> ## Sizing -Set heights using classes like `.form-control-lg` and `.form-control-sm`. +Change the size of a form control by using classes like `.form-control-lg` and `.form-control-sm`. This adjusts `height`, `padding`, `font-size`, `line-height`, and `border-radius`. + + + + +
+
+ + +
+
+ + +
`} /> + + + + +
+
+ + +
+
+ + +
`} /> + +## Select + +The `multiple` attribute is supported on select elements: + + + + + + + `} /> + +As is the `size` attribute: - - -`} /> + + + + + + `} /> ## Form text @@ -62,7 +127,13 @@ Inline text can use any typical inline HTML element (be it a ``, `` Add the `disabled` boolean attribute on an input to give it a grayed out appearance, remove pointer events, and prevent focusing. -`} /> + +`} /> ## Readonly @@ -153,14 +224,14 @@ Learn more about [support for datalist elements](https://caniuse.com/datalist). `$input-*` are shared across most of our form controls (and not buttons). - + `$form-label-*` and `$form-text-*` are for our `
`} /> + +## Label + +Wrap the `.switch` in a `` layout component and add your label. We use a custom element for layout here that sets some basic flexbox styling. Switches are checkboxes under the hood, so we reused the same custom element here. + +Consider margin utilities for additional spacing, and flex utilities for alignment, especially when using small or large switches. + + +
+ +
+ +
+ + +
+ +
+ +
`} /> + +## Theme colors + +Modify the appearance of checked checkboxes by adding the `.checked-{color}` class to the `.switch` element. This will set the checked background and border color to the theme color. + + ` +
+ +
+ +
`)} /> + +## Disabled + +Add the `disabled` attribute and the associated `
+
+ +
+
+ +
`} /> + +## CSS + +### Variables + + diff --git a/site/src/content/docs/forms/validation.mdx b/site/src/content/docs/forms/validation.mdx index cf6da63bd845..90738e7d8826 100644 --- a/site/src/content/docs/forms/validation.mdx +++ b/site/src/content/docs/forms/validation.mdx @@ -339,8 +339,6 @@ If your form layout allows it, you can swap the `.{valid|invalid}-feedback` clas ### Variables - - As part of Bootstrap’s evolving CSS variables approach, forms now use local CSS variables for validation for enhanced real-time customization. Values for the CSS variables are set via Sass, so Sass customization is still supported, too. @@ -349,23 +347,23 @@ These variables are also color mode adaptive, meaning they change color while in ### Sass variables - + - + - +{/* */} ### Sass mixins Two mixins are combined, through our [loop](#sass-loops), to generate our form validation feedback styles. - + ### Sass maps This is the validation Sass map from `_variables.scss`. Override or extend this to generate different or additional states. - + Maps of `$form-validation-states` can contain three optional parameters to override tooltips and focus styles. diff --git a/site/src/content/docs/getting-started/contribute.mdx b/site/src/content/docs/getting-started/contribute.mdx index e1af5dc2934f..23213b860b7b 100644 --- a/site/src/content/docs/getting-started/contribute.mdx +++ b/site/src/content/docs/getting-started/contribute.mdx @@ -3,9 +3,6 @@ title: Contribute description: Help develop Bootstrap with our documentation build scripts and tests. toc: true aliases: "/docs/[[config:docs_version]]/getting-started/build-tools/" -added: - show_badge: false - version: "5.1" --- ## Tooling setup diff --git a/site/src/content/docs/getting-started/javascript.mdx b/site/src/content/docs/getting-started/javascript.mdx index 5a54d290dcbe..81177a074aee 100644 --- a/site/src/content/docs/getting-started/javascript.mdx +++ b/site/src/content/docs/getting-started/javascript.mdx @@ -263,47 +263,6 @@ const tooltip = new bootstrap.Tooltip(yourTooltipEl, { }) ``` -## Optionally using jQuery - -**You don’t need jQuery in Bootstrap 5**, but it’s still possible to use our components with jQuery. If Bootstrap detects `jQuery` in the `window` object, it'll add all of our components in jQuery’s plugin system. This allows you to do the following: - -```js -// to enable tooltips with the default configuration -$('[data-bs-toggle="tooltip"]').tooltip() - -// to initialize tooltips with given configuration -$('[data-bs-toggle="tooltip"]').tooltip({ - boundary: 'clippingParents', - customClass: 'myClass' -}) - -// to trigger the `show` method -$('#myTooltip').tooltip('show') -``` - -The same goes for our other components. - -### No conflict - -Sometimes it is necessary to use Bootstrap plugins with other UI frameworks. In these circumstances, namespace collisions can occasionally occur. If this happens, you may call `.noConflict` on the plugin you wish to revert the value of. - -```js -const bootstrapButton = $.fn.button.noConflict() // return $.fn.button to previously assigned value -$.fn.bootstrapBtn = bootstrapButton // give $().bootstrapBtn the Bootstrap functionality -``` - -Bootstrap does not officially support third-party JavaScript libraries like Prototype or jQuery UI. Despite `.noConflict` and namespaced events, there may be compatibility problems that you need to fix on your own. - -### jQuery events - -Bootstrap will detect jQuery if `jQuery` is present in the `window` object and there is no `data-bs-no-jquery` attribute set on ``. If jQuery is found, Bootstrap will emit events thanks to jQuery’s event system. So if you want to listen to Bootstrap’s events, you’ll have to use the jQuery methods (`.on`, `.one`) instead of `addEventListener`. - -```js -$('#myTab a').on('shown.bs.tab', () => { - // do something... -}) -``` - ## Disabled JavaScript Bootstrap’s plugins have no special fallback when JavaScript is disabled. If you care about the user experience in this case, use [`