From 195440f2fb1e94c014a9cf08f3eae40f3d224620 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Sun, 17 Apr 2022 22:17:50 -0700 Subject: [PATCH] v5.2.0 design refresh, plus responsive offcanvas classes (#35736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add responsive offcanvas classes - Updates navbar-expand classes to de-dupe some styles—these shouldn't interfere now. - Adds some JS to the offcanvas component to help with responsiveness Co-Authored-By: GeoSot * Redesign homepage, docs, and examples Homepage: - New Bootstrap purple navbar - Redesigned masthead - Rewrote and redesigned homepage content - Replace Copy text with icons like Bootstrap Icons site across all ClipboardJS instances - Fixed padding issues in site footer - Match homepage button styles to examples page, use gap instead of tons of responsive margin utils Docs: - New navbar, no more subnav. Migrated search and version picker into the main navbar and refreshed the design of it all, including the responsive toggles. - New sidebar navigation is always expanded, and now features Bootstrap Icons alongside section headings - Sidebar navigation autoscrolls to active link for better usability - Subnav and navbar padding issues ironed out - Enhanced the version picker in anticipation of v5.2: we can now link right to the same page in the previous version. - Redesign callouts to add more color to our pages - Collapse table of contents on mobile - Cleanup and redesign button styles with CSS variables - Update design for subnav version dropdown - Update highlight and example to be full-width until md - Improve the Added In badges - Turn the ToC into a well on mobile - Redesign code snippets to better house two action buttons Examples: - Redesign Examples page layout - Add new example for responsive offcanvases in navbars * Convert offcanvas to CSS vars * Feat: add resize handler to Offcanvas.js. If we could use as default the `.offcanvas` class without modifiers, we then, could add a simplified selector The selector itself, ignores the .offcanvas class as it doesn't have any responsive behavior The `aria-modal` addon is to protect us, selection backdrop elements * Separate examples code, Add some selectors, fix stackblitz btn Co-authored-by: GeoSot --- .bundlewatch.config.json | 4 +- js/src/offcanvas.js | 9 + js/tests/unit/offcanvas.spec.js | 22 ++ scss/_navbar.scss | 46 +-- scss/_offcanvas.scss | 179 +++++---- scss/_spinners.scss | 2 +- site/assets/js/application.js | 74 +--- site/assets/js/code-examples.js | 88 +++++ site/assets/js/search.js | 7 +- site/assets/scss/_ads.scss | 5 +- site/assets/scss/_algolia.scss | 7 +- site/assets/scss/_buttons.scss | 16 +- site/assets/scss/_callouts.scss | 30 +- site/assets/scss/_clipboard-js.scss | 37 +- site/assets/scss/_component-examples.scss | 42 ++- site/assets/scss/_content.scss | 59 ++- site/assets/scss/_layout.scss | 19 +- site/assets/scss/_masthead.scss | 77 +++- site/assets/scss/_navbar.scss | 85 +++-- site/assets/scss/_search.scss | 62 ++++ site/assets/scss/_sidebar.scss | 90 ++--- site/assets/scss/_subnav.scss | 82 ----- site/assets/scss/_syntax.scss | 2 +- site/assets/scss/_toc.scss | 45 +++ site/assets/scss/_variables.scss | 12 +- site/assets/scss/docs.scss | 2 +- site/content/docs/5.1/components/offcanvas.md | 57 ++- site/content/docs/5.1/examples/_index.md | 57 +-- .../5.1/examples/navbars-offcanvas/index.html | 147 ++++++++ .../5.1/examples/navbars-offcanvas/navbar.css | 7 + site/content/docs/5.1/utilities/api.md | 2 +- site/content/docs/5.1/utilities/spacing.md | 4 +- site/data/examples.yml | 2 + site/data/plugins.yml | 47 +++ site/data/sidebar.yml | 20 + site/layouts/_default/baseof.html | 1 + site/layouts/_default/docs.html | 38 +- site/layouts/_default/single.html | 17 +- site/layouts/partials/docs-navbar.html | 103 +++--- site/layouts/partials/docs-sidebar.html | 37 +- site/layouts/partials/docs-subnav.html | 14 - site/layouts/partials/docs-versions.html | 26 +- site/layouts/partials/favicons.html | 4 +- site/layouts/partials/footer.html | 5 +- .../partials/home/masthead-followup.html | 342 +++++++++++++++--- site/layouts/partials/home/masthead.html | 50 +-- site/layouts/partials/icons.html | 72 ++++ site/layouts/partials/scripts.html | 3 +- site/layouts/shortcodes/example.html | 39 +- .../assets/img/examples/navbars-offcanvas.png | Bin 0 -> 6919 bytes .../img/examples/navbars-offcanvas@2x.png | Bin 0 -> 17121 bytes 51 files changed, 1572 insertions(+), 625 deletions(-) create mode 100644 site/assets/js/code-examples.js create mode 100644 site/assets/scss/_search.scss delete mode 100644 site/assets/scss/_subnav.scss create mode 100644 site/content/docs/5.1/examples/navbars-offcanvas/index.html create mode 100644 site/content/docs/5.1/examples/navbars-offcanvas/navbar.css create mode 100644 site/data/plugins.yml delete mode 100644 site/layouts/partials/docs-subnav.html create mode 100644 site/layouts/partials/icons.html create mode 100644 site/static/docs/5.1/assets/img/examples/navbars-offcanvas.png create mode 100644 site/static/docs/5.1/assets/img/examples/navbars-offcanvas@2x.png diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 19c4b9999429..b86447710781 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -26,11 +26,11 @@ }, { "path": "./dist/css/bootstrap.css", - "maxSize": "27.75 kB" + "maxSize": "28.5 kB" }, { "path": "./dist/css/bootstrap.min.css", - "maxSize": "26.0 kB" + "maxSize": "26.5 kB" }, { "path": "./dist/js/bootstrap.bundle.js", diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index b5afc0c87b49..0ae5dbfdab4d 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -41,6 +41,7 @@ const EVENT_SHOWN = `shown${EVENT_KEY}` const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` +const EVENT_RESIZE = `resize${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` @@ -263,6 +264,14 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { } }) +EventHandler.on(window, EVENT_RESIZE, () => { + for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { + if (getComputedStyle(element).position !== 'fixed') { + Offcanvas.getOrCreateInstance(element).hide() + } + } +}) + enableDismissTrigger(Offcanvas) /** diff --git a/js/tests/unit/offcanvas.spec.js b/js/tests/unit/offcanvas.spec.js index a98a8c13e3d0..ad0595a86680 100644 --- a/js/tests/unit/offcanvas.spec.js +++ b/js/tests/unit/offcanvas.spec.js @@ -155,6 +155,28 @@ describe('Offcanvas', () => { offCanvas.show() }) }) + + it('should call `hide` on resize, if element\'s position is not fixed any more', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + const offCanvasEl = fixtureEl.querySelector('div') + const offCanvas = new Offcanvas(offCanvasEl) + + spyOn(offCanvas, 'hide').and.callThrough() + + offCanvasEl.addEventListener('shown.bs.offcanvas', () => { + const resizeEvent = createEvent('resize') + offCanvasEl.style.removeProperty('position') + + window.dispatchEvent(resizeEvent) + expect(offCanvas.hide).toHaveBeenCalled() + resolve() + }) + + offCanvas.show() + }) + }) }) describe('config', () => { diff --git a/scss/_navbar.scss b/scss/_navbar.scss index 6db562519c62..bd140ae09965 100644 --- a/scss/_navbar.scss +++ b/scss/_navbar.scss @@ -222,41 +222,31 @@ display: none; } - .offcanvas-header { - display: none; - } - .offcanvas { - position: inherit; - bottom: 0; + // stylelint-disable declaration-no-important + position: static; z-index: auto; flex-grow: 1; - visibility: visible !important; // stylelint-disable-line declaration-no-important - background-color: transparent; - border-right: 0; - border-left: 0; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; @include box-shadow(none); @include transition(none); - transform: none; - } - .offcanvas-top, - .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; - } + // stylelint-enable declaration-no-important - .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .offcanvas-header { + display: none; + } - // Reset `background-color` in case `.bg-*` classes are used in offcanvas - .offcanvas, - .offcanvas-body { - background-color: transparent !important; // stylelint-disable-line declaration-no-important + .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } } } diff --git a/scss/_offcanvas.scss b/scss/_offcanvas.scss index 5288fa9ce68b..e923db2f2481 100644 --- a/scss/_offcanvas.scss +++ b/scss/_offcanvas.scss @@ -1,27 +1,115 @@ -.offcanvas { - position: fixed; - bottom: 0; - z-index: $zindex-offcanvas; - display: flex; - flex-direction: column; - max-width: 100%; - color: $offcanvas-color; - visibility: hidden; - background-color: $offcanvas-bg-color; - background-clip: padding-box; - outline: 0; - @include box-shadow($offcanvas-box-shadow); - @include transition(transform $offcanvas-transition-duration ease-in-out); - - &.showing, - &.show:not(.hiding) { - transform: none; +// stylelint-disable function-disallowed-list + +%offcanvas-css-vars { + // scss-docs-start offcanvas-css-vars + --#{$prefix}offcanvas-width: #{$offcanvas-horizontal-width}; + --#{$prefix}offcanvas-height: #{$offcanvas-vertical-height}; + --#{$prefix}offcanvas-padding-x: #{$offcanvas-padding-x}; + --#{$prefix}offcanvas-padding-y: #{$offcanvas-padding-y}; + --#{$prefix}offcanvas-color: #{$offcanvas-color}; + --#{$prefix}offcanvas-bg: #{$offcanvas-bg-color}; + --#{$prefix}offcanvas-border-width: #{$offcanvas-border-width}; + --#{$prefix}offcanvas-border-color: #{$offcanvas-border-color}; + --#{$prefix}offcanvas-box-shadow: #{$offcanvas-box-shadow}; + // scss-docs-end offcanvas-css-vars +} + +@each $breakpoint in map-keys($grid-breakpoints) { + $next: breakpoint-next($breakpoint, $grid-breakpoints); + $infix: breakpoint-infix($next, $grid-breakpoints); + + .offcanvas#{$infix} { + @extend %offcanvas-css-vars; } +} + +@each $breakpoint in map-keys($grid-breakpoints) { + $next: breakpoint-next($breakpoint, $grid-breakpoints); + $infix: breakpoint-infix($next, $grid-breakpoints); + + .offcanvas#{$infix} { + @include media-breakpoint-down($next) { + position: fixed; + bottom: 0; + z-index: $zindex-offcanvas; + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--#{$prefix}offcanvas-color); + visibility: hidden; + background-color: var(--#{$prefix}offcanvas-bg); + background-clip: padding-box; + outline: 0; + @include box-shadow(var(--#{$prefix}offcanvas-box-shadow)); + @include transition(transform $offcanvas-transition-duration ease-in-out); + + &.showing, + &.show:not(.hiding) { + transform: none; + } + + &.showing, + &.hiding, + &.show { + visibility: visible; + } + + &.offcanvas-start { + top: 0; + left: 0; + width: var(--#{$prefix}offcanvas-width); + border-right: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); + transform: translateX(-100%); + } + + &.offcanvas-end { + top: 0; + right: 0; + width: var(--#{$prefix}offcanvas-width); + border-left: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); + transform: translateX(100%); + } - &.showing, - &.hiding, - &.show { - visibility: visible; + &.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--#{$prefix}offcanvas-height); + max-height: 100%; + border-bottom: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); + transform: translateY(-100%); + } + + &.offcanvas-bottom { + right: 0; + left: 0; + height: var(--#{$prefix}offcanvas-height); + max-height: 100%; + border-top: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); + transform: translateY(100%); + } + } + + @if not ($infix == "") { + @include media-breakpoint-up($next) { + --#{$prefix}offcanvas-height: auto; + --#{$prefix}offcanvas-border-width: 0; + background-color: transparent !important; // stylelint-disable-line declaration-no-important + + .offcanvas-header { + display: none; + } + + .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + // Reset `background-color` in case `.bg-*` classes are used in offcanvas + background-color: transparent !important; // stylelint-disable-line declaration-no-important + } + } + } } } @@ -33,13 +121,13 @@ display: flex; align-items: center; justify-content: space-between; - padding: $offcanvas-padding-y $offcanvas-padding-x; + padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x); .btn-close { - padding: ($offcanvas-padding-y * .5) ($offcanvas-padding-x * .5); - margin-top: $offcanvas-padding-y * -.5; - margin-right: $offcanvas-padding-x * -.5; - margin-bottom: $offcanvas-padding-y * -.5; + padding: calc(var(--#{$prefix}offcanvas-padding-y) * .5) calc(var(--#{$prefix}offcanvas-padding-x) * .5); + margin-top: calc(var(--#{$prefix}offcanvas-padding-y) * -.5); + margin-right: calc(var(--#{$prefix}offcanvas-padding-x) * -.5); + margin-bottom: calc(var(--#{$prefix}offcanvas-padding-y) * -.5); } } @@ -50,41 +138,6 @@ .offcanvas-body { flex-grow: 1; - padding: $offcanvas-padding-y $offcanvas-padding-x; + padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x); overflow-y: auto; } - -.offcanvas-start { - top: 0; - left: 0; - width: $offcanvas-horizontal-width; - border-right: $offcanvas-border-width solid $offcanvas-border-color; - transform: translateX(-100%); -} - -.offcanvas-end { - top: 0; - right: 0; - width: $offcanvas-horizontal-width; - border-left: $offcanvas-border-width solid $offcanvas-border-color; - transform: translateX(100%); -} - -.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: $offcanvas-vertical-height; - max-height: 100%; - border-bottom: $offcanvas-border-width solid $offcanvas-border-color; - transform: translateY(-100%); -} - -.offcanvas-bottom { - right: 0; - left: 0; - height: $offcanvas-vertical-height; - max-height: 100%; - border-top: $offcanvas-border-width solid $offcanvas-border-color; - transform: translateY(100%); -} diff --git a/scss/_spinners.scss b/scss/_spinners.scss index 2fc7d3edc519..ec8473207eff 100644 --- a/scss/_spinners.scss +++ b/scss/_spinners.scss @@ -29,7 +29,7 @@ --#{$prefix}spinner-animation-name: spinner-border; // scss-docs-end spinner-border-css-vars - border: var(--#{$prefix}spinner-border-width) solid currentColor; + border: var(--#{$prefix}spinner-border-width) solid currentcolor; border-right-color: transparent; } diff --git a/site/assets/js/application.js b/site/assets/js/application.js index c14dc57c22b5..55e8969802c3 100644 --- a/site/assets/js/application.js +++ b/site/assets/js/application.js @@ -10,11 +10,26 @@ * For details, see https://creativecommons.org/licenses/by/3.0/. */ -/* global ClipboardJS: false, bootstrap: false */ +/* global bootstrap: false */ (() => { 'use strict' + // Scroll the active sidebar link into view + const sidenav = document.querySelector('.bd-sidebar') + if (sidenav) { + const sidenavHeight = sidenav.clientHeight + const sidenavActiveLink = document.querySelector('.bd-links-nav .active') + const sidenavActiveLinkTop = sidenavActiveLink.offsetTop + const sidenavActiveLinkHeight = sidenavActiveLink.clientHeight + const viewportTop = sidenavActiveLinkTop + const viewportBottom = viewportTop - sidenavHeight + sidenavActiveLinkHeight + + if (sidenav.scrollTop > viewportTop || sidenav.scrollTop < viewportBottom) { + sidenav.scrollTop = viewportTop - (sidenavHeight / 2) + (sidenavActiveLinkHeight / 2) + } + } + // Tooltip and popover demos document.querySelectorAll('.tooltip-demo') .forEach(tooltip => { @@ -116,61 +131,4 @@ modalBodyInput.value = recipient }) } - - // Insert copy to clipboard button before .highlight - const btnTitle = 'Copy to clipboard' - const btnEdit = 'Edit on StackBlitz' - const btnHtml = '
' - document.querySelectorAll('div.highlight') - .forEach(element => { - element.insertAdjacentHTML('beforebegin', btnHtml) - }) - - /** - * - * @param {string} selector - * @param {string} title - */ - function snippetButtonTooltip(selector, title) { - document.querySelectorAll(selector).forEach(btn => { - const tooltipBtn = new bootstrap.Tooltip(btn, { title }) - - btn.addEventListener('mouseleave', () => { - // Explicitly hide tooltip, since after clicking it remains - // focused (as it's a button), so tooltip would otherwise - // remain visible until focus is moved away - tooltipBtn.hide() - }) - }) - } - - snippetButtonTooltip('.btn-clipboard', btnTitle) - snippetButtonTooltip('.btn-edit', btnEdit) - - const clipboard = new ClipboardJS('.btn-clipboard', { - target(trigger) { - return trigger.parentNode.nextElementSibling - } - }) - - clipboard.on('success', event => { - const tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger) - - tooltipBtn.setContent({ '.tooltip-inner': 'Copied!' }) - event.trigger.addEventListener('hidden.bs.tooltip', () => { - tooltipBtn.setContent({ '.tooltip-inner': btnTitle }) - }, { once: true }) - event.clearSelection() - }) - - clipboard.on('error', event => { - const modifierKey = /mac/i.test(navigator.userAgent) ? '\u2318' : 'Ctrl-' - const fallbackMsg = `Press ${modifierKey}C to copy` - const tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger) - - tooltipBtn.setContent({ '.tooltip-inner': fallbackMsg }) - event.trigger.addEventListener('hidden.bs.tooltip', () => { - tooltipBtn.setContent({ '.tooltip-inner': btnTitle }) - }, { once: true }) - }) })() diff --git a/site/assets/js/code-examples.js b/site/assets/js/code-examples.js new file mode 100644 index 000000000000..4a61fa09b720 --- /dev/null +++ b/site/assets/js/code-examples.js @@ -0,0 +1,88 @@ +// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT +// IT'S ALL JUST JUNK FOR OUR DOCS! +// ++++++++++++++++++++++++++++++++++++++++++ + +/*! + * JavaScript for Bootstrap's docs (https://getbootstrap.com/) + * Copyright 2011-2022 The Bootstrap Authors + * Copyright 2011-2022 Twitter, Inc. + * Licensed under the Creative Commons Attribution 3.0 Unported License. + * For details, see https://creativecommons.org/licenses/by/3.0/. + */ + +/* global ClipboardJS: false, bootstrap: false */ + +(() => { + 'use strict' + // Insert copy to clipboard button before .highlight + const btnTitle = 'Copy to clipboard' + const btnEdit = 'Edit on StackBlitz' + + const btnHtml = [ + '
', + '
', + ' ', + '
', + '
' + ].join('') + + // wrap programmatically code blocks and add copy btn. + document.querySelectorAll('.highlight') + .forEach(element => { + if (!element.closest('.bd-example-snippet')) { // Ignore examples made be shortcode + element.insertAdjacentHTML('beforebegin', btnHtml) + element.previousElementSibling.append(element) + } + }) + + /** + * + * @param {string} selector + * @param {string} title + */ + function snippetButtonTooltip(selector, title) { + document.querySelectorAll(selector).forEach(btn => { + bootstrap.Tooltip.getOrCreateInstance(btn, { title }) + }) + } + + snippetButtonTooltip('.btn-clipboard', btnTitle) + snippetButtonTooltip('.btn-edit', btnEdit) + + const clipboard = new ClipboardJS('.btn-clipboard', { + target: trigger => trigger.closest('.bd-code-snippet').querySelector('.highlight') + }) + + clipboard.on('success', event => { + const iconFirstChild = event.trigger.querySelector('.bi').firstChild + const tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger) + const namespace = 'http://www.w3.org/1999/xlink' + const originalXhref = iconFirstChild.getAttributeNS(namespace, 'href') + const originalTitle = event.trigger.title + + tooltipBtn.setContent({ '.tooltip-inner': 'Copied!' }) + event.trigger.addEventListener('hidden.bs.tooltip', () => { + tooltipBtn.setContent({ '.tooltip-inner': btnTitle }) + }, { once: true }) + event.clearSelection() + iconFirstChild.setAttributeNS(namespace, 'href', originalXhref.replace('clipboard', 'check2')) + + setTimeout(() => { + iconFirstChild.setAttributeNS(namespace, 'href', originalXhref) + event.trigger.title = originalTitle + }, 2000) + }) + + clipboard.on('error', event => { + const modifierKey = /mac/i.test(navigator.userAgent) ? '\u2318' : 'Ctrl-' + const fallbackMsg = `Press ${modifierKey}C to copy` + const tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger) + + tooltipBtn.setContent({ '.tooltip-inner': fallbackMsg }) + event.trigger.addEventListener('hidden.bs.tooltip', () => { + tooltipBtn.setContent({ '.tooltip-inner': btnTitle }) + }, { once: true }) + }) +})() diff --git a/site/assets/js/search.js b/site/assets/js/search.js index 15197bfd9a5b..6c90a208725a 100644 --- a/site/assets/js/search.js +++ b/site/assets/js/search.js @@ -14,12 +14,17 @@ const siteDocsVersion = inputElement.getAttribute('data-bd-docs-version') document.addEventListener('keydown', event => { - if (event.ctrlKey && event.key === '/') { + if ((((event.ctrlKey || event.metaKey) && event.key === 'k')) || (event.ctrlKey && event.key === '/')) { event.preventDefault() inputElement.focus() } }) + if (navigator.platform.includes('Win') || navigator.platform.includes('Linux')) { + const searchShortcut = document.querySelector('.bd-search') + searchShortcut.setAttribute('data-shortcut', '⌃K') + } + window.docsearch({ apiKey: '5990ad008512000bba2cf951ccf0332f', indexName: 'bootstrap', diff --git a/site/assets/scss/_ads.scss b/site/assets/scss/_ads.scss index 026d1a17a7a1..b9369eb19cfc 100644 --- a/site/assets/scss/_ads.scss +++ b/site/assets/scss/_ads.scss @@ -14,7 +14,7 @@ @include font-size(.8125rem); line-height: 1.4; text-align: left; - background-color: rgba(0, 0, 0, .05); + background-color: $gray-100; a { color: $gray-800; @@ -22,8 +22,7 @@ } @include media-breakpoint-up(sm) { - max-width: 330px; - @include border-radius(4px); + @include border-radius(.5rem); } } diff --git a/site/assets/scss/_algolia.scss b/site/assets/scss/_algolia.scss index 9c457d2e3765..ab22ad9a7e18 100644 --- a/site/assets/scss/_algolia.scss +++ b/site/assets/scss/_algolia.scss @@ -12,12 +12,15 @@ margin: $dropdown-spacer 0 0; @include font-size(.875rem); background-color: $dropdown-bg; + background-clip: padding-box; border: $dropdown-border-width solid $dropdown-border-color; @include border-radius($dropdown-border-radius); - @include box-shadow($dropdown-box-shadow); + box-shadow: $dropdown-box-shadow; @include media-breakpoint-up(md) { - width: 400px; + width: 500px; + margin-top: .5rem; + margin-left: -110px; } } diff --git a/site/assets/scss/_buttons.scss b/site/assets/scss/_buttons.scss index 93ffa83ed57f..13bd6e48a389 100644 --- a/site/assets/scss/_buttons.scss +++ b/site/assets/scss/_buttons.scss @@ -8,10 +8,14 @@ --bs-btn-color: var(--bs-white); --bs-btn-bg: var(--bd-violet); --bs-btn-border-color: var(--bd-violet); + --bs-btn-border-radius: .5rem; --bs-btn-hover-color: var(--bs-white); - --bs-btn-hover-bg: #{shade-color($bd-violet, 20%)}; - --bs-btn-hover-border-color: #{shade-color($bd-violet, 20%)}; + --bs-btn-hover-bg: #{shade-color($bd-violet, 10%)}; + --bs-btn-hover-border-color: #{shade-color($bd-violet, 10%)}; --bs-btn-focus-shadow-rgb: var(--bd-violet-rgb); + --bs-btn-active-color: var(--bs-btn-hover-color); + --bs-btn-active-bg: #{shade-color($bd-violet, 20%)}; + --bs-btn-active-border-color: #{shade-color($bd-violet, 20%)}; } // scss-docs-end btn-css-vars-example @@ -23,13 +27,19 @@ --bs-btn-hover-bg: var(--bd-accent); --bs-btn-hover-border-color: var(--bd-accent); --bs-btn-focus-shadow-rgb: var(--bd-accent-rgb); + --bs-btn-active-color: var(--bs-btn-hover-color); + --bs-btn-active-bg: var(--bs-btn-hover-bg); + --bs-btn-active-border-color: var(--bs-btn-hover-border-color); } .btn-bd-light { --bs-btn-color: var(--bs-gray-600); - --bs-btn-border-color: var(--bs-gray-300); + --bs-btn-border-color: var(--bs-gray-400); + --bs-btn-hover-color: var(--bd-violet); + --bs-btn-hover-border-color: var(--bd-violet); --bs-btn-active-color: var(--bd-violet); --bs-btn-active-bg: var(--bs-white); --bs-btn-active-border-color: var(--bd-violet); + --bs-btn-focus-border-color: var(--bd-violet); --bs-btn-focus-shadow-rgb: var(--bd-violet-rgb); } diff --git a/site/assets/scss/_callouts.scss b/site/assets/scss/_callouts.scss index aff91f18b628..4513a7d1d569 100644 --- a/site/assets/scss/_callouts.scss +++ b/site/assets/scss/_callouts.scss @@ -6,36 +6,30 @@ padding: 1.25rem; margin-top: 1.25rem; margin-bottom: 1.25rem; - border: 1px solid $gray-200; - border-left-width: .25rem; - @include border-radius(); + background-color: var(--bd-callout-bg, var(--bs-gray-100)); + border-left: .25rem solid var(--bd-callout-border, var(--bs-gray-300)); h4 { margin-bottom: .25rem; } - p:last-child { + > :last-child { margin-bottom: 0; } - code { - @include border-radius(); - } - + .bd-callout { margin-top: -.25rem; } -} -// Variations -.bd-callout-info { - border-left-color: $bd-info; -} - -.bd-callout-warning { - border-left-color: $bd-warning; + .highlight { + background-color: rgba($black, .05); + } } -.bd-callout-danger { - border-left-color: $bd-danger; +// Variations +@each $variant in $bd-callout-variants { + .bd-callout-#{$variant} { + --bd-callout-bg: rgba(var(--bs-#{$variant}-rgb), .075); + --bd-callout-border: rgba(var(--bs-#{$variant}-rgb), .5); + } } diff --git a/site/assets/scss/_clipboard-js.scss b/site/assets/scss/_clipboard-js.scss index b2b0854dc415..7acf90f0b7d0 100644 --- a/site/assets/scss/_clipboard-js.scss +++ b/site/assets/scss/_clipboard-js.scss @@ -19,26 +19,29 @@ .btn-clipboard, .btn-edit { - position: absolute; - top: .65rem; - right: .65rem; - z-index: 10; display: block; - padding: .25rem .5rem; - @include font-size(.65em); - color: $primary; - white-space: nowrap; - background-color: $white; - border: 1px solid; - @include border-radius(); + padding: .5em; + line-height: 1; + color: $gray-900; + background-color: $gray-100; + border: 0; + @include border-radius(.25rem); - &:hover, - &:focus { - color: $white; - background-color: $primary; + &:hover { + color: $primary; } } -.btn-edit { - right: 3.65rem; +.btn-clipboard { + position: relative; + z-index: 2; + margin-top: .75rem; + margin-right: .75rem; +} + +.highlight-toolbar { + .btn-clipboard { + margin-top: 0; + margin-right: 0; + } } diff --git a/site/assets/scss/_component-examples.scss b/site/assets/scss/_component-examples.scss index 330bd43bac53..b305f088f9fc 100644 --- a/site/assets/scss/_component-examples.scss +++ b/site/assets/scss/_component-examples.scss @@ -85,21 +85,17 @@ .bd-example { position: relative; padding: 1rem; - margin: 1rem ($bd-gutter-x * -1) 0; - border: solid $gray-300; + margin: 1rem ($bd-gutter-x * -.5) 0; + border: solid $border-color; border-width: 1px 0 0; @include clearfix(); - @include media-breakpoint-up(sm) { + @include media-breakpoint-up(md) { padding: 1.5rem; margin-right: 0; margin-left: 0; border-width: 1px; - @include border-top-radius(.25rem); - - + .bd-clipboard + .highlight { - @include border-bottom-radius(.25rem); - } + @include border-top-radius(var(--bs-border-radius)); } + p { @@ -348,12 +344,14 @@ // .highlight { - padding: var(--bs-gutter-x) $bd-gutter-x; + position: relative; + padding: .75rem ($bd-gutter-x * .5); margin-bottom: 1rem; background-color: var(--bs-gray-100); - @include media-breakpoint-up(sm) { - padding: 1rem 1.5rem; + @include media-breakpoint-up(md) { + padding: .75rem 1.25rem; + @include border-radius(var(--bs-border-radius)); } pre { @@ -363,6 +361,11 @@ white-space: pre; background-color: transparent; border: 0; + + // Undo tabindex that's automatically added by Hugo + &:focus { + outline: 0; + } } pre code { @@ -372,11 +375,22 @@ } } +.bd-example-snippet { + .highlight { + @include border-top-radius(0); + border: 1px solid $border-color; + } + .highlight-toolbar { + border: solid $border-color; + border-width: 0 1px; + } +} + .bd-content .highlight { - margin-right: $bd-gutter-x * -1; - margin-left: $bd-gutter-x * -1; + margin-right: $bd-gutter-x * -.5; + margin-left: $bd-gutter-x * -.5; - @include media-breakpoint-up(sm) { + @include media-breakpoint-up(md) { margin-right: 0; margin-left: 0; } diff --git a/site/assets/scss/_content.scss b/site/assets/scss/_content.scss index d796c491e01f..cd594335a605 100644 --- a/site/assets/scss/_content.scss +++ b/site/assets/scss/_content.scss @@ -2,14 +2,13 @@ // Bootstrap docs content theming // -// Offset for the sticky header -@include media-breakpoint-up(md) { - :root { - scroll-padding-top: 4rem; +.bd-content { + // Offset content from fixed navbar when jumping to headings + > :target { + padding-top: 5rem; + margin-top: -5rem; } -} -.bd-content { > h2:not(:first-child) { margin-top: 3rem; } @@ -46,6 +45,10 @@ border-bottom: 2px solid currentcolor; } + tbody:not(:first-child) { + border-top: 2px solid currentcolor; + } + th, td { &:first-child { @@ -59,12 +62,24 @@ // Prevent breaking of code // stylelint-disable-next-line selector-max-compound-selectors + th, td:first-child > code { white-space: nowrap; } } } +.table-options { + td:nth-child(2) { + min-width: 160px; + } +} + +.table-options td:last-child, +.table-utilities td:last-child { + min-width: 280px; +} + .bd-title { @include font-size(3rem); } @@ -77,3 +92,35 @@ .bd-bg-violet { background-color: $bd-violet; } + +.bi { + width: 1em; + height: 1em; + fill: currentcolor; +} + +.icon-link { + display: flex; + align-items: center; + text-decoration-color: rgba($primary, .5); + text-underline-offset: .5rem; + backface-visibility: hidden; + + .bi { + width: 1.5em; + height: 1.5em; + transition: .2s ease-in-out transform; // stylelint-disable-line property-disallowed-list + } + + &:hover { + .bi { + transform: translate3d(5px, 0, 0); + } + } +} + +.border-lg-start { + @include media-breakpoint-up(lg) { + border-left: $border-width solid $border-color; + } +} diff --git a/site/assets/scss/_layout.scss b/site/assets/scss/_layout.scss index f83cf094c99a..d0482d9b7123 100644 --- a/site/assets/scss/_layout.scss +++ b/site/assets/scss/_layout.scss @@ -1,16 +1,14 @@ +.bd-gutter { + --bs-gutter-x: #{$bd-gutter-x}; +} + .bd-layout { - padding-right: $bd-gutter-x; - padding-left: $bd-gutter-x; - @include media-breakpoint-up(md) { + @include media-breakpoint-up(lg) { display: grid; grid-template-areas: "sidebar main"; - grid-template-columns: 1fr 3fr; - gap: $grid-gutter-width; - } - - @include media-breakpoint-up(lg) { grid-template-columns: 1fr 5fr; + gap: $grid-gutter-width; } } @@ -21,6 +19,11 @@ .bd-main { grid-area: main; + @include media-breakpoint-down(lg) { + max-width: 760px; + margin-inline: auto; + } + @include media-breakpoint-up(md) { display: grid; grid-template-areas: diff --git a/site/assets/scss/_masthead.scss b/site/assets/scss/_masthead.scss index 2e742e76d161..42ad0f385396 100644 --- a/site/assets/scss/_masthead.scss +++ b/site/assets/scss/_masthead.scss @@ -1,38 +1,87 @@ .bd-masthead { + --bd-pink-rgb: #{to-rgb($pink)}; padding: 3rem 0; - background: linear-gradient(165deg, tint-color($bd-purple-light, 85%) 50%, $white 50%); + // stylelint-disable + background-image: linear-gradient(180deg, rgba(var(--bs-body-bg-rgb), .01), rgba(var(--bs-body-bg-rgb), 1) 85%), + radial-gradient(ellipse at top left, rgba(var(--bs-primary-rgb), .5), transparent 50%), + radial-gradient(ellipse at top right, rgba(var(--bd-accent-rgb), .5), transparent 50%), + radial-gradient(ellipse at center right, rgba(var(--bd-violet-rgb), .5), transparent 50%), + radial-gradient(ellipse at center left, rgba(var(--bd-pink-rgb), .5), transparent 50%); + // stylelint-enable h1 { @include font-size(4rem); line-height: 1; } - p:not(.lead) { + .lead { + @include font-size(1rem); + font-weight: 400; color: $gray-700; } - .btn { - padding: .8rem 2rem; - font-weight: 600; + .highlight { + padding: .5rem 4rem .5rem 1rem; + margin-bottom: 0; + line-height: 1.25; + background-color: rgba(var(--bs-body-color-rgb), .075); + @include border-radius(.5rem); + } + .btn-clipboard { + margin-top: .4rem; + background-color: transparent; + } + + #carbonads { // stylelint-disable-line selector-max-id + margin-right: auto; + margin-left: auto; } + + @include media-breakpoint-up(md) { + .lead { + @include font-size(1.5rem); + } + } +} + +.masthead-followup { .lead { - @include font-size(1.5rem); - font-weight: 400; - color: $gray-700; + @include font-size(1rem); + } + + .highlight { + @include border-radius(.5rem); + } + + @include media-breakpoint-up(md) { + .lead { + @include font-size(1.25rem); + } } } -@include media-breakpoint-up(md) { - .mw-md-75 { max-width: 75%; } +.bd-btn-lg { + padding: .8rem 2rem; } .masthead-followup-icon { - padding: .75rem; - background-image: linear-gradient(to bottom right, rgba(255, 255, 255, .2), rgba(255, 255, 255, .01)); - @include border-radius(.75rem); - box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .1); + padding: 1rem; + color: rgba(var(--bg-rgb), 1); + background-color: rgba(var(--bg-rgb), .1); + background-blend-mode: multiple; + @include border-radius(1rem); + mix-blend-mode: darken; + + svg { + filter: drop-shadow(0 1px 1px #fff); + } } .masthead-followup-svg { filter: drop-shadow(0 1px 0 rgba(0, 0, 0, .125)); } + +.masthead-notice { + background-color: var(--bd-accent); + box-shadow: inset 0 -1px 1px rgba(var(--bs-body-color-rgb), .15), 0 .25rem 1.5rem rgba(var(--bs-body-bg-rgb), .75); +} diff --git a/site/assets/scss/_navbar.scss b/site/assets/scss/_navbar.scss index 401731ae19eb..4f9f3f829092 100644 --- a/site/assets/scss/_navbar.scss +++ b/site/assets/scss/_navbar.scss @@ -1,44 +1,85 @@ .bd-navbar { - --bs-gutter-x: $bd-gutter-x; - --bs-gutter-y: $bd-gutter-x; - padding: .75rem 0; - background-color: $bd-violet; + background-color: transparent; + background-image: linear-gradient(to bottom, rgba(var(--bd-violet-rgb), 1), rgba(var(--bd-violet-rgb), .95)); + box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15), inset 0 -1px 0 rgba(0, 0, 0, .15); .navbar-toggler { padding: 0; + margin-right: -.5rem; border: 0; + + &:first-child { + margin-left: -.5rem; + } + + .bi { + width: 1.5rem; + height: 1.5rem; + } + + &:focus { + box-shadow: none; + } } - .navbar-nav { - .nav-link { - padding-right: $spacer * .25; - padding-left: $spacer * .25; - color: rgba($white, .85); + .navbar-brand { + transition: .2s ease-in-out transform; // stylelint-disable-line property-disallowed-list + + &:hover { + transform: rotate(-5deg) scale(1.1); + } + } - &:hover, - &:focus { - color: $white; - } + .navbar-toggler, + .nav-link { + padding-right: $spacer * .25; + padding-left: $spacer * .25; + color: rgba($white, .85); - &.active { - font-weight: 600; - color: $white; - } + &:hover, + &:focus { + color: $white; + } + + &.active { + font-weight: 600; + color: $white; } } .navbar-nav-svg { - width: 1rem; - height: 1rem; + display: inline-block; + vertical-align: -.125rem; } - .offcanvas { - background-color: $bd-violet; + .offcanvas-lg { + background-color: var(--bd-violet); border-left: 0; - @include media-breakpoint-down(md) { + @include media-breakpoint-down(lg) { box-shadow: $box-shadow-lg; } } + + .dropdown-toggle { + &:focus { + outline: 0; + } + } + + .dropdown-menu { + --#{$variable-prefix}dropdown-min-width: 12rem; + --#{$variable-prefix}dropdown-link-hover-bg: rgba(var(--bd-violet-rgb), .1); + @include rfs(.875rem, --#{$variable-prefix}dropdown-font-size); + box-shadow: $dropdown-box-shadow; + } + + .dropdown-item.current { + font-weight: 600; + background-image: escape-svg($dropdown-active-icon); + background-repeat: no-repeat; + background-position: right $dropdown-item-padding-x top .6rem; + background-size: .75rem .75rem; + } } diff --git a/site/assets/scss/_search.scss b/site/assets/scss/_search.scss new file mode 100644 index 000000000000..1825337a002a --- /dev/null +++ b/site/assets/scss/_search.scss @@ -0,0 +1,62 @@ +.bd-search { + position: relative; + width: 100%; + + &::after { + position: absolute; + top: .4rem; + right: .4rem; + bottom: .4rem; + display: flex; + align-items: center; + justify-content: center; + padding-right: .3125rem; + padding-left: .3125rem; + @include font-size(.75rem); + color: rgba($white, .65); + // content: "⌘K"; + content: attr(data-shortcut); + background-color: rgba($white, .1); + @include border-radius(.125rem); + } + + @include media-breakpoint-up(lg) { + position: absolute; + top: .75rem; + left: 50%; + width: 200px; + margin-left: -100px; + } + + @include media-breakpoint-up(xl) { + width: 280px; + margin-left: -140px; + } + + .form-control { + padding-right: 2.75rem; + color: $white; + background-color: rgba($black, .1); + border-color: rgba($white, .4); + transition-property: background-color, border-color, box-shadow; + + &::placeholder { + color: rgba($white, .65); + } + + &::-webkit-search-cancel-button { + appearance: none; + width: 1rem; + height: 1rem; + cursor: pointer; + background: escape-svg($search-clear-icon) no-repeat 0 0; + background-size: 100% 100%; + } + + &:focus { + background-color: rgba($black, .25); + border-color: rgba($bd-accent, 1); + box-shadow: 0 0 0 .25rem rgba($bd-accent, .4); + } + } +} diff --git a/site/assets/scss/_sidebar.scss b/site/assets/scss/_sidebar.scss index 452b155ffa79..ed76b3ca823f 100644 --- a/site/assets/scss/_sidebar.scss +++ b/site/assets/scss/_sidebar.scss @@ -1,87 +1,53 @@ .bd-sidebar { - @include media-breakpoint-down(md) { - margin: 0 ($bd-gutter-x * -1) 1rem; - } -} - -.bd-links { - overflow: auto; - font-weight: 600; - - @include media-breakpoint-up(md) { + @include media-breakpoint-up(lg) { position: sticky; top: 5rem; // Override collapse behaviors // stylelint-disable-next-line declaration-no-important display: block !important; - height: subtract(100vh, 7rem); + height: subtract(100vh, 6rem); // Prevent focus styles to be cut off: padding-left: .25rem; margin-left: -.25rem; overflow-y: auto; } +} - > ul { - @include media-breakpoint-down(md) { - padding: 1.5rem .75rem; - background-color: $gray-100; - border-bottom: 1px solid $gray-200; - } - } - - a { - padding: .1875rem .5rem; - margin-top: .125rem; - margin-left: 1.25rem; - color: rgba($black, .65); - text-decoration: if($link-decoration == none, null, none); - - &:hover, - &:focus { - color: rgba($black, .85); - text-decoration: if($link-hover-decoration == underline, none, null); - background-color: rgba($bd-violet, .1); - } +.bd-links-nav { + @include media-breakpoint-down(lg) { + font-size: .875rem; } - .btn { - // Custom styles (as we don't have a completely neutral button style) - padding: .25rem .5rem; - font-weight: 600; - color: rgba($black, .65); - background-color: transparent; - border: 0; + @include media-breakpoint-between(xs, lg) { + column-count: 2; + column-gap: 1.5rem; - &:hover, - &:focus { - color: rgba($black, .85); - background-color: rgba($bd-violet, .1); + .bd-links-group { + break-inside: avoid; } - &:focus { - box-shadow: 0 0 0 1px rgba($bd-violet, .7); - } - - // Add chevron if there's a submenu - &::before { - width: 1.25em; - line-height: 0; // Align in the middle - content: escape-svg($sidebar-collapse-icon); - @include transition(transform .35s ease); - transform-origin: .5em 50%; + .bd-links-span-all { + column-span: all; } + } +} - &[aria-expanded="true"] { - color: rgba($black, .85); +.bd-links-link { + padding: .1875rem .5rem; + margin-top: .125rem; + margin-left: 1rem; + color: rgba($black, .65); + text-decoration: if($link-decoration == none, null, none); - &::before { - transform: rotate(90deg); - } - } + &:hover, + &:focus, + &.active { + color: rgba($black, .85); + text-decoration: if($link-hover-decoration == underline, none, null); + background-color: rgba(var(--bd-violet-rgb), .1); } - .active { + &.active { font-weight: 600; - color: rgba($black, .85); } } diff --git a/site/assets/scss/_subnav.scss b/site/assets/scss/_subnav.scss deleted file mode 100644 index db183ff6a801..000000000000 --- a/site/assets/scss/_subnav.scss +++ /dev/null @@ -1,82 +0,0 @@ -.bd-subnavbar { - --bs-gutter-x: $bd-gutter-x; - --bs-gutter-y: $bd-gutter-x; - - // The position and z-index are needed for the dropdown to stay on top of the content - position: relative; - z-index: $zindex-sticky; - background-color: rgba($white, .95); - box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .05), inset 0 -1px 0 rgba(0, 0, 0, .15); - - .dropdown-menu { - @include font-size(.875rem); - box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .05); - } - - .dropdown-item.current { - font-weight: 600; - background-image: escape-svg($dropdown-active-icon); - background-repeat: no-repeat; - background-position: right $dropdown-item-padding-x top .6rem; - background-size: .75rem .75rem; - } - - @include media-breakpoint-up(md) { - position: sticky; - top: 0; - } -} - -.bd-search { - position: relative; - - &::after { - position: absolute; - top: .4rem; - right: .4rem; - bottom: .4rem; - display: flex; - align-items: center; - justify-content: center; - padding-right: .3125rem; - padding-left: .3125rem; - @include font-size(.75rem); - color: $gray-600; - content: "Ctrl + /"; - background-color: $gray-100; - @include border-radius(.125rem); - } - - @include media-breakpoint-down(md) { - width: 100%; - } - - .form-control { - padding-right: 3.75rem; - - &:focus { - border-color: $bd-violet; - box-shadow: 0 0 0 3px rgba($bd-violet, .25); - } - } -} - -.bd-sidebar-toggle { - color: $text-muted; - - &:hover, - &:focus { - color: $bd-violet; - } - - &:focus { - box-shadow: 0 0 0 3px rgba($bd-violet, .25); - } - - .bi-collapse { display: none; } - - &:not(.collapsed) { - .bi-expand { display: none; } - .bi-collapse { display: inline-block; } - } -} diff --git a/site/assets/scss/_syntax.scss b/site/assets/scss/_syntax.scss index 54f0cdaaa8d4..662f70dc4d89 100644 --- a/site/assets/scss/_syntax.scss +++ b/site/assets/scss/_syntax.scss @@ -101,7 +101,7 @@ .language-bash, .language-sh { &::before { - color: #009; + color: #777; content: "$ "; user-select: none; } diff --git a/site/assets/scss/_toc.scss b/site/assets/scss/_toc.scss index 596945628e7c..512a11b42029 100644 --- a/site/assets/scss/_toc.scss +++ b/site/assets/scss/_toc.scss @@ -15,6 +15,7 @@ ul { padding-left: 0; + margin-bottom: 0; list-style: none; ul { @@ -40,3 +41,47 @@ } } } + +.bd-toc-toggle { + display: flex; + align-items: center; + + @include media-breakpoint-down(sm) { + justify-content: space-between; + width: 100%; + } + + @include media-breakpoint-down(md) { + border: 1px solid $border-color; + @include border-radius(.4rem); + + &:hover, + &:focus, + &:active, + &[aria-expanded="true"] { + color: var(--bd-violet); + background-color: $white; + border-color: var(--bd-violet); + } + + &:focus, + &[aria-expanded="true"] { + box-shadow: 0 0 0 3px rgba(var(--bd-violet-rgb), .25); + } + } +} + +.bd-toc-collapse { + @include media-breakpoint-down(md) { + nav { + padding: 1.25rem; + background-color: var(--bs-gray-100); + border: 1px solid $border-color; + @include border-radius(.25rem); + } + } + + @include media-breakpoint-up(md) { + display: block !important; // stylelint-disable-line declaration-no-important + } +} diff --git a/site/assets/scss/_variables.scss b/site/assets/scss/_variables.scss index 0c40b17e9e4b..8f74f94d0b53 100644 --- a/site/assets/scss/_variables.scss +++ b/site/assets/scss/_variables.scss @@ -1,17 +1,15 @@ // stylelint-disable scss/dollar-variable-default // Local docs variables -$bd-purple: #563d7c; +$bd-purple: #4c0bce; $bd-violet: lighten(saturate($bd-purple, 5%), 15%); // stylelint-disable-line function-disallowed-list $bd-purple-light: lighten(saturate($bd-purple, 5%), 45%); // stylelint-disable-line function-disallowed-list $bd-accent: #ffe484; -$bd-info: #5bc0de; -$bd-warning: #f0ad4e; -$bd-danger: #d9534f; $dropdown-active-icon: url("data:image/svg+xml,"); -$sidebar-collapse-icon: url("data:image/svg+xml,"); +$search-clear-icon: url("data:image/svg+xml,"); -$bd-gutter-x: 1.25rem; +$bd-gutter-x: 3rem; +$bd-callout-variants: info, warning, danger !default; :root { --bd-purple: #{$bd-purple}; @@ -19,4 +17,6 @@ $bd-gutter-x: 1.25rem; --bd-accent: #{$bd-accent}; --bd-violet-rgb: #{to-rgb($bd-violet)}; --bd-accent-rgb: #{to-rgb($bd-accent)}; + --bd-pink-rgb: #{to-rgb($pink-500)}; + --bd-teal-rgb: #{to-rgb($teal-500)}; } diff --git a/site/assets/scss/docs.scss b/site/assets/scss/docs.scss index 0029e83af965..f1390e2faece 100644 --- a/site/assets/scss/docs.scss +++ b/site/assets/scss/docs.scss @@ -37,7 +37,7 @@ $enable-cssgrid: true; // stylelint-disable-line scss/dollar-variable-default // Load docs components @import "variables"; @import "navbar"; -@import "subnav"; +@import "search"; @import "masthead"; @import "ads"; @import "content"; diff --git a/site/content/docs/5.1/components/offcanvas.md b/site/content/docs/5.1/components/offcanvas.md index 7a8cbc68a782..6ad9b6e893f9 100644 --- a/site/content/docs/5.1/components/offcanvas.md +++ b/site/content/docs/5.1/components/offcanvas.md @@ -137,9 +137,54 @@ When backdrop is set to static, the offcanvas will not close when clicking outsi {{< /example >}} +## Responsive + +Added in v5.2.0 + +Responsive offcanvas classes hide content outside the viewport from a specified breakpoint and down. Above that breakpoint, the contents within will behave as usual. For example, `.offcanvas-lg` hides content in an offcanvas below the `lg` breakpoint, but shows the content above the `lg` breakpoint. + +
+ + +
Resize your browser to show the responsive offcanvas toggle.
+ +
+
+
Responsive offcanvas
+ +
+
+

