1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
#' Add low-level theming customizations
#'
#' These functions provide direct access to the layers of a bslib theme created
#' with [bs_theme()]. Learn more about [composable Sass
#' layers](https://rstudio.github.io/sass/articles/sass.html#layering) on the
#' \pkg{sass} website.
#'
#' @details
#' Compared to higher-level theme customization available in [bs_theme()], these
#' functions are a more direct interface to Bootstrap Sass, and therefore, do
#' nothing to ensure theme customizations are portable between major Bootstrap
#' versions.
#'
#' @inheritParams bs_theme_update
#' @param ...
#' * `bs_add_variables()`: Should be named Sass variables or values that can be
#' passed in directly to the `defaults` argument of a [sass::sass_layer()].
#' * `bs_bundle()`: Should be arguments that can be handled by
#' [sass::sass_bundle()] to be appended to the `theme`
#' @param .where Whether to place the variable definitions before other Sass
#' `"defaults"`, after other Sass `"declarations"`, or after other Sass
#' `"rules"`.
#' @param .default_flag Whether or not to add a `!default` flag (if missing) to
#' variable expressions. It's recommended to keep this as `TRUE` when `.where
#' = "defaults"`.
#'
#' @return Returns a modified [bs_theme()] object.
#'
#' @family Bootstrap theme functions
#'
#' @seealso [bs_theme()] creates a Bootstrap theme object, and is the best place
#' to start learning about bslib's theming capabilities.
#'
#' @references
#' * bslib's theming capabilities are powered by
#' [the {sass} package](https://rstudio.github.io/sass/).
#' * Learn more about
#' [composable Sass layers](https://rstudio.github.io/sass/articles/sass.html#layering)
#' on the \pkg{sass} website.
#'
#' @examplesIf rlang::is_interactive()
#'
#' # Function to preview the styling a (primary) Bootstrap button
#' library(htmltools)
#' button <- tags$a(class = "btn btn-primary", href = "#", role = "button", "Hello")
#' preview_button <- function(theme) {
#' browsable(tags$body(bs_theme_dependencies(theme), button))
#' }
#'
#' # Here we start with a theme based on a Bootswatch theme,
#' # then override some variable defaults
#' theme <- bs_add_variables(
#' bs_theme(bootswatch = "sketchy", primary = "orange"),
#' "body-bg" = "#EEEEEE",
#' "font-family-base" = "monospace",
#' "font-size-base" = "1.4rem",
#' "btn-padding-y" = ".16rem",
#' "btn-padding-x" = "2rem"
#' )
#'
#' preview_button(theme)
#'
#' # If you need to set a variable based on another Bootstrap variable
#' theme <- bs_add_variables(theme, "body-color" = "$success", .where = "declarations")
#' preview_button(theme)
#'
#' # Start a new global theme and add some custom rules that
#' # use Bootstrap variables to define a custom styling for a
#' # 'person card'
#' person_rules <- system.file("custom", "person.scss", package = "bslib")
#' theme <- bs_add_rules(bs_theme(), sass::sass_file(person_rules))
#'
#' # Include custom CSS that leverages bootstrap Sass variables
#' person <- function(name, title, company) {
#' tags$div(
#' class = "person",
#' h3(class = "name", name),
#' div(class = "title", title),
#' div(class = "company", company)
#' )
#' }
#'
#' page_fluid(
#' theme = theme,
#' person("Andrew Carnegie", "Owner", "Carnegie Steel Company"),
#' person("John D. Rockefeller", "Chairman", "Standard Oil")
#' )
#'
#' @describeIn bs_bundle Add Bootstrap Sass
#' [variable defaults](https://rstudio.github.io/bslib/articles/bs5-variables/index.html).
#' @export
bs_add_variables <- function(
theme,
...,
.where = "defaults",
.default_flag = identical(.where, "defaults")
) {
assert_bs_theme(theme)
vars <- rlang::list2(...)
if (any(names2(vars) == "")) stop("Variables must be named.", call. = FALSE)
# Workaround to the problem of 'blue' winning in the scenario of:
# bs_add_variables("body-bg" = "blue")
# bs_add_variables("body-bg" = "red")
if (.default_flag) {
vars <- ensure_default_flag(vars)
}
bs_bundle(
theme,
do.call(sass_layer, rlang::list2(!!.where := vars))
)
}
# Given a named list of variable definitions,
# searches each variable's expression for a !default flag,
# and if missing, adds it.
ensure_default_flag <- function(x) {
Map(
x,
rlang::names2(x),
f = function(val, nm) {
# sass::font_collection() has its own default_flag, so warn if they conflict
if (sass::is_font_collection(val)) {
if (identical(val$default_flag, FALSE)) {
message(
"Ignoring `bs_add_variables()`'s `.default_flag = TRUE` for ",
"the ",
nm,
" variable (since it has it's own `default_flag`)."
)
}
return(val)
}
val <- paste(as_sass(val), collapse = "\n")
if (grepl("!default\\s*;*\\s*$", val)) {
val
} else {
paste(sub(";+$", "", val), "!default")
}
}
)
}
#' @describeIn bs_bundle Add additional
#' [Sass rules](https://sass-lang.com/documentation/style-rules).
#' @param rules Sass rules. Anything understood by [sass::as_sass()] may be
#' provided (e.g., a list, character vector, [sass::sass_file()], etc)
#' @export
bs_add_rules <- function(theme, rules) {
bs_bundle(theme, sass_layer(rules = rules))
}
#' @describeIn bs_bundle Add additional [Sass
#' functions](https://rstudio.github.io/sass/articles/sass.html#functions).
#' @param functions A character vector or [sass::sass_file()] containing
#' functions definitions.
#' @export
bs_add_functions <- function(theme, functions) {
bs_bundle(theme, sass_layer(functions = functions))
}
#' @describeIn bs_bundle Add additional [Sass
#' mixins](https://rstudio.github.io/sass/articles/sass.html#mixins).
#' @param mixins A character vector or [sass::sass_file()] containing
#' mixin definitions.
#' @export
bs_add_mixins <- function(theme, mixins) {
bs_bundle(theme, sass_layer(mixins = mixins))
}
#' @describeIn bs_bundle Add additional [sass::sass_bundle()] objects to an
#' existing `theme`.
#' @export
bs_bundle <- function(theme, ...) {
assert_bs_theme(theme)
structure(
sass_bundle(theme, ...),
class = class(theme)
)
}
|