Compare commits

..

No commits in common. "main" and "v5.1.3" have entirely different histories.
main ... v5.1.3

996 changed files with 69562 additions and 87671 deletions

View File

@ -9,4 +9,3 @@ Firefox ESR
iOS >= 12
Safari >= 12
not Explorer <= 11
not kaios <= 2.5 # fix floating label issues in Firefox (see https://github.com/postcss/autoprefixer/issues/1533)

View File

@ -2,47 +2,47 @@
"files": [
{
"path": "./dist/css/bootstrap-grid.css",
"maxSize": "6.5 kB"
"maxSize": "7.25 kB"
},
{
"path": "./dist/css/bootstrap-grid.min.css",
"maxSize": "6.0 kB"
"maxSize": "6.5 kB"
},
{
"path": "./dist/css/bootstrap-reboot.css",
"maxSize": "3.5 kB"
"maxSize": "2.5 kB"
},
{
"path": "./dist/css/bootstrap-reboot.min.css",
"maxSize": "3.25 kB"
"maxSize": "2.35 kB"
},
{
"path": "./dist/css/bootstrap-utilities.css",
"maxSize": "11.75 kB"
"maxSize": "7.75 kB"
},
{
"path": "./dist/css/bootstrap-utilities.min.css",
"maxSize": "10.75 kB"
"maxSize": "6.85 kB"
},
{
"path": "./dist/css/bootstrap.css",
"maxSize": "32.5 kB"
"maxSize": "25.5 kB"
},
{
"path": "./dist/css/bootstrap.min.css",
"maxSize": "30.25 kB"
"maxSize": "23.25 kB"
},
{
"path": "./dist/js/bootstrap.bundle.js",
"maxSize": "43.0 kB"
"maxSize": "42.5 kB"
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
"maxSize": "23.5 kB"
"maxSize": "22.75 kB"
},
{
"path": "./dist/js/bootstrap.esm.js",
"maxSize": "28.0 kB"
"maxSize": "27.5 kB"
},
{
"path": "./dist/js/bootstrap.esm.min.js",
@ -50,11 +50,11 @@
},
{
"path": "./dist/js/bootstrap.js",
"maxSize": "28.75 kB"
"maxSize": "28.25 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
"maxSize": "16.25 kB"
"maxSize": "16 kB"
}
],
"ci": {

View File

@ -3,31 +3,27 @@
"words": [
"affordance",
"allowfullscreen",
"Analyser",
"autohide",
"autohiding",
"autoplay",
"autoplays",
"autoplaying",
"blazingly",
"Blockquotes",
"Bootstrappers",
"borderless",
"Brotli",
"browserslist",
"browserslistrc",
"btncheck",
"btnradio",
"callout",
"callouts",
"camelCase",
"clearfix",
"Codesniffer",
"colorspace",
"combinator",
"Contentful",
"Cpath",
"Crossfade",
"crossfading",
"cssgrid",
"Csvg",
"Datalists",
@ -40,32 +36,29 @@
"dropright",
"dropstart",
"dropup",
"dgst",
"errorf",
"favicon",
"favicons",
"fieldsets",
"flexbox",
"fullscreen",
"fusv",
"getbootstrap",
"globby",
"Grayscale",
"Hoverable",
"hreflang",
"hstack",
"importmap",
"jsdelivr",
"Jumpstart",
"keyframes",
"libera",
"libman",
"Libsass",
"lightboxes",
"Lowercased",
"markdownify",
"mediaqueries",
"minifiers",
"misfunction",
"mkdir",
"monospace",
"mouseleave",
"navbars",
@ -75,9 +68,9 @@
"Noto",
"offcanvas",
"offcanvases",
"Packagist",
"popperjs",
"prebuild",
"precompiled",
"prefersreducedmotion",
"prepended",
"printf",
@ -87,18 +80,16 @@
"roboto",
"RTLCSS",
"ruleset",
"sassrc",
"screenreaders",
"scrollbars",
"scrollspy",
"Segoe",
"semibold",
"socio",
"sirv",
"srcset",
"stackblitz",
"stickied",
"Stylelint",
"subnav",
"svgo",
"tabbable",
"textareas",
"toggleable",
@ -106,26 +97,25 @@
"touchend",
"twbs",
"unitless",
"Unported",
"unstylable",
"unstyled",
"Uppercased",
"urlize",
"urlquery",
"vbtn",
"viewports",
"Vite",
"vstack",
"walkthroughs",
"WCAG",
"zindex"
"webfont",
"Woohoo",
"zindex",
"بالعالم",
"مرحبا"
],
"language": "en-US",
"language": "en,en-US",
"ignorePaths": [
".cspell.json",
"dist/",
"*.min.*",
"**/*rtl*",
"**/tests/**"
],
"useGitignore": true
".min."
]
}

View File

@ -2,8 +2,5 @@
**/dist/
**/vendor/
/_site/
/site/public/
/js/coverage/
/site/static/sw.js
/site/static/docs/**/assets/sw.js
/site/layouts/partials/

View File

@ -14,35 +14,6 @@
"error",
"never"
],
"import/extensions": [
"error",
"ignorePackages",
{
"js": "always"
}
],
"import/first": "error",
"import/newline-after-import": "error",
"import/no-absolute-path": "error",
"import/no-amd": "error",
"import/no-cycle": [
"error",
{
"ignoreExternal": true
}
],
"import/no-duplicates": "error",
"import/no-extraneous-dependencies": "error",
"import/no-mutable-exports": "error",
"import/no-named-as-default": "error",
"import/no-named-as-default-member": "error",
"import/no-named-default": "error",
"import/no-self-import": "error",
"import/no-unassigned-import": [
"error"
],
"import/no-useless-path-segments": "error",
"import/order": "error",
"indent": [
"error",
2,
@ -51,7 +22,6 @@
"SwitchCase": 1
}
],
"logical-assignment-operators": "off",
"max-params": [
"warn",
5
@ -67,7 +37,6 @@
}
],
"no-console": "error",
"no-negated-condition": "off",
"object-curly-spacing": [
"error",
"always"
@ -76,165 +45,24 @@
"error",
"after"
],
"prefer-object-has-own": "off",
"prefer-template": "error",
"semi": [
"error",
"never"
],
"strict": "error",
"unicorn/explicit-length-check": "off",
"unicorn/filename-case": "off",
"unicorn/no-anonymous-default-export": "off",
"unicorn/no-array-callback-reference": "off",
"unicorn/no-array-for-each": "off",
"unicorn/no-array-method-this-argument": "off",
"unicorn/no-for-loop": "off",
"unicorn/no-null": "off",
"unicorn/no-typeof-undefined": "off",
"unicorn/no-unused-properties": "error",
"unicorn/numeric-separators-style": "off",
"unicorn/prefer-array-flat": "off",
"unicorn/prefer-at": "off",
"unicorn/prefer-dom-node-dataset": "off",
"unicorn/prefer-module": "off",
"unicorn/prefer-prototype-methods": "off",
"unicorn/prefer-query-selector": "off",
"unicorn/prefer-spread": "off",
"unicorn/prefer-string-raw": "off",
"unicorn/prefer-string-replace-all": "off",
"unicorn/prefer-structured-clone": "off",
"unicorn/prevent-abbreviations": "off"
},
"overrides": [
{
"files": [
"build/**"
],
"env": {
"browser": false,
"node": true
},
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-console": "off",
"unicorn/prefer-top-level-await": "off"
}
},
{
"files": [
"js/**"
],
"parserOptions": {
"sourceType": "module"
}
},
{
"files": [
"js/tests/*.js",
"js/tests/integration/rollup*.js"
],
"env": {
"node": true
},
"parserOptions": {
"sourceType": "script"
}
},
{
"files": [
"js/tests/unit/**"
],
"env": {
"jasmine": true
},
"rules": {
"no-console": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/no-useless-undefined": "off",
"unicorn/prefer-add-event-listener": "off"
}
},
{
"files": [
"js/tests/visual/**"
],
"plugins": [
"html"
],
"settings": {
"html/html-extensions": [
".html"
]
},
"rules": {
"no-console": "off",
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
"files": [
"scss/tests/**"
],
"env": {
"node": true
},
"parserOptions": {
"sourceType": "script"
}
},
{
"files": [
"site/**"
],
"env": {
"browser": true,
"node": false
},
"parserOptions": {
"sourceType": "script",
"ecmaVersion": 2019
},
"rules": {
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
"files": [
"site/src/assets/application.js",
"site/src/assets/partials/*.js",
"site/src/assets/search.js",
"site/src/assets/snippets.js",
"site/src/assets/stackblitz.js",
"site/src/plugins/*.js"
],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2020
}
},
{
"files": [
"**/*.md"
],
"plugins": [
"markdown"
],
"processor": "markdown/markdown"
},
{
"files": [
"**/*.md/*.js",
"**/*.md/*.mjs"
],
"extends": "plugin:markdown/recommended-legacy",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"unicorn/prefer-node-protocol": "off"
}
}
]
}
}

View File