This is content within an .offcanvas-lg.

+
+
+
+ +```html + + +
+
+
Responsive offcanvas
+ +
+
+

This is content within an .offcanvas-lg.

+
+
+``` + +Responsive offcanvas classes are available across for each breakpoint. + +- `.offcanvas` +- `.offcanvas-sm` +- `.offcanvas-md` +- `.offcanvas-lg` +- `.offcanvas-xl` +- `.offcanvas-xxl` + ## Placement -There's no default placement for offcanvas components, so you must add one of the modifier classes below; +There's no default placement for offcanvas components, so you must add one of the modifier classes below. - `.offcanvas-start` places offcanvas on the left of the viewport (shown above) - `.offcanvas-end` places offcanvas on the right of the viewport @@ -194,10 +239,18 @@ Try the top, right, and bottom examples out below. Since the offcanvas panel is conceptually a modal dialog, be sure to add `aria-labelledby="..."`—referencing the offcanvas title—to `.offcanvas`. Note that you don’t need to add `role="dialog"` since we already add it via JavaScript. -## Sass +## CSS ### Variables +Added in v5.2.0 + +As part of Bootstrap's evolving CSS variables approach, offcanvas now uses local CSS variables on `.offcanvas` for enhanced real-time customization. Values for the CSS variables are set via Sass, so Sass customization is still supported, too. + +{{< scss-docs name="offcanvas-css-vars" file="scss/_offcanvas.scss" >}} + +### Sass variables + {{< scss-docs name="offcanvas-variables" file="scss/_variables.scss" >}} ## Usage diff --git a/site/content/docs/5.1/examples/_index.md b/site/content/docs/5.1/examples/_index.md index 3d5bfab2fb46..39102f3646a1 100644 --- a/site/content/docs/5.1/examples/_index.md +++ b/site/content/docs/5.1/examples/_index.md @@ -7,30 +7,39 @@ aliases: "/examples/" {{< list-examples.inline >}} {{ range $entry := $.Site.Data.examples -}} -

