diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 089067fdad..f0830c9f32 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -26,11 +26,11 @@ }, { "path": "./dist/css/boosted.css", - "maxSize": "44.5 kB" + "maxSize": "45.5 kB" }, { "path": "./dist/css/boosted.min.css", - "maxSize": "41.5 kB" + "maxSize": "42.25 kB" }, { "path": "./dist/js/boosted.bundle.js", diff --git a/scss/_offcanvas.scss b/scss/_offcanvas.scss index 71462bca82..4ea2a0a190 100644 --- a/scss/_offcanvas.scss +++ b/scss/_offcanvas.scss @@ -146,3 +146,9 @@ overflow-y: auto; color: var(--#{$prefix}offcanvas-color); // Boosted mod } + +.offcanvas-dark { + --#{$prefix}offcanvas-color: #{$white}; + --#{$prefix}offcanvas-bg: #{$gray-900}; + --#{$prefix}offcanvas-border-color: #{$gray-700}; +} diff --git a/scss/_side-navigation.scss b/scss/_side-navigation.scss new file mode 100644 index 0000000000..cc57b31600 --- /dev/null +++ b/scss/_side-navigation.scss @@ -0,0 +1,218 @@ +.side-nav { + // scss-docs-start side-nav-css-vars + --#{$prefix}side-nav-min-width: #{$side-nav-min-width}; + --#{$prefix}side-nav-max-width: #{$side-nav-max-width}; + --#{$prefix}side-nav-bg: #{$side-nav-bg}; + --#{$prefix}side-nav-border-width: #{$side-nav-border-width}; + --#{$prefix}side-nav-border-color: #{$side-nav-border-color}; + --#{$prefix}side-nav-item-min-height: #{$side-nav-item-min-height}; + --#{$prefix}side-nav-item-icon-size: #{$side-nav-item-icon-size}; + --#{$prefix}side-nav-item-spacing: #{$side-nav-item-spacing}; + --#{$prefix}side-nav-item-padding-top: #{$side-nav-item-padding-top}; + --#{$prefix}side-nav-item-padding-end: #{$side-nav-item-padding-end}; + --#{$prefix}side-nav-item-padding-bottom: #{$side-nav-item-padding-bottom}; + --#{$prefix}side-nav-item-padding-start: #{$side-nav-item-padding-start}; + --#{$prefix}side-nav-item-color: #{$side-nav-item-color}; + --#{$prefix}side-nav-item-hover-bg: #{$side-nav-item-hover-bg}; + --#{$prefix}side-nav-item-active-bg: #{$side-nav-item-active-bg}; + --#{$prefix}side-nav-item-active-border: #{$side-nav-item-active-border}; + // scss-docs-end side-nav-css-vars + + position: relative; + flex-shrink: 0; + min-width: add(var(--#{$prefix}side-nav-min-width), var(--#{$prefix}side-nav-border-width)); + height: 100%; + + :focus { + &[data-focus-visible-added] { + outline-offset: -$focus-visible-outer-width; + box-shadow: inset 0 0 0 add($focus-visible-inner-width, $focus-visible-outer-width) var(--#{$prefix}focus-visible-inner-color); + } + } + + .accordion { + --#{$prefix}accordion-bg: transparent; + --#{$prefix}accordion-border-width: 0; + --#{$prefix}accordion-body-padding-top: 0; + --#{$prefix}accordion-body-padding-end: 0; + --#{$prefix}accordion-body-padding-bottom: 0; + --#{$prefix}accordion-body-padding-start: 0; + + .accordion-button::after { + margin-right: var(--#{$prefix}side-nav-item-spacing); + } + + .accordion-body { + --#{$prefix}side-nav-item-padding-start: calc(var(--#{$prefix}side-nav-item-icon-size) + var(--#{$prefix}side-nav-item-spacing) * 2 - var(--#{$prefix}side-nav-border-width)); // stylelint-disable-line function-disallowed-list + + .accordion-body { + --#{$prefix}side-nav-item-padding-start: calc(var(--#{$prefix}side-nav-item-icon-size) * 2 + var(--#{$prefix}side-nav-item-spacing) * 3 - var(--#{$prefix}side-nav-border-width) * 2); // stylelint-disable-line function-disallowed-list + } + } + } + + > * { + overflow: hidden; + background: var(--#{$prefix}side-nav-bg); + } + + // Small trick to remove `.h-100` inside the markup + > div { + position: absolute; + height: 100%; + border-right: var(--#{$prefix}side-nav-border-width) solid var(--#{$prefix}side-nav-border-color); + } + + &.side-nav-static > div { + position: relative; + } + + .side-nav-item { + position: relative; + display: flex; + align-items: center; + min-width: var(--#{$prefix}side-nav-min-width); + min-height: var(--#{$prefix}side-nav-item-min-height); + padding: var(--#{$prefix}side-nav-item-padding-top) var(--#{$prefix}side-nav-item-padding-end) var(--#{$prefix}side-nav-item-padding-bottom) var(--#{$prefix}side-nav-item-padding-start); + margin: 0; + font-size: $font-size-base; + line-height: $line-height-base; + color: var(--#{$prefix}side-nav-item-color); + text-decoration: none; + background-color: transparent; + border: 0; + + &.active, + &.active-parent, + &:active { + background-color: var(--#{$prefix}side-nav-item-active-bg); + } + + &:hover { + background-color: var(--#{$prefix}side-nav-item-hover-bg); + } + + &.active, + &.active-parent.collapsed { + &::before { + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + content: ""; + background-color: var(--#{$prefix}side-nav-item-active-border); + } + } + + svg, + img { + width: var(--#{$prefix}side-nav-item-icon-size); + min-width: var(--#{$prefix}side-nav-item-icon-size); + height: var(--#{$prefix}side-nav-item-icon-size); + margin: calc(-1 * var(--#{$prefix}side-nav-item-padding-top)) subtract(var(--#{$prefix}side-nav-item-spacing), var(--#{$prefix}side-nav-border-width)) calc(-1 * var(--#{$prefix}side-nav-item-padding-bottom)) 0; // stylelint-disable-line function-disallowed-list + } + } +} + +.side-nav-toggle { + display: flex; + align-items: center; + justify-content: center; + width: var(--#{$prefix}side-nav-min-width); + height: var(--#{$prefix}side-nav-item-min-height); + margin-left: auto; + background-color: transparent; + border: 0; + + &::before { + position: absolute; + top: 0; + right: 0; + left: 0; + height: var(--#{$prefix}side-nav-item-min-height); + content: ""; + } + + &::after { + margin-right: 1px; + content: ""; + border: $caret-width solid transparent; + border-right-color: var(--#{$prefix}side-nav-item-color); + border-left: 0; + } + + &.collapsed::after { + margin-left: 3px; + transform: rotate(180deg); + } +} + +.side-nav-content { + display: flex; + height: subtract(100%, var(--#{$prefix}side-nav-item-min-height)); +} + +.side-nav-scrollable { + height: subtract(100%, var(--#{$prefix}side-nav-item-min-height)); + overflow: hidden auto; + scrollbar-width: thin; + + &::-webkit-scrollbar { + width: 8px; + background-color: rgba($black, .1); + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(192, 192, 192, .5); + + &:hover { + background-color: rgba(128, 128, 128, .6); + } + } +} + +@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) { + .side-nav > * { + border: 0; + } + + .side-nav-toggle { + display: none; + } + + .side-nav-content { + height: 100%; + } + } + } +} + +// Themes +.side-nav-accordion { + > * { + width: subtract(var(--#{$prefix}side-nav-max-width), var(--#{$prefix}side-nav-border-width)); + } +} + +.side-nav-collapsible { + .collapse-horizontal { + margin-left: var(--#{$prefix}side-nav-min-width); + + > * { + width: subtract(subtract(var(--#{$prefix}side-nav-max-width), var(--#{$prefix}side-nav-min-width)), var(--#{$prefix}side-nav-border-width)); + height: 0; + } + } + + .side-nav-content { + position: absolute; + flex-direction: column; + width: 100%; + } +} diff --git a/scss/_variables.scss b/scss/_variables.scss index 09aa0961c1..36d4c181f1 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -2240,11 +2240,11 @@ $btn-close-white-disabled-color: $gray-700 !default; // Boosted mod // scss-docs-start offcanvas-variables $offcanvas-padding-y: $modal-inner-padding !default; $offcanvas-padding-x: $modal-inner-padding !default; -$offcanvas-horizontal-width: 400px !default; +$offcanvas-horizontal-width: 300px !default; // Boosted mod: instead of `400px` $offcanvas-vertical-height: 30vh !default; $offcanvas-transition-duration: .3s !default; $offcanvas-border-color: $modal-content-border-color !default; -$offcanvas-border-width: $modal-content-border-width !default; +$offcanvas-border-width: 1px !default; // Boosted mod: instead of `$modal-content-border-width` $offcanvas-title-line-height: $modal-title-line-height !default; $offcanvas-bg-color: $modal-content-bg !default; // Boosted mod: instead of `var(--#{$prefix}body-bg)` $offcanvas-color: $modal-content-color !default; // Boosted mod: instead of `var(--#{$prefix}body-color)` @@ -2422,6 +2422,27 @@ $footer-gap: $spacer * .75 !default; $footer-gap-xl: $spacer * 1.7 !default; // scss-docs-end footer +//// Side navigation +// scss-docs-start side-nav-variables +$side-nav-min-width: calc(var(--#{$prefix}side-nav-item-icon-size) + var(--#{$prefix}side-nav-item-spacing) * 2 - var(--#{$prefix}side-nav-border-width)) !default; // stylelint-disable-line function-disallowed-list +$side-nav-max-width: $offcanvas-horizontal-width !default; +$side-nav-bg: $offcanvas-bg-color !default; +$side-nav-border-width: 1px !default; +$side-nav-border-color: $offcanvas-border-color !default; + +$side-nav-item-min-height: $spacer * 2 !default; +$side-nav-item-icon-size: $spacer * 1.25 !default; +$side-nav-item-spacing: 15px !default; +$side-nav-item-padding-top: 8px !default; +$side-nav-item-padding-end: 0 !default; +$side-nav-item-padding-bottom: 10px !default; +$side-nav-item-padding-start: 15px !default; +$side-nav-item-color: var(--#{$prefix}body-color) !default; +$side-nav-item-hover-bg: var(--#{$prefix}secondary-bg) !default; +$side-nav-item-active-bg: var(--#{$prefix}tertiary-active-bg) !default; +$side-nav-item-active-border: var(--#{$prefix}primary) !default; +// scss-docs-end side-nav-variables +// End mod // Tags diff --git a/scss/boosted.scss b/scss/boosted.scss index e9c9eb904f..130a7ae4df 100644 --- a/scss/boosted.scss +++ b/scss/boosted.scss @@ -50,6 +50,7 @@ @import "footer"; @import "local-navigation"; @import "orange-navbar"; +@import "side-navigation"; @import "stepped-process"; @import "sticker"; @import "title-bars"; diff --git a/site/assets/scss/_component-examples.scss b/site/assets/scss/_component-examples.scss index ee77113db0..6784265088 100644 --- a/site/assets/scss/_component-examples.scss +++ b/site/assets/scss/_component-examples.scss @@ -250,6 +250,15 @@ } // Boosted mod +.side-nav-example { + overflow: auto; + resize: vertical; + + @include media-breakpoint-up(lg) { + height: 21.875rem; + } +} + .simple-list-example-scrollspy .active { color: var(--bs-primary); } diff --git a/site/content/docs/5.3/about/overview.md b/site/content/docs/5.3/about/overview.md index 618f24605e..096c84eb44 100644 --- a/site/content/docs/5.3/about/overview.md +++ b/site/content/docs/5.3/about/overview.md @@ -29,6 +29,7 @@ Boosted ships with custom accessible components to suit specific needs: - [Local navigation]({{< docsref "/components/local-navigation" >}}) - [Orange navbar]({{< docsref "/components/orange-navbar" >}}) - [Quantity selector]({{< docsref "/forms/quantity-selector" >}}) +- [Side navigation]({{< docsref "/components/side-navigation" >}}) - [Star rating]({{< docsref "/forms/checks-radios#star-rating" >}}) - [Stepped process]({{< docsref "/components/stepped-process" >}}) - [Sticker]({{< docsref "/components/sticker" >}}) diff --git a/site/content/docs/5.3/components/side-navigation.md b/site/content/docs/5.3/components/side-navigation.md new file mode 100644 index 0000000000..2406c213c4 --- /dev/null +++ b/site/content/docs/5.3/components/side-navigation.md @@ -0,0 +1,508 @@ +--- +layout: docs +title: Side Navigation +description: Documentation and examples for Boosted's exclusive Brand responsive side navigation. +group: components +aliases: + - "/docs/components/side-navigation/" +toc: true +added: "5.3" +--- + +## How it works + +Our side navigation is basically a `