Skip to content

Commit

Permalink
feat: Improve navbar compatiblity for Bootstrap 5 and light/dark modes (
Browse files Browse the repository at this point in the history
#1145)

* fix(navbar): Don't flip navbar bg in dark mode

This rule only changes the background color of the navbar,
which is not enough to fully restyle the navbar in dark mode.
It might work in the default cases, but it certainly does not
work if users have set $navbar-dark-bg or $navbar-light-bg
to appropriate values. Note that Bootstrap expects the navbar
colors to be static (i.e. the same in light and dark mode), so if
we want to support different navbar colors we'll need to
implement something better.

* fix(bootstrap): Navbar toggler icon should follow navbar color mode

Bootstrap has the navbar toggler icon follow the global color mode, which makes the icon invisible when placed on a light background in dark mode. Outside of this rule, Bootstrap expects the navbar color to be consistent in light and dark mode.

* fix(bootstrap): Keep global color mode follow

* fix: Fix `_navbar.scss` patch

* feat: Only use `.navbar-default` and `.navbar-inverse` classes in BS <5

* fix(navbar_compat): navbar light background

* feat: account for navbar in light mode region

* feat: use `--bslib-navbar-{light,dark}-bg` variables

* feat(shiny-preset): Use border-bottom for default navbar not in a dashboard page

* refactor(shiny-preset): Use Sass vars to set navbar light/dark bg

* fix(bs5): Selector on light-region navbar-toggler-icon

* fix(navbar): Dark/light adjustments need to have the lowest specificity possible

Otherwise they get in the way of local classes, e.g. `bg-blue` or `bg-primary`,
which are the primary mechanism for setting navbar color in BS5

https://getbootstrap.com/docs/5.3/components/navbar/#color-schemes

* fix(navbar): Use `:where()` to reduce navbar dark/light styles to minimal specificity

* fix(navbar): Use `:where()` when setting navbar bg to ensure that utility classes will win

* feat!(navbars): Don't use BS3 compat for navbars in BS5+

* chore: whitespace fixes
  • Loading branch information
gadenbuie authored Jan 22, 2025
1 parent f77a966 commit e0ce81d
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 34 deletions.
9 changes: 7 additions & 2 deletions R/bs-theme-preset-bootswatch.R
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,14 @@ bootswatch_bundle <- function(bootswatch, version) {
# Use local fonts (this path is relative to the bootstrap HTML dependency dir)
'$web-font-path: "font.css" !default;',
bootswatch_sass_file(bootswatch, "variables", version),
# Unless we change navbarPage()'s markup, BS4+ will likely want BS3 compatibility
# BS4 navbars are matched with BS3 for compatibility
switch_version(
version, three = "", default = bs3compat_navbar_defaults(bootswatch)
version,
three = "",
four = bs3compat_navbar_defaults(bootswatch),
# BS5 uses more neutral defaults (navbar that flips in light/dark mode)
# or requires a bit more user input
default = list()
)
),
rules = list(
Expand Down
62 changes: 39 additions & 23 deletions inst/bs3compat/_navbar_compat.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,42 +72,58 @@ ul.nav.navbar-nav {
}
}

:root {
--bslib-navbar-light-bg: var(--bslib-navbar-default-bg, var(--#{$prefix}light));
--bslib-navbar-dark-bg: var(--bslib-navbar-inverse-bg, var(--#{$prefix}black));
}

@mixin navbar-background-dark($important: false) {
background-color: var(--bslib-navbar-dark-bg) if($important, !important, null);
}

@mixin navbar-background-light($important: false) {
background-color: var(--bslib-navbar-light-bg) if($important, !important, null);
}

.navbar {

// Defaults to null (and in that case, we don't want to define the CSS var)
@if $navbar-light-bg {
--bslib-navbar-default-bg: #{$navbar-light-bg};
--bslib-navbar-light-bg: #{$navbar-light-bg};
}
@if $navbar-dark-bg {
--bslib-navbar-inverse-bg: #{$navbar-dark-bg};
}

// BS3 .navbar-default -> BS4 .navbar-light
&.navbar-default {
// Sets a variety of fg colors which are configurable via $navbar-light-* options
@extend .navbar-light;
background-color: var(--bslib-navbar-default-bg, var(--#{$prefix}light)) !important;
--bslib-navbar-dark-bg: #{$navbar-dark-bg};
}

// BS3 .navbar-inverse -> BS4 .navbar-dark
&.navbar-inverse {
// Sets a variety of fg colors which are configurable via $navbar-dark-* options
@extend .navbar-dark;
background-color: var(--bslib-navbar-inverse-bg, var(--#{$prefix}dark)) !important;
// For BS5+ lean on emphasis-color
--bs-emphasis-color: white;
--bs-emphasis-color-rgb: 255, 255, 255;
@if $bootstrap-version < 5 {
// BS3 .navbar-default -> BS4 .navbar-light
&.navbar-default {
// Sets a variety of fg colors which are configurable via $navbar-light-* options
@extend .navbar-light;
@include navbar-background-light($important: true);
}

// BS3 .navbar-inverse -> BS4 .navbar-dark
&.navbar-inverse {
// Sets a variety of fg colors which are configurable via $navbar-dark-* options
@extend .navbar-dark;
@include navbar-background-dark($important: true);
}
}
}

$enable-dark-mode: false !default;
@if $enable-dark-mode {
@include color-mode(dark) {
.navbar.navbar-default {
background-color: var(--bslib-navbar-default-bg, var(--#{$prefix}dark)) !important;
}
@if $bootstrap-version >= 5 {
.navbar {
background-color: $navbar-bg;
}

[data-bs-theme="dark"] :where(.navbar) { @include navbar-background-dark(); }
[data-bs-theme="light"] :where(.navbar), :where(.navbar) { @include navbar-background-light(); }

// These are defined *after* the above rules because we want the local version
// to win without having to resort to specificity tricks.
:where(.navbar)[data-bs-theme="dark"] { @include navbar-background-dark(); }
:where(.navbar)[data-bs-theme="light"] { @include navbar-background-light(); }
}

// Implement bs3 navbar toggler; used in Rmd websites, i.e.
Expand Down
15 changes: 8 additions & 7 deletions inst/builtin/bs5/shiny/_rules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,15 @@ $bslib-checkbox-radio-margin-right: 0.35em !default;
background-color: var(--bslib-dashboard-main-bg);
}

.navbar {
// Add border-bottom for general navbars (primarily in non-bslib contexts)
border-bottom: $card-border-width solid $card-border-color;
}

.bslib-page-navbar, .bslib-page-dashboard {
> .navbar {
@if not $navbar-light-bg and not $navbar-bg {
--bslib-navbar-default-bg: var(--#{$prefix}body-bg);
}
@if not $navbar-dark-bg and not $navbar-bg {
--bslib-navbar-inverse-bg: var(--#{$prefix}body-color);
}
// Inside bslib we only add the border on the top-level navbar
.navbar {
border-bottom: none;
}

> .navbar + div {
Expand Down
15 changes: 15 additions & 0 deletions inst/builtin/bs5/shiny/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,21 @@ $bslib-sidebar-fg: null !default;
$bslib-sidebar-fg: color-contrast($bslib-sidebar-bg);
}

// From inst/lib/bs5/scss/_variables.scss
// Repeated here so that we can set navbar light/dark to `--bs-body-bg`
$navbar-bg: null !default; // Background color for any navbarPage()
$navbar-light-bg: $navbar-bg !default; // Background color for navbarPage(inverse = FALSE)
$navbar-dark-bg: $navbar-bg !default; // Background color for navbarPage(inverse = TRUE)

@if $bslib-dashboard-design and $navbar-bg == null {
@if $navbar-light-bg == null {
$navbar-light-bg: var(--#{$prefix}body-bg);
}
@if $navbar-dark-bg == null {
$navbar-dark-bg: var(--#{$prefix}body-bg);
}
}

$border-color-translucent: if($bslib-dashboard-design, rgba(40, 70, 94, 0.1), null) !default;
$border-color-translucent-dark: if($bslib-dashboard-design, rgba(255, 255, 255, 0.1), null) !default;

Expand Down
30 changes: 28 additions & 2 deletions inst/lib/bs5/scss/_navbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// Provide a static navbar from which we expand to create full-width, fixed, and
// other navbar variations.

.navbar {
.navbar,
:where([data-bs-theme="light"]) .navbar { // bslib-patched: explicitly set navbar props in light mode regions
// scss-docs-start navbar-css-vars
--#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)};
--#{$prefix}navbar-padding-y: #{$navbar-padding-y};
Expand All @@ -26,7 +27,9 @@
--#{$prefix}navbar-toggler-focus-width: #{$navbar-toggler-focus-width};
--#{$prefix}navbar-toggler-transition: #{$navbar-toggler-transition};
// scss-docs-end navbar-css-vars
}

.navbar {
position: relative;
display: flex;
display: -webkit-flex;
Expand Down Expand Up @@ -296,6 +299,7 @@
}

.navbar-dark,
:where([data-bs-theme="dark"]) .navbar, // bslib-patched: dark mode inside dark regions
.navbar[data-bs-theme="dark"] {
// scss-docs-start navbar-dark-css-vars
--#{$prefix}navbar-color: #{$navbar-dark-color};
Expand All @@ -309,10 +313,32 @@
// scss-docs-end navbar-dark-css-vars
}

:where(.navbar[data-bs-theme="dark"] .navbar-toggler-icon) {
// bslib-patched: toggler icon should follow closest navbar color mode over global mode
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
}

@if $enable-dark-mode {
@include color-mode(dark) {
.navbar-toggler-icon {
// bslib-patched: toggler follows global theme unless in a light region
:where(.navbar:not([data-bs-theme="light"]) .navbar-toggler-icon) {
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
}
}
}

.navbar[data-bs-theme="light"] {
// bslib-patched: Make sure local light navbar overrides page global
--#{$prefix}navbar-color: #{$navbar-light-color};
--#{$prefix}navbar-hover-color: #{$navbar-light-hover-color};
--#{$prefix}navbar-disabled-color: #{$navbar-light-disabled-color};
--#{$prefix}navbar-active-color: #{$navbar-light-active-color};
--#{$prefix}navbar-brand-color: #{$navbar-light-brand-color};
--#{$prefix}navbar-brand-hover-color: #{$navbar-light-brand-hover-color};
--#{$prefix}navbar-toggler-border-color: #{$navbar-light-toggler-border-color};

.navbar-toggler-icon {
// bslib-patched: Make sure toggler icon follows local light mode, too
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)};
}
}
66 changes: 66 additions & 0 deletions tools/patches/034-bs5-navbar-bg.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
diff --git a/inst/lib/bs5/scss/_navbar.scss b/inst/lib/bs5/scss/_navbar.scss
index 988bbe09..ec497278 100644
--- a/inst/lib/bs5/scss/_navbar.scss
+++ b/inst/lib/bs5/scss/_navbar.scss
@@ -3,7 +3,8 @@
// Provide a static navbar from which we expand to create full-width, fixed, and
// other navbar variations.

-.navbar {
+.navbar,
+:where([data-bs-theme="light"]) .navbar { // bslib-patched: explicitly set navbar props in light mode regions
// scss-docs-start navbar-css-vars
--#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)};
--#{$prefix}navbar-padding-y: #{$navbar-padding-y};
@@ -26,7 +27,9 @@
--#{$prefix}navbar-toggler-focus-width: #{$navbar-toggler-focus-width};
--#{$prefix}navbar-toggler-transition: #{$navbar-toggler-transition};
// scss-docs-end navbar-css-vars
+}

+.navbar {
position: relative;
display: flex;
display: -webkit-flex;
@@ -296,6 +299,7 @@
}

.navbar-dark,
+:where([data-bs-theme="dark"]) .navbar, // bslib-patched: dark mode inside dark regions
.navbar[data-bs-theme="dark"] {
// scss-docs-start navbar-dark-css-vars
--#{$prefix}navbar-color: #{$navbar-dark-color};
@@ -309,10 +313,32 @@
// scss-docs-end navbar-dark-css-vars
}

+:where(.navbar[data-bs-theme="dark"] .navbar-toggler-icon) {
+ // bslib-patched: toggler icon should follow closest navbar color mode over global mode
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
+}
+
@if $enable-dark-mode {
@include color-mode(dark) {
- .navbar-toggler-icon {
+ // bslib-patched: toggler follows global theme unless in a light region
+ :where(.navbar:not([data-bs-theme="light"]) .navbar-toggler-icon) {
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
}
}
}
+
+.navbar[data-bs-theme="light"] {
+ // bslib-patched: Make sure local light navbar overrides page global
+ --#{$prefix}navbar-color: #{$navbar-light-color};
+ --#{$prefix}navbar-hover-color: #{$navbar-light-hover-color};
+ --#{$prefix}navbar-disabled-color: #{$navbar-light-disabled-color};
+ --#{$prefix}navbar-active-color: #{$navbar-light-active-color};
+ --#{$prefix}navbar-brand-color: #{$navbar-light-brand-color};
+ --#{$prefix}navbar-brand-hover-color: #{$navbar-light-brand-hover-color};
+ --#{$prefix}navbar-toggler-border-color: #{$navbar-light-toggler-border-color};
+
+ .navbar-toggler-icon {
+ // bslib-patched: Make sure toggler icon follows local light mode, too
+ --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)};
+ }
+}

0 comments on commit e0ce81d

Please sign in to comment.