{{ $entry.category }}

-

{{ $entry.description }}

- {{ if eq $entry.category "RTL" -}} -
-

The RTL feature is still experimental and will probably evolve according to user feedback. Spotted something or have an improvement to suggest? Open an issue, we'd love to get your insights.

-
- {{ end -}} - - {{ range $i, $example := $entry.examples -}} - {{- $len := len $entry.examples -}} - {{ if (eq $i 0) }}
{{ end }} -
- - -

{{ $example.name }}

-
-

{{ $example.description }}

+
+
+

{{ $entry.category }}

+

{{ $entry.description }}

+ {{ if eq $entry.category "RTL" -}} +
+

+ RTL is still experimental and will evolve with feedback. Spotted something or have an improvement to suggest? +

+

Please open an issue.

- {{ if (eq (add $i 1) $len) }}
{{ end }} - {{ end -}} + {{ end -}} +
+ +
+ {{ range $i, $example := $entry.examples -}} + {{- $len := len $entry.examples -}} + {{ if (eq $i 0) }}
{{ end }} +
+ + +

{{ $example.name }}

+
+

{{ $example.description }}

+
+ {{ if (eq (add $i 1) $len) }}
{{ end }} + {{ end -}} +
+
{{ end -}} {{< /list-examples.inline >}} diff --git a/site/content/docs/5.1/examples/navbars-offcanvas/index.html b/site/content/docs/5.1/examples/navbars-offcanvas/index.html new file mode 100644 index 000000000000..47041fa01110 --- /dev/null +++ b/site/content/docs/5.1/examples/navbars-offcanvas/index.html @@ -0,0 +1,147 @@ +--- +layout: examples +title: Navbar Template +extra_css: + - "navbar.css" +--- + +
+ + + + + + +
+
+
+