@ -1,6 +1,6 @@
# Contributing to Bootstrap
Looking to contribute something to Bootstrap? **Heres how you can help.**
Looking to contribute something to Bootstrap? **Here's how you can help.**
Please take a moment to review this document in order to make the contribution
process easy and effective for everyone involved.
@ -18,29 +18,24 @@ the preferred channel for [bug reports](#bug-reports), [features requests](#feat
and [submitting pull requests](#pull-requests), but please respect the following
restrictions:
- Please **do not** use the issue tracker for personal support requests. Stack Overflow ([`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag), [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions) or [IRC](/README.md#community) are better places to get help.
* Please **do not** use the issue tracker for personal support requests. Stack
Overflow ([`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag),
[Slack](https://bootstrap-slack.herokuapp.com/) or [IRC](/README.md#community) are better places to get help.
- Please **do not** derail or troll issues. Keep the discussion on topic and
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
- Please **do not** post comments consisting solely of "+1" or ":thumbsup:".
* Please **do not** post comments consisting solely of "+1" or ":thumbsup:".
Use [GitHub's "reactions" feature](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/)
instead. We reserve the right to delete comments which violate this rule.
- Please **do not** open issues regarding the official themes offered on <https://themes.getbootstrap.com/>.
* Please **do not** open issues regarding the official themes offered on <https://themes.getbootstrap.com/>.
Instead, please email any questions or feedback regarding those themes to `themes AT getbootstrap DOT com`.
## Issues assignment
The core team will be looking at the open issues, analyze them, and provide guidance on how to proceed. **Issues wont be assigned to anyone outside the core team.** However, contributors are welcome to participate in the discussion and provide their input on how to best solve the issue, and even submit a PR if they want to. Please wait that the issue is ready to be worked on before submitting a PR, we dont want to waste your time.
Please keep in mind that the core team is small, has limited resources and that we are not always able to respond immediately. We will try to provide feedback as soon as possible, but please be patient. If you dont get a response immediately, it doesnt mean that we are ignoring you or that we dont care about your issue or PR. We will get back to you as soon as we can.
## Issues and labels
Our bug tracker utilizes several labels to help organize and identify issues. Heres what they represent and how we use them:
Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them:
- `browser bug` - Issues that are reported to us, but actually are the result of a browser-specific bug. These are diagnosed with reduced test cases and result in an issue opened on that browser's own bug tracker.
- `confirmed` - Issues that have been confirmed with a reduced test case and identify a bug in Bootstrap.
@ -64,7 +59,7 @@ Good bug reports are extremely helpful, so thanks!
Guidelines for bug reports:
0. **[Validate your HTML](https://html5.validator.nu/)** to ensure your
problem isnt caused by a simple error in your own code.
problem isn't caused by a simple error in your own code.
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
@ -74,10 +69,10 @@ Guidelines for bug reports:
3. **Isolate the problem** &mdash; ideally create a [reduced test
case](https://css-tricks.com/reduced-test-cases/) and a live example.
These [v4 CodePen](https://codepen.io/team/bootstrap/pen/yLabNQL) and [v5 CodePen](https://codepen.io/team/bootstrap/pen/qBamdLj) are helpful templates.
[This JS Bin](https://jsbin.com/lolome/edit?html,output) is a helpful template.
A good bug report shouldnt leave others needing to chase you up for more
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is
your environment? What steps will reproduce the issue? What browser(s) and OS
experience the problem? Do other browsers show the bug differently? What
@ -108,17 +103,17 @@ Sometimes bugs reported to us are actually caused by bugs in the browser(s) them
| Vendor(s) | Browser(s) | Rendering engine | Bug reporting website(s) | Notes |
| ------------- | ---------------------------- | ---------------- | ------------------------------------------------------ | -------------------------------------------------------- |
| Mozilla | Firefox | Gecko | <https://bugzilla.mozilla.org/enter_bug.cgi> | "Core" is normally the right product option to choose. |
| Apple | Safari | WebKit | <https://bugs.webkit.org/enter_bug.cgi?product=WebKit> | In Apples bug reporter, choose "Safari" as the product. |
| Google, Opera | Chrome, Chromium, Opera v15+ | Blink | <https://bugs.chromium.org/p/chromium/issues/list> | Click the "New issue" button. |
| Microsoft | Edge | Blink | <https://developer.microsoft.com/en-us/microsoft-edge/> | Go to "Help > Send Feedback" from the browser |
| Mozilla | Firefox | Gecko | https://bugzilla.mozilla.org/enter_bug.cgi | "Core" is normally the right product option to choose. |
| Apple | Safari | WebKit | https://bugs.webkit.org/enter_bug.cgi?product=WebKit | In Apple's bug reporter, choose "Safari" as the product. |
| Google, Opera | Chrome, Chromium, Opera v15+ | Blink | https://bugs.chromium.org/p/chromium/issues/list | Click the "New issue" button. |
| Microsoft | Edge | Blink | https://developer.microsoft.com/en-us/microsoft-edge/ | Go to "Help > Send Feedback" from the browser |
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. Its up to _you_ to make a strong
case to convince the projects developers of the merits of this feature. Please
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
@ -131,8 +126,8 @@ commits.
**Please ask first** before embarking on any **significant** pull request (e.g.
implementing features, refactoring code, porting to a different language),
otherwise you risk spending a lot of time working on something that the
projects developers might not want to merge into the project. For trivial
things, or things that dont require a lot of your time, you can go ahead and
project's developers might not want to merge into the project. For trivial
things, or things that don't require a lot of your time, you can go ahead and
make a PR.
Please adhere to the [coding guidelines](#code-guidelines) used throughout the
@ -144,7 +139,7 @@ any dist files (`dist/` or `js/dist`).** Those files are automatically generated
edit the source files in [`/bootstrap/scss/`](https://github.com/twbs/bootstrap/tree/main/scss)
and/or [`/bootstrap/js/src/`](https://github.com/twbs/bootstrap/tree/main/js/src) instead.
Similarly, when contributing to Bootstraps documentation, you should edit the
Similarly, when contributing to Bootstrap's documentation, you should edit the
documentation source files in
[the `/bootstrap/site/content/docs/` directory of the `main` branch](https://github.com/twbs/bootstrap/tree/main/site/content/docs).
**Do not edit the `gh-pages` branch.** That branch is generated from the
@ -172,47 +167,33 @@ included in the project:
git pull upstream main
```
3. Install or update project dependencies with npm:
```bash
npm install
```
4. Create a new topic branch (off the main project development branch) to
3. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
5. Commit your changes in logical chunks. Please adhere to these [git commit
4. Commit your changes in logical chunks. Please adhere to these [git commit
message guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Gits
or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://help.github.com/articles/about-git-rebase/)
feature to tidy up your commits before making them public.
6. Ensure your changes compile the dist CSS and JS files in the `dist/` directory. Verify
the build succeeds locally without errors.
```bash
npm run dist
```
7. Locally merge (or rebase) the upstream development branch into your topic branch:
5. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream main
```
8. Commit your changes, but **do not push compiled CSS and JS files in `dist` and `js/dist`**.
Push your topic branch up to your fork:
6. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
9. [Open a pull request](https://help.github.com/articles/about-pull-requests/)
with a clear title and description against the `main` branch.
7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/)
with a clear title and description against the `main` branch.
**IMPORTANT**: By submitting a patch, you agree to allow the project owners to
license your work under the terms of the [MIT License](../LICENSE) (if it
@ -228,15 +209,15 @@ includes code changes) and under the terms of the
[Adhere to the Code Guide.](https://codeguide.co/#html)
- Use tags and elements appropriate for an HTML5 doctype (e.g., self-closing tags).
- Use CDNs and HTTPS for third-party JS when possible. We dont use protocol-relative URLs in this case because they break when viewing the page locally via `file://`.
- Use CDNs and HTTPS for third-party JS when possible. We don't use protocol-relative URLs in this case because they break when viewing the page locally via `file://`.
- Use [WAI-ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) attributes in documentation examples to promote accessibility.
### CSS
[Adhere to the Code Guide.](https://codeguide.co/#css)
- When feasible, default color palettes should comply with [WCAG color contrast guidelines](https://www.w3.org/TR/WCAG/#distinguishable).
- Except in rare cases, dont remove default `:focus` styles (via e.g. `outline: none;`) without providing alternative styles. See [this A11Y Project post](https://www.a11yproject.com/posts/2013-01-25-never-remove-css-outlines/) for more details.
- When feasible, default color palettes should comply with [WCAG color contrast guidelines](https://www.w3.org/TR/WCAG20/#visual-audio-contrast).
- Except in rare cases, don't remove default `:focus` styles (via e.g. `outline: none;`) without providing alternative styles. See [this A11Y Project post](https://www.a11yproject.com/posts/2013-01-25-never-remove-css-outlines/) for more details.
### JS
@ -255,4 +236,4 @@ Run `npm run test` before committing to ensure your changes follow our coding st
By contributing your code, you agree to license your contribution under the [MIT License](../LICENSE).
By contributing to the documentation, you agree to license your contribution under the [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/).
Prior to v3.1.0, Bootstraps code was released under the Apache License v2.0.
Prior to v3.1.0, Bootstrap's code was released under the Apache License v2.0.

View File

@ -1,38 +0,0 @@
### Description
<!-- Describe your changes in detail -->
### Motivation & Context
<!-- Why is this change required? What problem does it solve? -->
### Type of changes
<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Refactoring (non-breaking change)
- [ ] Breaking change (fix or feature that would change existing functionality)
### Checklist
<!-- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!-- If youre unsure about any of these, dont hesitate to ask. Were here to help! -->
- [ ] I have read the [contributing guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md)
- [ ] My code follows the code style of the project _(using `npm run lint`)_
- [ ] My change introduces changes to the documentation
- [ ] I have updated the documentation accordingly
- [ ] I have added tests to cover my changes
- [ ] All new and existing tests passed
#### Live previews
<!-- Please add direct links where your modifications can be seen in the documentation -->
- <https://deploy-preview-{your_pr_number}--twbs-bootstrap.netlify.app/>
### Related issues
<!-- Please link any related issues here. -->

2
.github/SUPPORT.md vendored
View File

@ -6,6 +6,6 @@ See the [contributing guidelines](CONTRIBUTING.md) for sharing bug reports.
For general troubleshooting or help getting started:
- Ask and explore [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions).
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
- Chat with fellow Bootstrappers in IRC. On the `irc.libera.chat` server, in the `#bootstrap` channel.
- Ask and explore Stack Overflow with the [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag.

View File

@ -1,3 +0,0 @@
name: "CodeQL config"
paths-ignore:
- dist

View File

@ -1,5 +1,20 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly
day: tuesday
time: "12:00"
timezone: Europe/Athens
open-pull-requests-limit: 10
reviewers:
- XhmikosR
labels:
- dependencies
- v5
versioning-strategy: increase
rebase-strategy: disabled
- package-ecosystem: "github-actions"
directory: "/"
schedule:
@ -7,20 +22,3 @@ updates:
day: tuesday
time: "12:00"
timezone: Europe/Athens
- package-ecosystem: npm
directory: "/"
labels:
- dependencies
- v5
schedule:
interval: weekly
day: tuesday
time: "12:00"
timezone: Europe/Athens
versioning-strategy: increase
rebase-strategy: disabled
groups:
production-dependencies:
dependency-type: "production"
development-dependencies:
dependency-type: "development"

View File

@ -7,9 +7,6 @@ categories:
- title: '❗ Breaking Changes'
labels:
- 'breaking-change'
- title: '🚀 Highlights'
labels:
- 'release-highlight'
- title: '🚀 Features'
labels:
- 'new-feature'

View File

@ -2,32 +2,23 @@ name: BrowserStack
on:
push:
branches:
- "**"
- "!dependabot/**"
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
browserstack:
runs-on: ubuntu-latest
if: github.repository == 'twbs/bootstrap'
if: github.repository == 'twbs/bootstrap' && (!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]'))
timeout-minutes: 30
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
cache: npm

View File

@ -2,17 +2,13 @@ name: Bundlewatch
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
bundlewatch:
@ -20,12 +16,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
cache: npm

View File

@ -1,32 +0,0 @@
name: Compress Images
on:
pull_request:
paths:
- '**.jpg'
- '**.jpeg'
- '**.png'
- '**.webp'
permissions:
contents: read
jobs:
build:
# Only run on Pull Requests within the same repository, and not from forks.
if: github.event.pull_request.head.repo.full_name == github.repository
name: calibreapp/image-actions
runs-on: ubuntu-latest
permissions:
# allow calibreapp/image-actions to update PRs
pull-requests: write
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Compress Images
uses: calibreapp/image-actions@1.1.0
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}

View File

@ -7,38 +7,31 @@ on:
- v4-dev
- "!dependabot/**"
pull_request:
# The branches below must be a subset of the branches above
branches:
- main
- v4-dev
- "!dependabot/**"
schedule:
- cron: "0 2 * * 4"
workflow_dispatch:
- cron: "0 2 * * 5"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v1
with:
config-file: ./.github/codeql/codeql-config.yml
languages: "javascript"
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:javascript"
uses: github/codeql-action/analyze@v1

View File

@ -1,36 +0,0 @@
name: cspell
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
permissions:
contents: read
jobs:
cspell:
permissions:
# allow streetsidesoftware/cspell-action to fetch files for commits and PRs
contents: read
pull-requests: read
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Run cspell
uses: streetsidesoftware/cspell-action@v6
with:
config: ".cspell.json"
files: "**/*.{md,mdx}"
inline: error
incremental_files_only: false

View File

@ -2,17 +2,13 @@ name: CSS
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
css:
@ -20,12 +16,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
cache: npm
@ -35,6 +29,3 @@ jobs:
- name: Build CSS
run: npm run css
- name: Run CSS tests
run: npm run css-test

View File

@ -2,17 +2,13 @@ name: Docs
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
docs:
@ -20,12 +16,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
cache: npm
@ -35,16 +29,5 @@ jobs:
- name: Install npm dependencies
run: npm ci
- name: Build docs
run: npm run docs-build
- name: Validate HTML
run: npm run docs-vnu
- name: Run linkinator
uses: JustinBeckwith/linkinator-action@v1
with:
paths: _site
recurse: true
verbosity: error
skip: "^(?!http://localhost)"
- name: Test docs
run: npm run docs

View File

@ -1,26 +0,0 @@
name: Close Issue Awaiting Reply
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
issue-close-require:
permissions:
# allow actions-cool/issues-helper to update issues and PRs
issues: write
pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'twbs/bootstrap'
steps:
- name: awaiting reply
uses: actions-cool/issues-helper@v3
with:
actions: "close-issues"
labels: "awaiting-reply"
inactive-day: 14
body: |
As the issue was labeled with `awaiting-reply`, but there has been no response in 14 days, this issue will be closed. If you have any questions, you can comment/reply.

View File

@ -1,26 +0,0 @@
name: Issue Labeled
on:
issues:
types: [labeled]
permissions:
contents: read
jobs:
issue-labeled:
permissions:
# allow actions-cool/issues-helper to update issues and PRs
issues: write
pull-requests: write
if: github.repository == 'twbs/bootstrap'
runs-on: ubuntu-latest
steps:
- name: awaiting reply
if: github.event.label.name == 'needs-example'
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment"
token: ${{ secrets.GITHUB_TOKEN }}
body: |
Hello @${{ github.event.issue.user.login }}. Bug reports must include a **live demo** of the issue. Per our [contributing guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md), please create a reduced test case on [CodePen](https://codepen.io/) or [StackBlitz](https://stackblitz.com/) and report back with your link, Bootstrap version, and specific browser and Operating System details.

View File

@ -2,37 +2,31 @@ name: JS Tests
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
jobs:
run:
permissions:
# allow coverallsapp/github-action to create new checks issues and fetch code
checks: write
contents: read
name: JS Tests
name: Node ${{ matrix.node }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [12, 14, 16]
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE }}
node-version: ${{ matrix.node }}
cache: npm
- name: Install npm dependencies
@ -45,8 +39,8 @@ jobs:
run: npm run js-test
- name: Run Coveralls
uses: coverallsapp/github-action@v2
if: ${{ !github.event.repository.fork }}
uses: coverallsapp/github-action@1.1.3
if: matrix.node == 14
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
path-to-lcov: "./js/coverage/lcov.info"

View File

@ -2,17 +2,13 @@ name: Lint
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
lint:
@ -20,12 +16,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
cache: npm

View File

@ -2,17 +2,13 @@ name: CSS (node-sass)
on:
push:
branches:
- main
branches-ignore:
- "dependabot/**"
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
NODE: 20
permissions:
contents: read
NODE: 14
jobs:
css:
@ -20,12 +16,10 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
persist-credentials: false
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v2
with:
node-version: "${{ env.NODE }}"
@ -34,16 +28,3 @@ jobs:
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

View File

@ -4,20 +4,12 @@ on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
jobs:
update_release_draft:
permissions:
# allow release-drafter/release-drafter to create GitHub releases and add labels to PRs
contents: write
pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'twbs/bootstrap'
steps:
- uses: release-drafter/release-drafter@v6
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

9
.gitignore vendored
View File

@ -1,5 +1,7 @@
# Ignore docs files
/_site/
# Hugo resources folder
/resources/
# Numerous always-ignore extensions
*.diff
@ -35,12 +37,5 @@ Thumbs.db
*.komodoproject
# Folders to ignore
/dist-sass/
/js/coverage/
/node_modules/
# Site
/site/dist
/site/node_modules
/site/.astro
/site/public

View File

@ -1,12 +0,0 @@
# Prettier is only used for the website
site/.astro
site/dist
site/public
site/src/assets
site/src/scss
site/src/pages/**/*.md
site/src/pages/**/*.mdx
site/src/content/**/*.mdx
site/src/layouts/RedirectLayout.astro
site/static

View File

@ -2,5 +2,4 @@
**/dist/
**/vendor/
/_site/
/site/public/
/js/coverage/

31
.stylelintrc Normal file
View File

@ -0,0 +1,31 @@
{
"extends": [
"stylelint-config-twbs-bootstrap/scss"
],
"rules": {
"declaration-property-value-disallowed-list": {
"border": "none",
"outline": "none"
},
"function-disallowed-list": [
"calc",
"lighten",
"darken"
],
"property-disallowed-list": [
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"transition"
],
"scss/dollar-variable-default": [
true,
{
"ignore": "local"
}
],
"scss/selector-no-union-class-name": true
}
}

View File

@ -1,60 +0,0 @@
{
"extends": [
"stylelint-config-twbs-bootstrap"
],
"reportInvalidScopeDisables": true,
"reportNeedlessDisables": true,
"overrides": [
{
"files": "**/*.scss",
"rules": {
"declaration-property-value-disallowed-list": {
"border": "none",
"outline": "none"
},
"function-disallowed-list": [
"calc",
"lighten",
"darken"
],
"property-disallowed-list": [
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"transition"
],
"scss/dollar-variable-default": [
true,
{
"ignore": "local"
}
],
"scss/selector-no-union-class-name": true
}
},
{
"files": "scss/**/*.{test,spec}.scss",
"rules": {
"scss/dollar-variable-default": null,
"declaration-no-important": null
}
},
{
"files": "site/**/*.scss",
"rules": {
"scss/dollar-variable-default": null
}
},
{
"files": "site/**/examples/**/*.css",
"rules": {
"comment-empty-line-before": null,
"property-no-vendor-prefix": null,
"selector-no-qualifying-type": null,
"value-no-vendor-prefix": null
}
}
]
}

View File

@ -2,131 +2,42 @@
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
Examples of behavior that contributes to creating a positive environment include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall
community
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior include:
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery, and sexual attention or advances of
any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others private information, such as a physical or email address,
without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
## Our Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
mdo@getbootstrap.com.
All complaints will be reviewed and investigated promptly and fairly.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mdo@getbootstrap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct/>

View File

@ -1,6 +1,7 @@
The MIT License (MIT)
Copyright (c) 2011-2025 The Bootstrap Authors
Copyright (c) 2011-2021 Twitter, Inc.
Copyright (c) 2011-2021 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

178
README.md
View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://getbootstrap.com/">
<img src="https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap logo" width="200" height="165">
<img src="https://getbootstrap.com/docs/5.1/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap logo" width="200" height="165">
</a>
</p>
@ -9,7 +9,7 @@
<p align="center">
Sleek, intuitive, and powerful front-end framework for faster and easier web development.
<br>
<a href="https://getbootstrap.com/docs/5.3/"><strong>Explore Bootstrap docs »</strong></a>
<a href="https://getbootstrap.com/docs/5.1/"><strong>Explore Bootstrap docs »</strong></a>
<br>
<br>
<a href="https://github.com/twbs/bootstrap/issues/new?assignees=-&labels=bug&template=bug_report.yml">Report bug</a>
@ -31,7 +31,7 @@ Our default branch is for development of our Bootstrap 5 release. Head to the [`
- [Quick start](#quick-start)
- [Status](#status)
- [Whats included](#whats-included)
- [What's included](#whats-included)
- [Bugs and feature requests](#bugs-and-feature-requests)
- [Documentation](#documentation)
- [Contributing](#contributing)
@ -46,114 +46,113 @@ Our default branch is for development of our Bootstrap 5 release. Head to the [`
Several quick start options are available:
- [Download the latest release](https://github.com/twbs/bootstrap/archive/v5.3.6.zip)
- [Download the latest release](https://github.com/twbs/bootstrap/archive/v5.1.3.zip)
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`
- Install with [npm](https://www.npmjs.com/): `npm install bootstrap@v5.3.6`
- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@v5.3.6`
- Install with [Bun](https://bun.sh/): `bun add bootstrap@v5.3.6`
- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.3.6`
- Install with [npm](https://www.npmjs.com/): `npm install bootstrap`
- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap`
- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.1.3`
- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass`
Read the [Getting started page](https://getbootstrap.com/docs/5.3/getting-started/introduction/) for information on the framework contents, templates, examples, and more.
Read the [Getting started page](https://getbootstrap.com/docs/5.1/getting-started/introduction/) for information on the framework contents, templates and examples, and more.
## Status
[![Build Status](https://img.shields.io/github/actions/workflow/status/twbs/bootstrap/js.yml?branch=main&label=JS%20Tests&logo=github)](https://github.com/twbs/bootstrap/actions/workflows/js.yml?query=workflow%3AJS+branch%3Amain)
[![npm version](https://img.shields.io/npm/v/bootstrap?logo=npm&logoColor=fff)](https://www.npmjs.com/package/bootstrap)
[![Gem version](https://img.shields.io/gem/v/bootstrap?logo=rubygems&logoColor=fff)](https://rubygems.org/gems/bootstrap)
[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue?logo=meteor&logoColor=fff)](https://atmospherejs.com/twbs/bootstrap)
[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap?logo=packagist&logoColor=fff)](https://packagist.org/packages/twbs/bootstrap)
[![NuGet](https://img.shields.io/nuget/vpre/bootstrap?logo=nuget&logoColor=fff)](https://www.nuget.org/packages/bootstrap/absoluteLatest)
[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/main?logo=coveralls&logoColor=fff)](https://coveralls.io/github/twbs/bootstrap?branch=main)
[![Slack](https://bootstrap-slack.herokuapp.com/badge.svg)](https://bootstrap-slack.herokuapp.com/)
[![Build Status](https://img.shields.io/github/workflow/status/twbs/bootstrap/JS%20Tests/main?label=JS%20Tests&logo=github)](https://github.com/twbs/bootstrap/actions?query=workflow%3AJS+Tests+branch%3Amain)
[![npm version](https://img.shields.io/npm/v/bootstrap)](https://www.npmjs.com/package/bootstrap)
[![Gem version](https://img.shields.io/gem/v/bootstrap)](https://rubygems.org/gems/bootstrap)
[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue)](https://atmospherejs.com/twbs/bootstrap)
[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap)](https://packagist.org/packages/twbs/bootstrap)
[![NuGet](https://img.shields.io/nuget/vpre/bootstrap)](https://www.nuget.org/packages/bootstrap/absoluteLatest)
[![peerDependencies Status](https://img.shields.io/david/peer/twbs/bootstrap)](https://david-dm.org/twbs/bootstrap?type=peer)
[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap)](https://david-dm.org/twbs/bootstrap?type=dev)
[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/main)](https://coveralls.io/github/twbs/bootstrap?branch=main)
[![CSS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=gzip&label=CSS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[![CSS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=brotli&label=CSS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[![JS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=gzip&label=JS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[![JS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=brotli&label=JS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[![Backers on Open Collective](https://img.shields.io/opencollective/backers/bootstrap?logo=opencollective&logoColor=fff)](#backers)
[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/bootstrap?logo=opencollective&logoColor=fff)](#sponsors)
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)](https://www.browserstack.com/automate/public-build/SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)
[![Backers on Open Collective](https://img.shields.io/opencollective/backers/bootstrap)](#backers)
[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/bootstrap)](#sponsors)
## Whats included
## What's included
Within the download youll find the following directories and files, logically grouping common assets and providing both compiled and minified variations.
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this:
<details>
<summary>Download contents</summary>
```text
bootstrap/
├── css/
│ ├── bootstrap-grid.css
│ ├── bootstrap-grid.css.map
│ ├── bootstrap-grid.min.css
│ ├── bootstrap-grid.min.css.map
│ ├── bootstrap-grid.rtl.css
│ ├── bootstrap-grid.rtl.css.map
│ ├── bootstrap-grid.rtl.min.css
│ ├── bootstrap-grid.rtl.min.css.map
│ ├── bootstrap-reboot.css
│ ├── bootstrap-reboot.css.map
│ ├── bootstrap-reboot.min.css
│ ├── bootstrap-reboot.min.css.map
│ ├── bootstrap-reboot.rtl.css
│ ├── bootstrap-reboot.rtl.css.map
│ ├── bootstrap-reboot.rtl.min.css
│ ├── bootstrap-reboot.rtl.min.css.map
│ ├── bootstrap-utilities.css
│ ├── bootstrap-utilities.css.map
│ ├── bootstrap-utilities.min.css
│ ├── bootstrap-utilities.min.css.map
│ ├── bootstrap-utilities.rtl.css
│ ├── bootstrap-utilities.rtl.css.map
│ ├── bootstrap-utilities.rtl.min.css
│ ├── bootstrap-utilities.rtl.min.css.map
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── bootstrap.rtl.css
│ ├── bootstrap.rtl.css.map
│ ├── bootstrap.rtl.min.css
│ └── bootstrap.rtl.min.css.map
└── js/
├── bootstrap.bundle.js
├── bootstrap.bundle.js.map
├── bootstrap.bundle.min.js
├── bootstrap.bundle.min.js.map
├── bootstrap.esm.js
├── bootstrap.esm.js.map
├── bootstrap.esm.min.js
├── bootstrap.esm.min.js.map
├── bootstrap.js
├── bootstrap.js.map
├── bootstrap.min.js
└── bootstrap.min.js.map
```
```text
bootstrap/
├── css/
│ ├── bootstrap-grid.css
│ ├── bootstrap-grid.css.map
│ ├── bootstrap-grid.min.css
│ ├── bootstrap-grid.min.css.map
│ ├── bootstrap-grid.rtl.css
│ ├── bootstrap-grid.rtl.css.map
│ ├── bootstrap-grid.rtl.min.css
│ ├── bootstrap-grid.rtl.min.css.map
│ ├── bootstrap-reboot.css
│ ├── bootstrap-reboot.css.map
│ ├── bootstrap-reboot.min.css
│ ├── bootstrap-reboot.min.css.map
│ ├── bootstrap-reboot.rtl.css
│ ├── bootstrap-reboot.rtl.css.map
│ ├── bootstrap-reboot.rtl.min.css
│ ├── bootstrap-reboot.rtl.min.css.map
│ ├── bootstrap-utilities.css
│ ├── bootstrap-utilities.css.map
│ ├── bootstrap-utilities.min.css
│ ├── bootstrap-utilities.min.css.map
│ ├── bootstrap-utilities.rtl.css
│ ├── bootstrap-utilities.rtl.css.map
│ ├── bootstrap-utilities.rtl.min.css
│ ├── bootstrap-utilities.rtl.min.css.map
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── bootstrap.rtl.css
│ ├── bootstrap.rtl.css.map
│ ├── bootstrap.rtl.min.css
│ └── bootstrap.rtl.min.css.map
└── js/
├── bootstrap.bundle.js
├── bootstrap.bundle.js.map
├── bootstrap.bundle.min.js
├── bootstrap.bundle.min.js.map
├── bootstrap.esm.js
├── bootstrap.esm.js.map
├── bootstrap.esm.min.js
├── bootstrap.esm.min.js.map
├── bootstrap.js
├── bootstrap.js.map
├── bootstrap.min.js
└── bootstrap.min.js.map
```
</details>
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [Source maps](https://web.dev/articles/source-maps) (`bootstrap.*.map`) are available for use with certain browsers developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/docs/v2/).
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [Source maps](https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/).
## Bugs and feature requests
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new/choose).
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new).
## Documentation
Bootstraps documentation, included in this repo in the root directory, is built with [Astro](https://astro.build/) and publicly hosted on GitHub Pages at <https://getbootstrap.com/>. The docs may also be run locally.
Bootstrap's documentation, included in this repo in the root directory, is built with [Hugo](https://gohugo.io/) and publicly hosted on GitHub Pages at <https://getbootstrap.com/>. The docs may also be run locally.
Documentation search is powered by [Algolia's DocSearch](https://docsearch.algolia.com/).
Documentation search is powered by [Algolia's DocSearch](https://community.algolia.com/docsearch/). Working on our search? Be sure to set `debug: true` in `site/assets/js/search.js`.
### Running documentation locally
1. Run `npm install` to install the Node.js dependencies, including Astro (the site builder).
1. Run `npm install` to install the Node.js dependencies, including Hugo (the site builder).
2. Run `npm run test` (or a specific npm script) to rebuild distributed CSS and JavaScript files, as well as our docs assets.
3. From the root `/bootstrap` directory, run `npm run docs-serve` in the command line.
4. Open `http://localhost:9001/` in your browser, and voilà.
Learn more about using Astro by reading its [documentation](https://docs.astro.build/en/getting-started/).
Learn more about using Hugo by reading its [documentation](https://gohugo.io/documentation/).
### Documentation for previous releases
@ -173,12 +172,11 @@ Editor preferences are available in the [editor config](https://github.com/twbs/
## Community
Get updates on Bootstraps development and chat with the project maintainers and community members.
Get updates on Bootstrap's development and chat with the project maintainers and community members.
- Follow [@getbootstrap on X](https://x.com/getbootstrap).
- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap).
- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/).
- Ask questions and explore [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions).
- Discuss, ask questions, and more on [the community Discord](https://discord.gg/bZUvakRU3M) or [Bootstrap subreddit](https://www.reddit.com/r/bootstrap/).
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
- Chat with fellow Bootstrappers in IRC. On the `irc.libera.chat` server, in the `#bootstrap` channel.
- Implementation help may be found at Stack Overflow (tagged [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5)).
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
@ -195,29 +193,23 @@ See [the Releases section of our GitHub project](https://github.com/twbs/bootstr
**Mark Otto**
- <https://x.com/mdo>
- <https://twitter.com/mdo>
- <https://github.com/mdo>
**Jacob Thornton**
- <https://x.com/fat>
- <https://twitter.com/fat>
- <https://github.com/fat>
## Thanks
<a href="https://www.browserstack.com/">
<img src="https://live.browserstack.com/images/opensource/browserstack-logo.svg" alt="BrowserStack" width="192" height="42">
<img src="https://live.browserstack.com/images/opensource/browserstack-logo.svg" alt="BrowserStack Logo" width="192" height="42">
</a>
Thanks to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to test in real browsers!
<a href="https://www.netlify.com/">
<img src="https://www.netlify.com/v3/img/components/full-logo-light.svg" alt="Netlify" width="147" height="40">
</a>
Thanks to [Netlify](https://www.netlify.com/) for providing us with Deploy Previews!
## Sponsors
@ -244,4 +236,4 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
## Copyright and license
Code and documentation copyright 2011-2025 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors). Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
Code and documentation copyright 20112021 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).

14
build/.eslintrc.json Normal file
View File

@ -0,0 +1,14 @@
{
"env": {
"browser": false,
"node": true
},
"parserOptions": {
"sourceType": "script"
},
"extends": "../.eslintrc.json",
"rules": {
"no-console": "off",
"strict": "error"
}
}

View File

@ -1,12 +1,6 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const pkgJson = path.join(__dirname, '../package.json')
const pkg = JSON.parse(await fs.readFile(pkgJson, 'utf8'))
'use strict'
const pkg = require('../package.json')
const year = new Date().getFullYear()
function getBanner(pluginFilename) {
@ -17,4 +11,4 @@ function getBanner(pluginFilename) {
*/`
}
export default getBanner
module.exports = getBanner

183
build/build-plugins.js Normal file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env node
/*!
* Script to build our plugins to use them separately.
* Copyright 2020-2021 The Bootstrap Authors
* Copyright 2020-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
const path = require('path')
const rollup = require('rollup')
const { babel } = require('@rollup/plugin-babel')
const banner = require('./banner.js')
const rootPath = path.resolve(__dirname, '../js/dist/')
const plugins = [
babel({
// Only transpile our source code
exclude: 'node_modules/**',
// Include the helpers in each file, at most one copy of each
babelHelpers: 'bundled'
})
]
const bsPlugins = {
Data: path.resolve(__dirname, '../js/src/dom/data.js'),
EventHandler: path.resolve(__dirname, '../js/src/dom/event-handler.js'),
Manipulator: path.resolve(__dirname, '../js/src/dom/manipulator.js'),
SelectorEngine: path.resolve(__dirname, '../js/src/dom/selector-engine.js'),
Alert: path.resolve(__dirname, '../js/src/alert.js'),
Base: path.resolve(__dirname, '../js/src/base-component.js'),
Button: path.resolve(__dirname, '../js/src/button.js'),
Carousel: path.resolve(__dirname, '../js/src/carousel.js'),
Collapse: path.resolve(__dirname, '../js/src/collapse.js'),
Dropdown: path.resolve(__dirname, '../js/src/dropdown.js'),
Modal: path.resolve(__dirname, '../js/src/modal.js'),
Offcanvas: path.resolve(__dirname, '../js/src/offcanvas.js'),
Popover: path.resolve(__dirname, '../js/src/popover.js'),
ScrollSpy: path.resolve(__dirname, '../js/src/scrollspy.js'),
Tab: path.resolve(__dirname, '../js/src/tab.js'),
Toast: path.resolve(__dirname, '../js/src/toast.js'),
Tooltip: path.resolve(__dirname, '../js/src/tooltip.js')
}
const defaultPluginConfig = {
external: [
bsPlugins.Data,
bsPlugins.Base,
bsPlugins.EventHandler,
bsPlugins.SelectorEngine
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.Base]: 'Base',
[bsPlugins.EventHandler]: 'EventHandler',
[bsPlugins.SelectorEngine]: 'SelectorEngine'
}
}
const getConfigByPluginKey = pluginKey => {
switch (pluginKey) {
case 'Alert':
case 'Offcanvas':
case 'Tab':
return defaultPluginConfig
case 'Base':
case 'Button':
case 'Carousel':
case 'Collapse':
case 'Modal':
case 'ScrollSpy': {
const config = Object.assign(defaultPluginConfig)
config.external.push(bsPlugins.Manipulator)
config.globals[bsPlugins.Manipulator] = 'Manipulator'
return config
}
case 'Dropdown':
case 'Tooltip': {
const config = Object.assign(defaultPluginConfig)
config.external.push(bsPlugins.Manipulator, '@popperjs/core')
config.globals[bsPlugins.Manipulator] = 'Manipulator'
config.globals['@popperjs/core'] = 'Popper'
return config
}
case 'Popover':
return {
external: [
bsPlugins.Data,
bsPlugins.SelectorEngine,
bsPlugins.Tooltip
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.SelectorEngine]: 'SelectorEngine',
[bsPlugins.Tooltip]: 'Tooltip'
}
}
case 'Toast':
return {
external: [
bsPlugins.Data,
bsPlugins.Base,
bsPlugins.EventHandler,
bsPlugins.Manipulator
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.Base]: 'Base',
[bsPlugins.EventHandler]: 'EventHandler',
[bsPlugins.Manipulator]: 'Manipulator'
}
}
default:
return {
external: []
}
}
}
const utilObjects = new Set([
'Util',
'Sanitizer',
'Backdrop'
])
const domObjects = new Set([
'Data',
'EventHandler',
'Manipulator',
'SelectorEngine'
])
const build = async plugin => {
console.log(`Building ${plugin} plugin...`)
const { external, globals } = getConfigByPluginKey(plugin)
const pluginFilename = path.basename(bsPlugins[plugin])
let pluginPath = rootPath
if (utilObjects.has(plugin)) {
pluginPath = `${rootPath}/util/`
}
if (domObjects.has(plugin)) {
pluginPath = `${rootPath}/dom/`
}
const bundle = await rollup.rollup({
input: bsPlugins[plugin],
plugins,
external
})
await bundle.write({
banner: banner(pluginFilename),
format: 'umd',
name: plugin,
sourcemap: true,
globals,
generatedCode: 'es2015',
file: path.resolve(__dirname, `${pluginPath}/${pluginFilename}`)
})
console.log(`Building ${plugin} plugin... Done!`)
}
const main = async () => {
try {
await Promise.all(Object.keys(bsPlugins).map(plugin => build(plugin)))
} catch (error) {
console.error(error)
process.exit(1)
}
}
main()

View File

@ -1,108 +0,0 @@
#!/usr/bin/env node
/*!
* Script to build our plugins to use them separately.
* Copyright 2020-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import { globby } from 'globby'
import { rollup } from 'rollup'
import banner from './banner.mjs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const sourcePath = path.resolve(__dirname, '../js/src/').replace(/\\/g, '/')
const jsFiles = await globby(`${sourcePath}/**/*.js`)
// Array which holds the resolved plugins
const resolvedPlugins = []
// Trims the "js" extension and uppercases => first letter, hyphens, backslashes & slashes
const filenameToEntity = filename => filename.replace('.js', '')
.replace(/(?:^|-|\/|\\)[a-z]/g, str => str.slice(-1).toUpperCase())
for (const file of jsFiles) {
resolvedPlugins.push({
src: file,
dist: file.replace('src', 'dist'),
fileName: path.basename(file),
className: filenameToEntity(path.basename(file))
// safeClassName: filenameToEntity(path.relative(sourcePath, file))
})
}
const build = async plugin => {
/**
* @type {import('rollup').GlobalsOption}
*/
const globals = {}
const bundle = await rollup({
input: plugin.src,
plugins: [
babel({
// Only transpile our source code
exclude: 'node_modules/**',
// Include the helpers in each file, at most one copy of each
babelHelpers: 'bundled'
})
],
external(source) {
// Pattern to identify local files
const pattern = /^(\.{1,2})\//
// It's not a local file, e.g a Node.js package
if (!pattern.test(source)) {
globals[source] = source
return true
}
const usedPlugin = resolvedPlugins.find(plugin => {
return plugin.src.includes(source.replace(pattern, ''))
})
if (!usedPlugin) {
throw new Error(`Source ${source} is not mapped!`)
}
// We can change `Index` with `UtilIndex` etc if we use
// `safeClassName` instead of `className` everywhere
globals[path.normalize(usedPlugin.src)] = usedPlugin.className
return true
}
})
await bundle.write({
banner: banner(plugin.fileName),
format: 'umd',
name: plugin.className,
sourcemap: true,
globals,
generatedCode: 'es2015',
file: plugin.dist
})
console.log(`Built ${plugin.className}`)
}
(async () => {
try {
const basename = path.basename(__filename)
const timeLabel = `[${basename}] finished`
console.log('Building individual plugins...')
console.time(timeLabel)
await Promise.all(Object.values(resolvedPlugins).map(plugin => build(plugin)))
console.timeEnd(timeLabel)
} catch (error) {
console.error(error)
process.exit(1)
}
})()

81
build/change-version.js Normal file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env node
/*!
* Script to update version number references in the project.
* Copyright 2017-2021 The Bootstrap Authors
* Copyright 2017-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
'use strict'
const fs = require('fs').promises
const path = require('path')
const globby = require('globby')
const VERBOSE = process.argv.includes('--verbose')
const DRY_RUN = process.argv.includes('--dry') || process.argv.includes('--dry-run')
// These are the filetypes we only care about replacing the version
const GLOB = [
'**/*.{css,html,js,json,md,scss,txt,yml}'
]
const GLOBBY_OPTIONS = {
cwd: path.join(__dirname, '..'),
gitignore: true
}
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
function regExpQuote(string) {
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
}
function regExpQuoteReplacement(string) {
return string.replace(/\$/g, '$$')
}
async function replaceRecursively(file, oldVersion, newVersion) {
const originalString = await fs.readFile(file, 'utf8')
const newString = originalString.replace(
new RegExp(regExpQuote(oldVersion), 'g'), regExpQuoteReplacement(newVersion)
)
// No need to move any further if the strings are identical
if (originalString === newString) {
return
}
if (VERBOSE) {
console.log(`FILE: ${file}`)
}
if (DRY_RUN) {
return
}
await fs.writeFile(file, newString, 'utf8')
}
async function main(args) {
const [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
console.error('Got arguments:', args)
process.exit(1)
}
// Strip any leading `v` from arguments because otherwise we will end up with duplicate `v`s
[oldVersion, newVersion].map(arg => arg.startsWith('v') ? arg.slice(1) : arg)
try {
const files = await globby(GLOB, GLOBBY_OPTIONS)
await Promise.all(files.map(file => replaceRecursively(file, oldVersion, newVersion)))
} catch (error) {
console.error(error)
process.exit(1)
}
}
main(process.argv.slice(2))

View File

@ -1,113 +0,0 @@
#!/usr/bin/env node
/*!
* Script to update version number references in the project.
* Copyright 2017-2025 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import { execFile } from 'node:child_process'
import fs from 'node:fs/promises'
import process from 'node:process'
const VERBOSE = process.argv.includes('--verbose')
const DRY_RUN = process.argv.includes('--dry') || process.argv.includes('--dry-run')
// These are the files we only care about replacing the version
const FILES = [
'README.md',
'config.yml',
'js/src/base-component.js',
'package.js',
'scss/mixins/_banner.scss',
'site/data/docs-versions.yml'
]
// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37
function regExpQuote(string) {
return string.replace(/[$()*+-.?[\\\]^{|}]/g, '\\$&')
}
function regExpQuoteReplacement(string) {
return string.replace(/\$/g, '$$')
}
async function replaceRecursively(file, oldVersion, newVersion) {
const originalString = await fs.readFile(file, 'utf8')
const newString = originalString
.replace(
new RegExp(regExpQuote(oldVersion), 'g'),
regExpQuoteReplacement(newVersion)
)
// Also replace the version used by the rubygem,
// which is using periods (`.`) instead of hyphens (`-`)
.replace(
new RegExp(regExpQuote(oldVersion.replace(/-/g, '.')), 'g'),
regExpQuoteReplacement(newVersion.replace(/-/g, '.'))
)
// No need to move any further if the strings are identical
if (originalString === newString) {
return
}
if (VERBOSE) {
console.log(`Found ${oldVersion} in ${file}`)
}
if (DRY_RUN) {
return
}
await fs.writeFile(file, newString, 'utf8')
}
function bumpNpmVersion(newVersion) {
if (DRY_RUN) {
return
}
execFile('npm', ['version', newVersion, '--no-git-tag'], { shell: true }, error => {
if (error) {
console.error(error)
process.exit(1)
}
})
}
function showUsage(args) {
console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
console.error('Got arguments:', args)
process.exit(1)
}
async function main(args) {
let [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
showUsage(args)
}
// Strip any leading `v` from arguments because
// otherwise we will end up with duplicate `v`s
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => {
return arg.startsWith('v') ? arg.slice(1) : arg
})
if (oldVersion === newVersion) {
showUsage(args)
}
bumpNpmVersion(newVersion)
try {
await Promise.all(
FILES.map(file => replaceRecursively(file, oldVersion, newVersion))
)
} catch (error) {
console.error(error)
process.exit(1)
}
}
main(process.argv.slice(2))

View File

@ -1,181 +0,0 @@
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default branch suffix
BRANCH_SUFFIX="release"
# Check if a custom version parameter was provided
if [ $# -eq 1 ]; then
BRANCH_SUFFIX="$1"
fi
# Branch name to create
NEW_BRANCH="gh-pages-${BRANCH_SUFFIX}"
# Function to print colored messages
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
exit 1
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
# Function to execute command with error handling
execute() {
print_info "Running: $1"
eval $1
if [ $? -ne 0 ]; then
print_error "Failed to execute: $1"
else
print_success "Successfully executed: $1"
fi
}
# Check if /tmp/_site directory exists from a previous run
if [ -d "/tmp/_site" ]; then
print_warning "Found existing /tmp/_site directory. Removing it…"
rm -rf /tmp/_site
fi
# Main process
print_info "Starting documentation deployment process…"
# Step 1: Build documentation
print_info "Building documentation with npm run docs…"
npm run docs
if [ $? -ne 0 ]; then
print_error "Documentation build failed!"
fi
print_success "Documentation built successfully"
# Step 2: Move _site to /tmp/
print_info "Moving _site to temporary location…"
execute "mv _site /tmp/"
# Step 3: Switch to gh-pages branch
print_info "Checking out gh-pages branch…"
git checkout gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to checkout gh-pages branch. Make sure it exists."
fi
print_success "Switched to gh-pages branch"
git reset --hard origin/gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to reset to origin/gh-pages. Check your git configuration."
fi
print_success "Reset to origin/gh-pages"
git pull origin gh-pages
if [ $? -ne 0 ]; then
print_error "Failed to pull from origin/gh-pages. Check your network connection and git configuration."
fi
print_success "Pulled latest changes from origin/gh-pages"
# Step 4: Create a new branch for the update
print_info "Creating new branch ${NEW_BRANCH}"
execute "git checkout -b ${NEW_BRANCH}"
# Step 5: Move root files
print_info "Moving root files from temporary location…"
ROOT_FILES=("404.html" "CNAME" "apple-touch-icon.png" "favicon.ico" "index.html" "robots.txt" "sitemap-0.xml" "sitemap-index.xml" "sw.js")
for file in "${ROOT_FILES[@]}"; do
if [ -f "/tmp/_site/$file" ]; then
execute "mv /tmp/_site/$file ."
else
print_warning "File /tmp/_site/$file not found. Skipping."
fi
done
# Step 6: Move directories with cleanup
print_info "Moving directories from temporary location…"
DIRS=("about" "components" "docsref" "examples" "getting-started" "migration")
for dir in "${DIRS[@]}"; do
if [ -d "/tmp/_site/$dir" ]; then
if [ -d "$dir" ]; then
execute "rm -rf $dir"
fi
execute "mv /tmp/_site/$dir ."
else
print_warning "Directory /tmp/_site/$dir not found. Skipping."
fi
done
# Step 7: Handle special doc directories
print_info "Handling special documentation directories…"
SPECIAL_DOCS=("docs/getting-started" "docs/versions")
for dir in "${SPECIAL_DOCS[@]}"; do
if [ -d "/tmp/_site/$dir" ]; then
if [ -d "$dir" ]; then
execute "rm -rf $dir"
fi
# Make sure parent directory exists
parent_dir=$(dirname "$dir")
mkdir -p "$parent_dir"
execute "mv /tmp/_site/$dir $parent_dir/"
else
print_warning "Directory /tmp/_site/$dir not found. Skipping."
fi
done
# Step 8: Move docs index.html
if [ -f "/tmp/_site/docs/index.html" ]; then
execute "mv /tmp/_site/docs/index.html docs/index.html"
else
print_warning "File /tmp/_site/docs/index.html not found. Skipping."
fi
# Step 9: Handle docs/5.3
if [ -d "/tmp/_site/docs/5.3" ]; then
if [ -d "docs/5.3" ]; then
execute "rm -rf docs/5.3"
fi
execute "mv /tmp/_site/docs/5.3 docs/"
else
print_warning "Directory /tmp/_site/docs/5.3 not found. Skipping."
fi
# Clean up remaining files in /tmp/_site if any
if [ -d "/tmp/_site" ]; then
remaining_files=$(find /tmp/_site -type f | wc -l)
remaining_dirs=$(find /tmp/_site -type d | wc -l)
if [ $remaining_files -gt 0 ] || [ $remaining_dirs -gt 1 ]; then
print_warning "There are still some files or directories in /tmp/_site that weren't moved."
print_warning "You may want to inspect /tmp/_site to see if anything important was missed."
else
print_info "Cleaning up temporary directory…"
rm -rf /tmp/_site
print_success "Temporary directory cleaned up"
fi
fi
# Step 10: Remove empty site directory if it exists
if [ -d "site" ]; then
print_info "Removing empty site directory…"
execute "rm -rf site"
fi
print_success "Docs prep complete!"
print_info "Review changes before committing and pushing."
print_info "Next steps:"
print_info " 1. Run a local server to review changes"
print_info " 2. Check browser and web inspector for any errors"
print_info " 3. git add ."
print_info " 4. git commit -m \"Update documentation\""
print_info " 5. git push origin ${NEW_BRANCH}"

View File

@ -5,17 +5,17 @@
* Remember to use the same vendor files as the CDN ones,
* otherwise the hashes won't match!
*
* Copyright 2017-2025 The Bootstrap Authors
* Copyright 2017-2021 The Bootstrap Authors
* Copyright 2017-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import sh from 'shelljs'
'use strict'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const crypto = require('crypto')
const fs = require('fs')
const path = require('path')
const sh = require('shelljs')
sh.config.fatal = true
@ -47,18 +47,18 @@ const files = [
}
]
for (const { file, configPropertyName } of files) {
fs.readFile(file, 'utf8', (error, data) => {
if (error) {
throw error
files.forEach(file => {
fs.readFile(file.file, 'utf8', (err, data) => {
if (err) {
throw err
}
const algorithm = 'sha384'
const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64')
const integrity = `${algorithm}-${hash}`
const algo = 'sha384'
const hash = crypto.createHash(algo).update(data, 'utf8').digest('base64')
const integrity = `${algo}-${hash}`
console.log(`${configPropertyName}: ${integrity}`)
console.log(`${file.configPropertyName}: ${integrity}`)
sh.sed('-i', new RegExp(`^(\\s+${configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
sh.sed('-i', new RegExp(`^(\\s+${file.configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
})
}
})

19
build/postcss.config.js Normal file
View File

@ -0,0 +1,19 @@
'use strict'
module.exports = ctx => {
return {
map: ctx.file.dirname.includes('examples') ?
false :
{
inline: false,
annotation: true,
sourcesContent: true
},
plugins: {
autoprefixer: {
cascade: false
},
rtlcss: ctx.env === 'RTL' ? {} : false
}
}
}

View File

@ -1,17 +0,0 @@
const mapConfig = {
inline: false,
annotation: true,
sourcesContent: true
}
export default context => {
return {
map: context.file.dirname.includes('examples') ? false : mapConfig,
plugins: {
autoprefixer: {
cascade: false
},
rtlcss: context.env === 'RTL'
}
}
}

View File

@ -1,17 +1,15 @@
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { babel } from '@rollup/plugin-babel'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import replace from '@rollup/plugin-replace'
import banner from './banner.mjs'
'use strict'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const path = require('path')
const { babel } = require('@rollup/plugin-babel')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const replace = require('@rollup/plugin-replace')
const banner = require('./banner.js')
const BUNDLE = process.env.BUNDLE === 'true'
const ESM = process.env.ESM === 'true'
let destinationFile = `bootstrap${ESM ? '.esm' : ''}`
let fileDest = `bootstrap${ESM ? '.esm' : ''}`
const external = ['@popperjs/core']
const plugins = [
babel({
@ -26,7 +24,7 @@ const globals = {
}
if (BUNDLE) {
destinationFile += '.bundle'
fileDest += '.bundle'
// Remove last entry in external array to bundle Popper
external.pop()
delete globals['@popperjs/core']
@ -42,8 +40,8 @@ if (BUNDLE) {
const rollupConfig = {
input: path.resolve(__dirname, `../js/index.${ESM ? 'esm' : 'umd'}.js`),
output: {
banner: banner(),
file: path.resolve(__dirname, `../dist/js/${destinationFile}.js`),
banner,
file: path.resolve(__dirname, `../dist/js/${fileDest}.js`),
format: ESM ? 'esm' : 'umd',
globals,
generatedCode: 'es2015'
@ -56,4 +54,4 @@ if (!ESM) {
rollupConfig.output.name = 'bootstrap'
}
export default rollupConfig
module.exports = rollupConfig

59
build/svgo.yml Normal file
View File

@ -0,0 +1,59 @@
# Usage:
# install svgo globally: `npm i -g svgo`
# svgo --config=build/svgo.yml --input=foo.svg
# https://github.com/svg/svgo/blob/master/docs/how-it-works/en.md
# replace default config
multipass: true
#full: true
# https://github.com/svg/svgo/blob/master/lib/svgo/js2svg.js#L6 for more config options
js2svg:
pretty: true
indent: 2
plugins:
# - addAttributesToSVGElement:
# attributes:
# - focusable: false
- cleanupAttrs: true
- cleanupEnableBackground: true
- cleanupIDs: true
- cleanupListOfValues: true
- cleanupNumericValues: true
- collapseGroups: true
- convertColors: true
- convertPathData: true
- convertShapeToPath: true
- convertStyleToAttrs: true
- convertTransform: true
- inlineStyles: true
- mergePaths: true
- minifyStyles: true
- moveElemsAttrsToGroup: true
- moveGroupAttrsToElems: true
- removeAttrs:
attrs:
- "data-name"
- removeComments: true
- removeDesc: true
- removeDoctype: true
- removeEditorsNSData: true
- removeEmptyAttrs: true
- removeEmptyContainers: true
- removeEmptyText: true
- removeHiddenElems: true
- removeMetadata: true
- removeNonInheritableGroupAttrs: true
- removeTitle: false
- removeUnknownsAndDefaults:
keepRoleAttr: true
- removeUnusedNS: true
- removeUselessDefs: true
- removeUselessStrokeAndFill: true
- removeViewBox: false
- removeXMLNS: false
- removeXMLProcInst: true
- sortAttrs: true

View File

@ -2,22 +2,22 @@
/*!
* Script to run vnu-jar if Java is available.
* Copyright 2017-2025 The Bootstrap Authors
* Copyright 2017-2021 The Bootstrap Authors
* Copyright 2017-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import { execFile, spawn } from 'node:child_process'
import vnu from 'vnu-jar'
'use strict'
const { execFile, spawn } = require('child_process')
const vnu = require('vnu-jar')
execFile('java', ['-version'], (error, stdout, stderr) => {
if (error) {
console.error('Skipping vnu-jar test; Java is probably missing.')
console.error(error)
console.error('Skipping vnu-jar test; Java is missing.')
return
}
console.log('Running vnu-jar validation...')
const is32bitJava = !/64-Bit/.test(stderr)
// vnu-jar accepts multiple ignores joined with a `|`.
@ -30,14 +30,7 @@ execFile('java', ['-version'], (error, stdout, stderr) => {
// Per https://www.w3.org/TR/html-aria/#docconformance having "aria-disabled" on a link is
// NOT RECOMMENDED, but it's still valid - we explain in the docs that it's not ideal,
// and offer more robust alternatives, but also need to show a less-than-ideal example
'An “aria-disabled” attribute whose value is “true” should not be specified on an “a” element that has an “href” attribute.',
// A `code` element with the `is:raw` attribute coming from remark-prismjs (Astro upstream possible bug)
'Attribute “is:raw” is not serializable as XML 1.0.',
'Attribute “is:raw” not allowed on element “code” at this point.',
// Astro's expecting trailing slashes on HTML tags such as <br />
'Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.',
// Allow `switch` attribute.
'Attribute “switch” not allowed on element “input” at this point.'
'An “aria-disabled” attribute whose value is “true” should not be specified on an “a” element that has an “href” attribute.'
].join('|')
const args = [
@ -56,8 +49,6 @@ execFile('java', ['-version'], (error, stdout, stderr) => {
args.splice(0, 0, '-Xss512k')
}
console.log(`command used: java ${args.join(' ')}`)
return spawn('java', args, {
shell: true,
stdio: 'inherit'

View File

@ -3,19 +3,16 @@
/*!
* Script to create the built examples zip archive;
* requires the `zip` command to be present!
* Copyright 2020-2025 The Bootstrap Authors
* Copyright 2020-2021 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import sh from 'shelljs'
'use strict'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const path = require('path')
const sh = require('shelljs')
const pkgJson = path.join(__dirname, '../package.json')
const pkg = JSON.parse(await fs.readFile(pkgJson, 'utf8'))
const pkg = require('../package.json')
const versionShort = pkg.config.version_short
const distFolder = `bootstrap-${pkg.version}-examples`
@ -37,9 +34,6 @@ const imgFiles = [
'bootstrap-logo.svg',
'bootstrap-logo-white.svg'
]
const staticJsFiles = [
'color-modes.js'
]
sh.config.fatal = true
@ -58,32 +52,27 @@ sh.mkdir('-p', [
distFolder,
`${distFolder}/assets/brand/`,
`${distFolder}/assets/dist/css/`,
`${distFolder}/assets/dist/js/`,
`${distFolder}/assets/js/`
`${distFolder}/assets/dist/js/`
])
sh.cp('-Rf', `${docsDir}/examples/*`, distFolder)
for (const file of cssFiles) {
cssFiles.forEach(file => {
sh.cp('-f', `${docsDir}/dist/css/${file}`, `${distFolder}/assets/dist/css/`)
}
})
for (const file of jsFiles) {
jsFiles.forEach(file => {
sh.cp('-f', `${docsDir}/dist/js/${file}`, `${distFolder}/assets/dist/js/`)
}
})
for (const file of imgFiles) {
imgFiles.forEach(file => {
sh.cp('-f', `${docsDir}/assets/brand/${file}`, `${distFolder}/assets/brand/`)
}
for (const file of staticJsFiles) {
sh.cp('-f', `${docsDir}/assets/js/${file}`, `${distFolder}/assets/js/`)
}
})
sh.rm(`${distFolder}/index.html`)
// get all examples' HTML files
for (const file of sh.find(`${distFolder}/**/*.html`)) {
sh.find(`${distFolder}/**/*.html`).forEach(file => {
const fileContents = sh.cat(file)
.toString()
.replace(new RegExp(`"/docs/${versionShort}/`, 'g'), '"../')
@ -92,10 +81,10 @@ for (const file of sh.find(`${distFolder}/**/*.html`)) {
.replace(/(<script src="\.\.\/.*) integrity=".*>/g, '$1></script>')
.replace(/( +)<!-- favicons(.|\n)+<style>/i, ' <style>')
new sh.ShellString(fileContents).to(file)
}
})
// create the zip file
sh.exec(`zip -qr9 "${distFolder}.zip" "${distFolder}"`)
sh.exec(`zip -r9 "${distFolder}.zip" "${distFolder}"`)
// remove the folder we created
sh.rm('-rf', distFolder)

View File

@ -1,56 +1,79 @@
languageCode: "en"
title: "Bootstrap"
baseURL: "https://getbootstrap.com"
enableInlineShortcodes: true
docsDir: "site"
markup:
goldmark:
renderer:
unsafe: true
highlight:
noClasses: false
tableOfContents:
startLevel: 2
endLevel: 6
subtitle: "The most popular HTML, CSS, and JS library in the world."
description: "Powerful, extensible, and feature-packed frontend toolkit. Build and customize with Sass, utilize prebuilt grid system and components, and bring projects to life with powerful JavaScript plugins."
authors: "Mark Otto, Jacob Thornton, and Bootstrap contributors"
buildDrafts: true
buildFuture: true
current_version: "5.3.6"
current_ruby_version: "5.3.6"
docs_version: "5.3"
rfs_version: "v10.0.0"
github_org: "https://github.com/twbs"
repo: "https://github.com/twbs/bootstrap"
x: "getbootstrap"
opencollective: "https://opencollective.com/bootstrap"
blog: "https://blog.getbootstrap.com/"
themes: "https://themes.getbootstrap.com/"
icons: "https://icons.getbootstrap.com/"
swag: "https://cottonbureau.com/people/bootstrap"
enableRobotsTXT: true
metaDataFormat: "yaml"
disableKinds: ["404", "taxonomy", "term", "RSS"]
analytics:
fathom_site: "ITUSEYJG"
publishDir: "_site"
algolia:
app_id: "AK7KMZKZHQ"
api_key: "3151f502c7b9e9dafd5e6372b691a24e"
index_name: "bootstrap"
module:
mounts:
- source: dist
target: static/docs/5.1/dist
- source: site/assets
target: assets
- source: site/content
target: content
- source: site/data
target: data
- source: site/layouts
target: layouts
- source: site/static
target: static
- source: site/static/docs/5.1/assets/img/favicons/apple-touch-icon.png
target: static/apple-touch-icon.png
- source: site/static/docs/5.1/assets/img/favicons/favicon.ico
target: static/favicon.ico
download:
source: "https://github.com/twbs/bootstrap/archive/v5.3.6.zip"
dist: "https://github.com/twbs/bootstrap/releases/download/v5.3.6/bootstrap-5.3.6-dist.zip"
dist_examples: "https://github.com/twbs/bootstrap/releases/download/v5.3.6/bootstrap-5.3.6-examples.zip"
params:
description: "The most popular HTML, CSS, and JS library in the world."
authors: "Mark Otto, Jacob Thornton, and Bootstrap contributors"
social_image_path: /docs/5.1/assets/brand/bootstrap-social.png
social_logo_path: /docs/5.1/assets/brand/bootstrap-social-logo.png
cdn:
# See https://www.srihash.org for info on how to generate the hashes
css: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css"
css_hash: "sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT"
css_rtl: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.rtl.min.css"
css_rtl_hash: "sha384-MdqCcafa5BLgxBDJ3d/4D292geNL64JyRtSGjEszRUQX9rhL1QkcnId+OT7Yw+D+"
js: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.min.js"
js_hash: "sha384-RuyvpeZCxMJCqVUGFI0Do1mQrods/hhxYlcVfGPOfQtPJh0JCw12tUAZ/Mv10S7D"
js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/js/bootstrap.bundle.min.js"
js_bundle_hash: "sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO"
popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
popper_hash: "sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
popper_esm: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/esm/popper.min.js"
current_version: "5.1.3"
current_ruby_version: "5.1.3"
docs_version: "5.1"
rfs_version: "v9.0.6"
github_org: "https://github.com/twbs"
repo: "https://github.com/twbs/bootstrap"
twitter: "getbootstrap"
slack: "https://bootstrap-slack.herokuapp.com/"
opencollective: "https://opencollective.com/bootstrap"
blog: "https://blog.getbootstrap.com/"
themes: "https://themes.getbootstrap.com/"
icons: "https://icons.getbootstrap.com/"
anchors:
min: 2
max: 5
download:
source: "https://github.com/twbs/bootstrap/archive/v5.1.3.zip"
dist: "https://github.com/twbs/bootstrap/releases/download/v5.1.3/bootstrap-5.1.3-dist.zip"
dist_examples: "https://github.com/twbs/bootstrap/releases/download/v5.1.3/bootstrap-5.1.3-examples.zip"
toc:
min: 2
max: 6
cdn:
# See https://www.srihash.org for info on how to generate the hashes
css: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
css_hash: "sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
css_rtl: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.rtl.min.css"
css_rtl_hash: "sha384-+qdLaIRZfNu4cVPK/PxJJEy0B0f3Ugv8i482AKY7gwXwhaCroABd086ybrVKTa0q"
js: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"
js_hash: "sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
js_bundle_hash: "sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js"
popper_hash: "sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB"

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,11 @@
/*!
* Bootstrap Reboot v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Bootstrap Reboot v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
:root,
[data-bs-theme=light] {
:root {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
@ -15,7 +16,6 @@
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
@ -44,33 +44,11 @@
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg-rgb: 255, 255, 255;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
@ -78,106 +56,7 @@
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
@ -208,17 +87,20 @@ body {
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
@ -270,7 +152,8 @@ p {
margin-bottom: 1rem;
}
abbr[title] {
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
@ -326,9 +209,8 @@ small {
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
@ -348,11 +230,11 @@ sup {
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
color: #0d6efd;
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
@ -366,6 +248,8 @@ kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
direction: ltr /* rtl:ignore */;
unicode-bidi: bidi-override;
}
pre {
@ -383,7 +267,7 @@ pre code {
code {
font-size: 0.875em;
color: var(--bs-code-color);
color: #d63384;
word-wrap: break-word;
}
a > code {
@ -391,15 +275,16 @@ a > code {
}
kbd {
padding: 0.1875rem 0.375rem;
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
@ -419,7 +304,7 @@ table {
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
color: #6c757d;
text-align: left;
}
@ -478,8 +363,8 @@ select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
@ -516,8 +401,8 @@ legend {
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
line-height: inherit;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
@ -543,8 +428,8 @@ legend + * {
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
-webkit-appearance: textfield;
}
/* rtl:raw:
@ -565,11 +450,14 @@ legend + * {
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,11 @@
/*!
* Bootstrap Reboot v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Bootstrap Reboot v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
:root,
[data-bs-theme=light] {
:root {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
@ -15,7 +16,6 @@
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
@ -44,33 +44,11 @@
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg-rgb: 255, 255, 255;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
@ -78,106 +56,7 @@
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
@ -208,17 +87,20 @@ body {
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
@ -270,7 +152,8 @@ p {
margin-bottom: 1rem;
}
abbr[title] {
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
@ -326,9 +209,8 @@ small {
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
@ -348,11 +230,11 @@ sup {
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
color: #0d6efd;
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
@ -366,6 +248,8 @@ kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
direction: ltr ;
unicode-bidi: bidi-override;
}
pre {
@ -383,7 +267,7 @@ pre code {
code {
font-size: 0.875em;
color: var(--bs-code-color);
color: #d63384;
word-wrap: break-word;
}
a > code {
@ -391,15 +275,16 @@ a > code {
}
kbd {
padding: 0.1875rem 0.375rem;
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
@ -419,7 +304,7 @@ table {
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
color: #6c757d;
text-align: right;
}
@ -478,8 +363,8 @@ select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
@ -516,8 +401,8 @@ legend {
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
line-height: inherit;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
@ -543,8 +428,8 @@ legend + * {
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
-webkit-appearance: textfield;
}
[type="tel"],
@ -563,11 +448,14 @@ legend + * {
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5367
dist/js/bootstrap.esm.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

197
js/dist/alert.js vendored
View File

@ -1,24 +1,155 @@
/*!
* Bootstrap alert.js v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Bootstrap alert.js v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js'), require('./util/index.js')) :
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions', './util/index'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alert = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions, global.Index));
})(this, (function (BaseComponent, EventHandler, componentFunctions_js, index_js) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./base-component.js')) :
typeof define === 'function' && define.amd ? define(['./dom/event-handler', './base-component'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alert = factory(global.EventHandler, global.Base));
})(this, (function (EventHandler, BaseComponent) { 'use strict';
const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
/**
* --------------------------------------------------------------------------
* Bootstrap alert.js
* Bootstrap (v5.1.3): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const getSelector = element => {
let selector = element.getAttribute('data-bs-target');
if (!selector || selector === '#') {
let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
// `document.querySelector` will rightfully complain it is invalid.
// See https://github.com/twbs/bootstrap/issues/32273
if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
return null;
} // Just in case some CMS puts out a full URL with the anchor appended
if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
hrefAttr = `#${hrefAttr.split('#')[1]}`;
}
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
}
return selector;
};
const getElementFromSelector = element => {
const selector = getSelector(element);
return selector ? document.querySelector(selector) : null;
};
const isDisabled = element => {
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
return true;
}
if (element.classList.contains('disabled')) {
return true;
}
if (typeof element.disabled !== 'undefined') {
return element.disabled;
}
return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
};
const getjQuery = () => {
const {
jQuery
} = window;
if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
return jQuery;
}
return null;
};
const DOMContentLoadedCallbacks = [];
const onDOMContentLoaded = callback => {
if (document.readyState === 'loading') {
// add listener on the first call when the document is in loading state
if (!DOMContentLoadedCallbacks.length) {
document.addEventListener('DOMContentLoaded', () => {
DOMContentLoadedCallbacks.forEach(callback => callback());
});
}
DOMContentLoadedCallbacks.push(callback);
} else {
callback();
}
};
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery();
/* istanbul ignore if */
if ($) {
const name = plugin.NAME;
const JQUERY_NO_CONFLICT = $.fn[name];
$.fn[name] = plugin.jQueryInterface;
$.fn[name].Constructor = plugin;
$.fn[name].noConflict = () => {
$.fn[name] = JQUERY_NO_CONFLICT;
return plugin.jQueryInterface;
};
}
});
};
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.1.3): util/component-functions.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`;
const name = component.NAME;
EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault();
}
if (isDisabled(this)) {
return;
}
const target = getElementFromSelector(this) || this.closest(`.${name}`);
const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
instance[method]();
});
};
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.1.3): alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'alert';
@ -28,61 +159,75 @@
const EVENT_CLOSED = `closed${EVENT_KEY}`;
const CLASS_NAME_FADE = 'fade';
const CLASS_NAME_SHOW = 'show';
/**
* Class definition
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Alert extends BaseComponent {
class Alert extends BaseComponent__default.default {
// Getters
static get NAME() {
return NAME;
}
} // Public
// Public
close() {
const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);
const closeEvent = EventHandler__default.default.trigger(this._element, EVENT_CLOSE);
if (closeEvent.defaultPrevented) {
return;
}
this._element.classList.remove(CLASS_NAME_SHOW);
const isAnimated = this._element.classList.contains(CLASS_NAME_FADE);
this._queueCallback(() => this._destroyElement(), this._element, isAnimated);
}
// Private
this._element.classList.remove(CLASS_NAME_SHOW);
const isAnimated = this._element.classList.contains(CLASS_NAME_FADE);
this._queueCallback(() => this._destroyElement(), this._element, isAnimated);
} // Private
_destroyElement() {
this._element.remove();
EventHandler.trigger(this._element, EVENT_CLOSED);
this.dispose();
}
// Static
EventHandler__default.default.trigger(this._element, EVENT_CLOSED);
this.dispose();
} // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Alert.getOrCreateInstance(this);
if (typeof config !== 'string') {
return;
}
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
data[config](this);
});
}
}
/**
* Data API implementation
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
componentFunctions_js.enableDismissTrigger(Alert, 'close');
enableDismissTrigger(Alert, 'close');
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .Alert to jQuery only if jQuery is present
*/
index_js.defineJQueryPlugin(Alert);
defineJQueryPlugin(Alert);
return Alert;

File diff suppressed because one or more lines are too long

View File

@ -1,83 +1,180 @@
/*!
* Bootstrap base-component.js v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Bootstrap base-component.js v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./util/config.js'), require('./util/index.js')) :
typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './util/config', './util/index'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BaseComponent = factory(global.Data, global.EventHandler, global.Config, global.Index));
})(this, (function (Data, EventHandler, Config, index_js) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js')) :
typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Base = factory(global.Data, global.EventHandler));
})(this, (function (Data, EventHandler) { 'use strict';
const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
/**
* --------------------------------------------------------------------------
* Bootstrap base-component.js
* Bootstrap (v5.1.3): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const MILLISECONDS_MULTIPLIER = 1000;
const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
const getTransitionDurationFromElement = element => {
if (!element) {
return 0;
} // Get transition-duration of the element
let {
transitionDuration,
transitionDelay
} = window.getComputedStyle(element);
const floatTransitionDuration = Number.parseFloat(transitionDuration);
const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0;
} // If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0];
transitionDelay = transitionDelay.split(',')[0];
return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
};
const triggerTransitionEnd = element => {
element.dispatchEvent(new Event(TRANSITION_END));
};
const isElement = obj => {
if (!obj || typeof obj !== 'object') {
return false;
}
if (typeof obj.jquery !== 'undefined') {
obj = obj[0];
}
return typeof obj.nodeType !== 'undefined';
};
const getElement = obj => {
if (isElement(obj)) {
// it's a jQuery object or a node element
return obj.jquery ? obj[0] : obj;
}
if (typeof obj === 'string' && obj.length > 0) {
return document.querySelector(obj);
}
return null;
};
const execute = callback => {
if (typeof callback === 'function') {
callback();
}
};
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
if (!waitForTransition) {
execute(callback);
return;
}
const durationPadding = 5;
const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
let called = false;
const handler = ({
target
}) => {
if (target !== transitionElement) {
return;
}
called = true;
transitionElement.removeEventListener(TRANSITION_END, handler);
execute(callback);
};
transitionElement.addEventListener(TRANSITION_END, handler);
setTimeout(() => {
if (!called) {
triggerTransitionEnd(transitionElement);
}
}, emulatedDuration);
};
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.1.3): base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const VERSION = '5.3.6';
const VERSION = '5.1.3';
/**
* Class definition
*/
class BaseComponent {
constructor(element) {
element = getElement(element);
class BaseComponent extends Config {
constructor(element, config) {
super();
element = index_js.getElement(element);
if (!element) {
return;
}
this._element = element;
this._config = this._getConfig(config);
Data.set(this._element, this.constructor.DATA_KEY, this);
Data__default.default.set(this._element, this.constructor.DATA_KEY, this);
}
// Public
dispose() {
Data.remove(this._element, this.constructor.DATA_KEY);
EventHandler.off(this._element, this.constructor.EVENT_KEY);
for (const propertyName of Object.getOwnPropertyNames(this)) {
Data__default.default.remove(this._element, this.constructor.DATA_KEY);
EventHandler__default.default.off(this._element, this.constructor.EVENT_KEY);
Object.getOwnPropertyNames(this).forEach(propertyName => {
this[propertyName] = null;
}
});
}
// Private
_queueCallback(callback, element, isAnimated = true) {
index_js.executeAfterTransition(callback, element, isAnimated);
executeAfterTransition(callback, element, isAnimated);
}
_getConfig(config) {
config = this._mergeConfigObj(config, this._element);
config = this._configAfterMerge(config);
this._typeCheckConfig(config);
return config;
/** Static */
static getInstance(element) {
return Data__default.default.get(getElement(element), this.DATA_KEY);
}
// Static
static getInstance(element) {
return Data.get(index_js.getElement(element), this.DATA_KEY);
}
static getOrCreateInstance(element, config = {}) {
return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);
}
static get VERSION() {
return VERSION;
}
static get NAME() {
throw new Error('You have to implement the static method "NAME", for each component!');
}
static get DATA_KEY() {
return `bs.${this.NAME}`;
}
static get EVENT_KEY() {
return `.${this.DATA_KEY}`;
}
static eventName(name) {
return `${name}${this.EVENT_KEY}`;
}
}
return BaseComponent;

File diff suppressed because one or more lines are too long

105
js/dist/button.js vendored
View File

@ -1,24 +1,84 @@
/*!
* Bootstrap button.js v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Bootstrap button.js v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/index.js')) :
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/index'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Button = factory(global.BaseComponent, global.EventHandler, global.Index));
})(this, (function (BaseComponent, EventHandler, index_js) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./base-component.js')) :
typeof define === 'function' && define.amd ? define(['./dom/event-handler', './base-component'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Button = factory(global.EventHandler, global.Base));
})(this, (function (EventHandler, BaseComponent) { 'use strict';
const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
/**
* --------------------------------------------------------------------------
* Bootstrap button.js
* Bootstrap (v5.1.3): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const getjQuery = () => {
const {
jQuery
} = window;
if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
return jQuery;
}
return null;
};
const DOMContentLoadedCallbacks = [];
const onDOMContentLoaded = callback => {
if (document.readyState === 'loading') {
// add listener on the first call when the document is in loading state
if (!DOMContentLoadedCallbacks.length) {
document.addEventListener('DOMContentLoaded', () => {
DOMContentLoadedCallbacks.forEach(callback => callback());
});
}
DOMContentLoadedCallbacks.push(callback);
} else {
callback();
}
};
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery();
/* istanbul ignore if */
if ($) {
const name = plugin.NAME;
const JQUERY_NO_CONFLICT = $.fn[name];
$.fn[name] = plugin.jQueryInterface;
$.fn[name].Constructor = plugin;
$.fn[name].noConflict = () => {
$.fn[name] = JQUERY_NO_CONFLICT;
return plugin.jQueryInterface;
};
}
});
};
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.1.3): button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'button';
@ -28,50 +88,57 @@
const CLASS_NAME_ACTIVE = 'active';
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]';
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
/**
* Class definition
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Button extends BaseComponent {
class Button extends BaseComponent__default.default {
// Getters
static get NAME() {
return NAME;
}
} // Public
// Public
toggle() {
// Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method
this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE));
}
} // Static
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Button.getOrCreateInstance(this);
if (config === 'toggle') {
data[config]();
}
});
}
}
}
/**
* Data API implementation
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
event.preventDefault();
const button = event.target.closest(SELECTOR_DATA_TOGGLE);
const data = Button.getOrCreateInstance(button);
data.toggle();
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .Button to jQuery only if jQuery is present
*/
index_js.defineJQueryPlugin(Button);
defineJQueryPlugin(Button);
return Button;

File diff suppressed because one or more lines are too long

915
js/dist/carousel.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

402
js/dist/collapse.js vendored
View File

@ -1,30 +1,199 @@
/*!
* Bootstrap collapse.js v5.3.6 (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Bootstrap collapse.js v5.1.3 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));
})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
})(this, (function (Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
/**
* --------------------------------------------------------------------------
* Bootstrap collapse.js
* Bootstrap (v5.1.3): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const toType = obj => {
if (obj === null || obj === undefined) {
return `${obj}`;
}
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
};
const getSelector = element => {
let selector = element.getAttribute('data-bs-target');
if (!selector || selector === '#') {
let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
// `document.querySelector` will rightfully complain it is invalid.
// See https://github.com/twbs/bootstrap/issues/32273
if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
return null;
} // Just in case some CMS puts out a full URL with the anchor appended
if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
hrefAttr = `#${hrefAttr.split('#')[1]}`;
}
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
}
return selector;
};
const getSelectorFromElement = element => {
const selector = getSelector(element);
if (selector) {
return document.querySelector(selector) ? selector : null;
}
return null;
};
const getElementFromSelector = element => {
const selector = getSelector(element);
return selector ? document.querySelector(selector) : null;
};
const isElement = obj => {
if (!obj || typeof obj !== 'object') {
return false;
}
if (typeof obj.jquery !== 'undefined') {
obj = obj[0];
}
return typeof obj.nodeType !== 'undefined';
};
const getElement = obj => {
if (isElement(obj)) {
// it's a jQuery object or a node element
return obj.jquery ? obj[0] : obj;
}
if (typeof obj === 'string' && obj.length > 0) {
return document.querySelector(obj);
}
return null;
};
const typeCheckConfig = (componentName, config, configTypes) => {
Object.keys(configTypes).forEach(property => {
const expectedTypes = configTypes[property];
const value = config[property];
const valueType = value && isElement(value) ? 'element' : toType(value);
if (!new RegExp(expectedTypes).test(valueType)) {
throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
}
});
};
/**
* Trick to restart an element's animation
*
* @param {HTMLElement} element
* @return void
*
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
// eslint-disable-next-line no-unused-expressions
element.offsetHeight;
};
const getjQuery = () => {
const {
jQuery
} = window;
if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
return jQuery;
}
return null;
};
const DOMContentLoadedCallbacks = [];
const onDOMContentLoaded = callback => {
if (document.readyState === 'loading') {
// add listener on the first call when the document is in loading state
if (!DOMContentLoadedCallbacks.length) {
document.addEventListener('DOMContentLoaded', () => {
DOMContentLoadedCallbacks.forEach(callback => callback());
});
}
DOMContentLoadedCallbacks.push(callback);
} else {
callback();
}
};
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery();
/* istanbul ignore if */
if ($) {
const name = plugin.NAME;
const JQUERY_NO_CONFLICT = $.fn[name];
$.fn[name] = plugin.jQueryInterface;
$.fn[name].Constructor = plugin;
$.fn[name].noConflict = () => {
$.fn[name] = JQUERY_NO_CONFLICT;
return plugin.jQueryInterface;
};
}
});
};
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.1.3): collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'collapse';
const DATA_KEY = 'bs.collapse';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const Default = {
toggle: true,
parent: null
};
const DefaultType = {
toggle: 'boolean',
parent: '(null|element)'
};
const EVENT_SHOW = `show${EVENT_KEY}`;
const EVENT_SHOWN = `shown${EVENT_KEY}`;
const EVENT_HIDE = `hide${EVENT_KEY}`;
@ -40,53 +209,53 @@
const HEIGHT = 'height';
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]';
const Default = {
parent: null,
toggle: true
};
const DefaultType = {
parent: '(null|element)',
toggle: 'boolean'
};
/**
* Class definition
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Collapse extends BaseComponent {
class Collapse extends BaseComponent__default.default {
constructor(element, config) {
super(element, config);
super(element);
this._isTransitioning = false;
this._config = this._getConfig(config);
this._triggerArray = [];
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE);
for (const elem of toggleList) {
const selector = SelectorEngine.getSelectorFromElement(elem);
const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);
const toggleList = SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE);
for (let i = 0, len = toggleList.length; i < len; i++) {
const elem = toggleList[i];
const selector = getSelectorFromElement(elem);
const filterElement = SelectorEngine__default.default.find(selector).filter(foundElem => foundElem === this._element);
if (selector !== null && filterElement.length) {
this._selector = selector;
this._triggerArray.push(elem);
}
}
this._initializeChildren();
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());
}
if (this._config.toggle) {
this.toggle();
}
}
} // Getters
// Getters
static get Default() {
return Default;
}
static get DefaultType() {
return DefaultType;
}
static get NAME() {
return NAME;
}
} // Public
// Public
toggle() {
if (this._isShown()) {
this.hide();
@ -94,154 +263,231 @@
this.show();
}
}
show() {
if (this._isTransitioning || this._isShown()) {
return;
}
let activeChildren = [];
// find active children
let actives = [];
let activesData;
if (this._config.parent) {
activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {
toggle: false
}));
const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
actives = SelectorEngine__default.default.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)); // remove children if greater depth
}
if (activeChildren.length && activeChildren[0]._isTransitioning) {
return;
const container = SelectorEngine__default.default.findOne(this._selector);
if (actives.length) {
const tempActiveData = actives.find(elem => container !== elem);
activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null;
if (activesData && activesData._isTransitioning) {
return;
}
}
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW);
const startEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW);
if (startEvent.defaultPrevented) {
return;
}
for (const activeInstance of activeChildren) {
activeInstance.hide();
}
actives.forEach(elemActive => {
if (container !== elemActive) {
Collapse.getOrCreateInstance(elemActive, {
toggle: false
}).hide();
}
if (!activesData) {
Data__default.default.set(elemActive, DATA_KEY, null);
}
});
const dimension = this._getDimension();
this._element.classList.remove(CLASS_NAME_COLLAPSE);
this._element.classList.add(CLASS_NAME_COLLAPSING);
this._element.style[dimension] = 0;
this._addAriaAndCollapsedClass(this._triggerArray, true);
this._isTransitioning = true;
const complete = () => {
this._isTransitioning = false;
this._element.classList.remove(CLASS_NAME_COLLAPSING);
this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
this._element.style[dimension] = '';
EventHandler.trigger(this._element, EVENT_SHOWN);
EventHandler__default.default.trigger(this._element, EVENT_SHOWN);
};
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
const scrollSize = `scroll${capitalizedDimension}`;
this._queueCallback(complete, this._element, true);
this._element.style[dimension] = `${this._element[scrollSize]}px`;
}
hide() {
if (this._isTransitioning || !this._isShown()) {
return;
}
const startEvent = EventHandler.trigger(this._element, EVENT_HIDE);
const startEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
if (startEvent.defaultPrevented) {
return;
}
const dimension = this._getDimension();
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;
index_js.reflow(this._element);
reflow(this._element);
this._element.classList.add(CLASS_NAME_COLLAPSING);
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
for (const trigger of this._triggerArray) {
const element = SelectorEngine.getElementFromSelector(trigger);
if (element && !this._isShown(element)) {
const triggerArrayLength = this._triggerArray.length;
for (let i = 0; i < triggerArrayLength; i++) {
const trigger = this._triggerArray[i];
const elem = getElementFromSelector(trigger);
if (elem && !this._isShown(elem)) {
this._addAriaAndCollapsedClass([trigger], false);
}
}
this._isTransitioning = true;
const complete = () => {
this._isTransitioning = false;
this._element.classList.remove(CLASS_NAME_COLLAPSING);
this._element.classList.add(CLASS_NAME_COLLAPSE);
EventHandler.trigger(this._element, EVENT_HIDDEN);
EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
};
this._element.style[dimension] = '';
this._queueCallback(complete, this._element, true);
}
// Private
_isShown(element = this._element) {
return element.classList.contains(CLASS_NAME_SHOW);
}
_configAfterMerge(config) {
} // Private
_getConfig(config) {
config = { ...Default,
...Manipulator__default.default.getDataAttributes(this._element),
...config
};
config.toggle = Boolean(config.toggle); // Coerce string values
config.parent = index_js.getElement(config.parent);
config.parent = getElement(config.parent);
typeCheckConfig(NAME, config, DefaultType);
return config;
}
_getDimension() {
return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;
}
_initializeChildren() {
if (!this._config.parent) {
return;
}
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE);
for (const element of children) {
const selected = SelectorEngine.getElementFromSelector(element);
const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem)).forEach(element => {
const selected = getElementFromSelector(element);
if (selected) {
this._addAriaAndCollapsedClass([element], this._isShown(selected));
}
}
}
_getFirstLevelChildren(selector) {
const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
// remove children if greater depth
return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));
});
}
_addAriaAndCollapsedClass(triggerArray, isOpen) {
if (!triggerArray.length) {
return;
}
for (const element of triggerArray) {
element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);
element.setAttribute('aria-expanded', isOpen);
}
}
// Static
triggerArray.forEach(elem => {
if (isOpen) {
elem.classList.remove(CLASS_NAME_COLLAPSED);
} else {
elem.classList.add(CLASS_NAME_COLLAPSED);
}
elem.setAttribute('aria-expanded', isOpen);
});
} // Static
static jQueryInterface(config) {
const _config = {};
if (typeof config === 'string' && /show|hide/.test(config)) {
_config.toggle = false;
}
return this.each(function () {
const _config = {};
if (typeof config === 'string' && /show|hide/.test(config)) {
_config.toggle = false;
}
const data = Collapse.getOrCreateInstance(this, _config);
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
data[config]();
}
});
}
}
}
/**
* Data API implementation
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {
event.preventDefault();
}
for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
const selector = getSelectorFromElement(this);
const selectorElements = SelectorEngine__default.default.find(selector);
selectorElements.forEach(element => {
Collapse.getOrCreateInstance(element, {
toggle: false
}).toggle();
}
});
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .Collapse to jQuery only if jQuery is present
*/
index_js.defineJQueryPlugin(Collapse);
defineJQueryPlugin(Collapse);
return Collapse;

Some files were not shown because too many files have changed in this diff Show More