Navbar with offcanvas examples

+

This example shows how responsive offcanvas menus work within the navbar. For positioning of navbars, checkout the }}">top and }}">fixed top examples.

+

From the top down, you'll see a dark navbar, light navbar and a responsive navbar—each with offcanvases built in. Resize your browser window to the large breakpoint to see the toggle for the offcanvas.

+

+ }}" role="button">Learn more about offcanvas navbars » +

+
+
+
+
diff --git a/site/content/docs/5.1/examples/navbars-offcanvas/navbar.css b/site/content/docs/5.1/examples/navbars-offcanvas/navbar.css new file mode 100644 index 000000000000..70d209409d82 --- /dev/null +++ b/site/content/docs/5.1/examples/navbars-offcanvas/navbar.css @@ -0,0 +1,7 @@ +body { + padding-bottom: 20px; +} + +.navbar { + margin-bottom: 20px; +} diff --git a/site/content/docs/5.1/utilities/api.md b/site/content/docs/5.1/utilities/api.md index 16270ad5112c..86cf329381c4 100644 --- a/site/content/docs/5.1/utilities/api.md +++ b/site/content/docs/5.1/utilities/api.md @@ -11,7 +11,7 @@ Bootstrap utilities are generated with our utility API and can be used to modify The `$utilities` map contains all our utilities and is later merged with your custom `$utilities` map, if present. The utility map contains a keyed list of utility groups which accept the following options: -{{< bs-table "table text-start" >}} +{{< bs-table "table table-utilities" >}} | Option | Type | Default value | Description | | --- | --- | --- | --- | | [`property`](#property) | **Required** | – | Name of the property, this can be a string or an array of strings (e.g., horizontal paddings or margins). | diff --git a/site/content/docs/5.1/utilities/spacing.md b/site/content/docs/5.1/utilities/spacing.md index 70fbe7694509..1e5f6d32f4c3 100644 --- a/site/content/docs/5.1/utilities/spacing.md +++ b/site/content/docs/5.1/utilities/spacing.md @@ -10,7 +10,9 @@ toc: true Assign responsive-friendly `margin` or `padding` values to an element or a subset of its sides with shorthand classes. Includes support for individual properties, all properties, and vertical and horizontal properties. Classes are built from a default Sass map ranging from `.25rem` to `3rem`. -Using the CSS Grid layout module? Consider using [the gap utility](#gap). +{{< callout >}} +**Using the CSS Grid layout module?** Consider using [the gap utility](#gap) instead. +{{< /callout >}} ### Notation diff --git a/site/data/examples.yml b/site/data/examples.yml index 6d6419d21842..38644ef4a0d4 100644 --- a/site/data/examples.yml +++ b/site/data/examples.yml @@ -63,6 +63,8 @@ examples: - name: Navbars description: "Demonstration of all responsive and container options for the navbar." + - name: Navbars offcanvas + description: "Same as the Navbars example, but with our offcanvas component." - name: Navbar static description: "Single navbar example of a static top navbar along with some additional content." - name: Navbar fixed diff --git a/site/data/plugins.yml b/site/data/plugins.yml new file mode 100644 index 000000000000..22cec9877b41 --- /dev/null +++ b/site/data/plugins.yml @@ -0,0 +1,47 @@ +- name: Alert + description: Show and hide alert messages to your users. + link: components/alerts/#javascript-behavior + +- name: Button + description: Programmatically control the active state for buttons. + link: components/buttons/#button-plugin + +- name: Carousel + description: Add slideshows to any page, including support for crossfade. + link: components/carousel/ + +- name: Collapse + description: Expand and collapse areas of content, or create accordions. + link: components/collapse/ + +- name: Dropdown + description: Create menus of links, actions, forms, and more. + link: components/dropdowns/ + +- name: Modal + description: Add flexible and responsive dialogs to your project. + link: components/modal/ + +- name: Offcanvas + description: Build and toggle hidden sidebars into any page. + link: components/offcanvas/ + +- name: Popover + description: Create custom overlays. Built on Popper.js. + link: components/popovers/ + +- name: Scrollspy + description: Automatically update active nav links based on page scroll. + link: components/scrollspy/ + +- name: Tab + description: Allow Bootstrap nav components to toggle contents. + link: components/navs-tabs/ + +- name: Toast + description: Show and hide notifications to your visitors. + link: components/toasts/ + +- name: Tooltip + description: Replace browser tooltips with custom ones. Built on Popper.js. + link: components/tooltips/ diff --git a/site/data/sidebar.yml b/site/data/sidebar.yml index 8aad6027bf0e..eb03285c5f4f 100644 --- a/site/data/sidebar.yml +++ b/site/data/sidebar.yml @@ -2,6 +2,8 @@ # The logic for the sidebar generation is in "site/layouts/partials/docs-sidebar.html". - title: Getting started + icon: book-half + icon_color: indigo pages: - title: Introduction - title: Download @@ -16,6 +18,8 @@ - title: Contribute - title: Customize + icon: palette2 + icon_color: pink pages: - title: Overview - title: Sass @@ -26,6 +30,8 @@ - title: Optimize - title: Layout + icon: grid-fill + icon_color: teal pages: - title: Breakpoints - title: Containers @@ -37,6 +43,8 @@ - title: CSS Grid - title: Content + icon: file-earmark-richtext + icon_color: gray pages: - title: Reboot - title: Typography @@ -45,6 +53,8 @@ - title: Figures - title: Forms + icon: ui-radios + icon_color: blue pages: - title: Overview - title: Form control @@ -57,6 +67,8 @@ - title: Validation - title: Components + icon: menu-button-wide-fill + icon_color: cyan pages: - title: Accordion - title: Alerts @@ -84,6 +96,8 @@ - title: Tooltips - title: Helpers + icon: magic + icon_color: orange pages: - title: Clearfix - title: Colored links @@ -96,6 +110,8 @@ - title: Vertical rule - title: Utilities + icon: braces-asterisk + icon_color: red pages: - title: API - title: Background @@ -116,11 +132,15 @@ - title: Visibility - title: Extend + icon: tools + icon_color: blue pages: - title: Approach - title: Icons - title: About + icon: globe2 + icon_color: indigo pages: - title: Overview - title: Team diff --git a/site/layouts/_default/baseof.html b/site/layouts/_default/baseof.html index 713ab2864ad0..fdf19b31f075 100644 --- a/site/layouts/_default/baseof.html +++ b/site/layouts/_default/baseof.html @@ -5,6 +5,7 @@ {{ block "body_override" . }}{{ end }} {{ partial "skippy" . }} + {{ partial "icons" . }} {{ partial "docs-navbar" . }} diff --git a/site/layouts/_default/docs.html b/site/layouts/_default/docs.html index dd063c5dc8a8..3c61cd342195 100644 --- a/site/layouts/_default/docs.html +++ b/site/layouts/_default/docs.html @@ -1,29 +1,45 @@ {{ define "main" }} - {{ partial "docs-subnav" . }} - -
+
-
+
- View on GitHub -

{{ .Title | markdownify }}

+ + View on GitHub + +

{{ .Title | markdownify }}

{{ .Page.Params.Description | markdownify }}

{{ partial "ads" . }}
{{ if (eq .Page.Params.toc true) }} -
- On this page - {{ .TableOfContents }} +
+ + On this page +
+
+ {{ .TableOfContents }} +
{{ end }} -
+
{{ if .Page.Params.sections }}
{{ range .Page.Params.sections }} diff --git a/site/layouts/_default/single.html b/site/layouts/_default/single.html index 8ba99c7188af..ae1a41faa766 100644 --- a/site/layouts/_default/single.html +++ b/site/layouts/_default/single.html @@ -1,14 +1,19 @@ {{ define "main" }}
-
+

{{ .Title | markdownify }}

{{ .Page.Params.Description | markdownify }}

{{ if eq .Title "Examples" }} - @@ -20,7 +25,7 @@

{{ .Title | markdownify }}

-
+
{{ .Content }} {{ if eq .Title "Examples" }} @@ -30,7 +35,7 @@

{{ .Title | markdownify }}

{{ partial "icons/droplet-fill.svg" (dict "width" "32" "height" "32") }}
-

Go further with Bootstrap Themes

+

Go further with Bootstrap Themes

Need something more than these examples? Take Bootstrap to the next level with premium themes from the official Bootstrap Themes marketplace. They’re built as their own extended frameworks, rich with new components and plugins, documentation, and powerful build tools.

diff --git a/site/layouts/partials/docs-navbar.html b/site/layouts/partials/docs-navbar.html index 171a2ad73e87..441c2bd56a24 100644 --- a/site/layouts/partials/docs-navbar.html +++ b/site/layouts/partials/docs-navbar.html @@ -1,74 +1,89 @@ -
diff --git a/site/layouts/partials/home/masthead-followup.html b/site/layouts/partials/home/masthead-followup.html index 48f77f47041c..95f394f5ade0 100644 --- a/site/layouts/partials/home/masthead-followup.html +++ b/site/layouts/partials/home/masthead-followup.html @@ -1,37 +1,39 @@ -
-
-
-
- {{ partial "icons/code.svg" (dict "width" "32" "height" "32") }} -
-

Installation

-

- Install Bootstrap’s source Sass and JavaScript files via npm, Composer, or Meteor. -

-

Package managed installs don’t include documentation or our full build scripts. You can also use our npm template repo to quickly generate a Bootstrap project via npm.

- Read installation docs -
-
- {{ highlight "npm install bootstrap" "sh" "" }} - {{ highlight (printf ("gem install bootstrap -v %s") .Site.Params.current_ruby_version) "sh" "" }} +
+
+
+
-
+

Get started any way you want

+

+ Jump right into building with Bootstrap—use the CDN, install it via package manager, or download the source code. +

+

+ + Read installation docs + + +

+
-
-
-
- {{ partial "icons/cloud-fill.svg" (dict "width" "32" "height" "32") }} -
-

jsDelivr

-

- When you only need to include Bootstrap’s compiled CSS or JS, you can use jsDelivr. +

+
+ +

Install via package manager

+

+ Install Bootstrap’s source Sass and JavaScript files via npm, RubyGems, Composer, or Meteor. Package managed installs don’t include documentation or our full build scripts. You can also use our npm template repo to quickly generate a Bootstrap project via npm.

+ {{ highlight "npm install bootstrap" "sh" "" }} + {{ highlight (printf ("gem install bootstrap -v %s") .Site.Params.current_ruby_version) "sh" "" }}

- See it in action with our simple starter template, or browse the examples to jumpstart your next project. You can also choose to include Popper and our JS separately. + Read our installation docs for more info and additional package managers.

- Explore the docs
-
+
+ +

Include via CDN

+

+ When you only need to include Bootstrap’s compiled CSS or JS, you can use jsDelivr. See it in action with our simple starter template, or browse the examples to jumpstart your next project. You can also choose to include Popper and our JS separately. +

{{ highlight (printf (` `) .Site.Params.cdn.css (.Site.Params.cdn.css_hash | safeHTMLAttr)) "html" "" }} @@ -41,21 +43,267 @@

jsDelivr

-
-
-
+
+
+ +
+

Customize everything with Sass

+

+ Bootstrap utilizes Sass for a modular and customizable architecture. Import only the components you need, enable global options like gradients and shadows, and write your own CSS with our variables, maps, functions, and mixins. +

+

+ + Learn more about customizing + + +

+
+ +
+
+

Include all of Bootstrap’s Sass

+

Import one stylesheet and you're off to the races with every feature of our CSS.

+ {{ highlight (printf `// Variable overrides first +$primary: #900; +$enable-shadows: true; +$variable-prefix: "mo-"; + +// Then import Bootstrap +@import "../node_modules/bootstrap/scss/bootstrap"; +`) "scss" "" }} +

Learn more about our global Sass options.

+
+
+

Include what you need

+

The easiest way to customize Bootstrap—include only the CSS you need.

+{{ highlight (printf `// Functions first +@import "../node_modules/bootstrap/scss/functions"; + +// Variable overrides second +$primary: #900; +$enable-shadows: true; +$variable-prefix: "mo-"; + +// Required Bootstrap imports +@import "../node_modules/bootstrap/scss/variables"; +@import "../node_modules/bootstrap/scss/maps"; +@import "../node_modules/bootstrap/scss/mixins"; +@import "../node_modules/bootstrap/scss/root"; + +// Optional components +@import "../node_modules/bootstrap/scss/utilities"; +@import "../node_modules/bootstrap/scss/reboot"; +@import "../node_modules/bootstrap/scss/containers"; +@import "../node_modules/bootstrap/scss/grid"; +@import "../node_modules/bootstrap/scss/helpers"; +@import "../node_modules/bootstrap/scss/utilities/api"; +`) "scss" "" }} +

Learn more about using Bootstrap with Sass.

+
+
+ +
+
+
+ +
+

Build and extend in real-time with CSS variables

+

+ Bootstrap 5 is evolving with each release to better utilize CSS variables for global theme styles, individual components, and even utilities. We provide dozens of variables for colors, font styles, and more at a :root level for use anywhere. On components and utilities, CSS variables are scoped to the relevant class and can easily be modified. +

+

+ + Learn more about CSS variables + + +

+
+
+
+

Using CSS variables

+

Use any of our global :root variables to write new styles. CSS variables use the var(--bs-variableName) syntax and can be inherited by children elements.

+ {{ highlight (printf `.component { + color: var(--bs-gray-800); + background-color: var(--bs-gray-100); + border: 1px solid var(--bs-gray-200); + border-radius: .25rem; +} + +.component-header { + color: var(--bs-purple); +}`) "scss" "" }} +
+
+

Customizing via CSS variables

+

Override global, component, or utility class variables to customize Bootstrap just how you like. No need to redeclare each rule, just a new variable value.

+ {{ highlight (printf `body { + --bs-body-font-family: var(--bs-font-monospace); + --bs-body-line-height: 1.4; + --bs-body-bg: var(--bs-gray-100); +} + +.table { + --bs-table-color: var(--bs-gray-600); + --bs-table-bg: var(--bs-gray-100); + --bs-table-border-color: transparent; +}`) "scss" "" }} +
+
+
+ +
+
+
+ +
+ +
+ +
+

Components, meet the Utility API

+

+ New in Bootstrap 5, our utilities are now generated by our Utility API. We built it as a feature-packed Sass map that can be quickly and easily customized. It's never been easier to add, remove, or modify any utility classes. Make utilities responsive, add pseudo-class variants, and give them custom names. +

+

+ + Learn more about utilities + + + + Explore customized components + + +

+
+
+
+
Quickly customize components
+
+ + +
+ {{ highlight (printf `// Create and extend utilities with the Utility API + +@import "bootstrap/scss/bootstrap"; + +$utilities: map-merge( + $utilities, + ( + "cursor": ( + property: cursor, + class: cursor, + responsive: true, + values: auto pointer grab, + ) + ) +); +`) "scss" "" }} +
+
+ +
+
+
+ +
+

Powerful JavaScript plugins without jQuery

+

+ Easily add toggleable hidden elements, modals and offcanvas menus, popovers and tooltips, and so much more—all without jQuery. JavaScript in Bootstrap is HTML-first, which means adding plugins is as easy as adding data attributes. Need more control? Include individual plugins programmatically. +

+

+ + Learn more about Bootstrap JavaScript + + +

+
+
+
+

Data attribute API

+

Why write more JavaScript when you can write HTML? Nearly all of Bootstrap's JavaScript plugins feature a first-class data API, allowing you to use JavaScript just by adding data attributes.

+
+ +
+ + {{ highlight (printf ` +`) "html" "" }} +

Learn more about our JavaScript as modules and using the programmatic API.

+
+
+

Comprehensive set of plugins

+

Bootstrap features a dozen plugins that you can drop into any project. Drop them in all at once, or choose just the ones you need.

+
+
+ {{- range $plugin := .Site.Data.plugins -}} + {{- $href := printf "/docs/%s/%s" $.Site.Params.docs_version $plugin.link }} + + {{- end }} +
+
+
+ +
+ +
+
+
{{ partial "icons/circle-square.svg" (dict "width" "32" "height" "32") }}
-

Bootstrap Icons

+

Personalize it with Bootstrap Icons

- For the first time ever, Bootstrap has its own open source SVG icon library, designed to work best with our components and documentation. + Bootstrap Icons is an open source SVG icon library featuring over 1,500 glyphs, with more added every release. They're designed to work in any project, whether you use Bootstrap itself or not. Use them as SVGs or icon fonts—both options give you vector scaling and easy customization via CSS.

-

- Bootstrap Icons are designed to work best with Bootstrap components, but they’ll work in any project. They’re SVGs, so they scale quickly and easily, can be implemented in several ways, and can be styled with CSS. +

+ + Get Bootstrap Icons + +

- Get Bootstrap Icons
-
+
Bootstrap Icons
-
-
-
+
+
+
{{ partial "icons/droplet-fill.svg" (dict "width" "32" "height" "32") }}
-

Official Themes

+

Make it yours with official Bootstrap Themes

- Take Bootstrap to the next level with premium themes from the official Bootstrap Themes marketplace. + Take Bootstrap to the next level with premium themes from the official Bootstrap Themes marketplace. Themes are built on Bootstrap as their own extended frameworks, rich with new components and plugins, documentation, and powerful build tools.

-

- Themes are built on Bootstrap as their own extended frameworks, rich with new components and plugins, documentation, and powerful build tools. +

+ + Browse Bootstrap Themes + +

- Browse themes
-
+
-
-
-
- {{ partial "icons/homepage-hero.svg" (dict "class" "img-fluid mb-3 mb-md-0" "width" "600" "height" "533") }} -
-
-

Build fast, responsive sites with Bootstrap

-

- Quickly design and customize responsive mobile-first sites with Bootstrap, the world’s most popular front-end open source toolkit, featuring Sass variables and mixins, responsive grid system, extensive prebuilt components, and powerful JavaScript plugins. -

- -
- Get started - Download +
+
+ + New in v5.1 + CSS Grid, offcanvas navbars, improved utilities, and more! + + Bootstrap +

Build fast, responsive sites with Bootstrap

+

+ 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. +

+
+
+ {{ highlight "npm i bootstrap" "sh" "" }}
-

- Currently v{{ .Site.Params.current_version }} - · - v4.6.x docs - · - All releases -

+ + + Read the docs +
+

+ Currently v{{ .Site.Params.current_version }} + · + Download + · + v4.6.x docs + · + All releases +

+ {{ partial "ads" . }}
- {{ partial "ads" . }}
diff --git a/site/layouts/partials/icons.html b/site/layouts/partials/icons.html new file mode 100644 index 000000000000..9841e143a8ee --- /dev/null +++ b/site/layouts/partials/icons.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + Check + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/layouts/partials/scripts.html b/site/layouts/partials/scripts.html index 2b70521f5d05..5e2e52e90bbb 100644 --- a/site/layouts/partials/scripts.html +++ b/site/layouts/partials/scripts.html @@ -25,8 +25,7 @@ // Open in StackBlitz logic document.querySelectorAll('.btn-edit').forEach(btn => { btn.addEventListener('click', event => { - const htmlSnippet = event.target.closest('.bd-edit').previousSibling.innerHTML - + const htmlSnippet = event.target.closest('.bd-code-snippet').querySelector('.bd-example').innerHTML StackBlitzSDK.openBootstrapSnippet(htmlSnippet) }) }) diff --git a/site/layouts/shortcodes/example.html b/site/layouts/shortcodes/example.html index a808c2589afd..9dab2f9d1a0f 100644 --- a/site/layouts/shortcodes/example.html +++ b/site/layouts/shortcodes/example.html @@ -15,17 +15,30 @@ {{- $show_markup := .Get "show_markup" | default true -}} {{- $input := .Inner -}} -{{- if eq $show_preview true -}} - - {{- $input -}} -
-{{- end -}} - -{{- if eq $show_markup true -}} -
- +
+ {{- if eq $show_preview true -}} + + {{- $input -}}
- {{- $content := replaceRE `\n` `...` $input -}} - {{- $content = replaceRE ` (class=" *?")` "" $content -}} - {{- highlight (trim $content "\n") $lang "" -}} -{{- end -}} + {{- end -}} + + {{- if eq $show_markup true -}} + {{- if eq $show_preview true -}} +
+ {{- $lang -}} +
+ + +
+
+ {{- end -}} + + {{- $content := replaceRE `\n` `...` $input -}} + {{- $content = replaceRE ` (class=" *?")` "" $content -}} + {{- highlight (trim $content "\n") $lang "" -}} + {{- end -}} +
diff --git a/site/static/docs/5.1/assets/img/examples/navbars-offcanvas.png b/site/static/docs/5.1/assets/img/examples/navbars-offcanvas.png new file mode 100644 index 0000000000000000000000000000000000000000..81b000dbfbb065e4f155290fbc533af02d41066d GIT binary patch literal 6919 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 literal 0 HcmV?d00001 diff --git a/site/static/docs/5.1/assets/img/examples/navbars-offcanvas@2x.png b/site/static/docs/5.1/assets/img/examples/navbars-offcanvas@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..af5313436f804cb65b0322380849c7c7841f6aaf GIT binary patch literal 17121 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