--- title: "Theming and light/dark" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Theming and light/dark} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` # Basics ```{r} library(shiny) library(shinyglass) ui <- fluidPage( theme = glass_theme(), # or preset = "dark" / "auto" titlePanel("Liquid Glass"), plotOutput("p") ) ``` `glass_theme()` returns a **bslib** theme. Pass it as `theme =` to `fluidPage()`, `navbarPage()`, `bslib::page_sidebar()`, and other page helpers that accept a bslib theme. # Presets | `preset` | Behavior | |----------|----------| | `"light"` | Light glass pack (default) | | `"dark"` | Dark glass pack | | `"auto"` | Follows `prefers-color-scheme`; updates when the OS theme changes | Light and dark surface tokens ship as dual CSS custom-property packs on `[data-glass-preset]`. Switching preset updates `document.documentElement.dataset.glassPreset` — no Sass recompile and no full page reload. # Runtime updates from the server ```{r} ui <- fluidPage( theme = glass_theme(preset = "auto"), glass_theme_toggle(selected = "auto"), selectInput("accent", "Accent", c( Blue = "#007AFF", Purple = "#AF52DE", Orange = "#FF9500" )) ) server <- function(input, output, session) { observe_glass_theme_toggle(input, session) observeEvent(input$accent, { update_glass_theme(session, primary = input$accent) }, ignoreInit = TRUE) } ``` `update_glass_theme()` accepts: | Argument | Effect | |----------|--------| | `preset` | `"light"` / `"dark"` / `"auto"` | | `tint` | Content-aware ambient color from plots/images | | `primary` | Live accent via CSS variables (`--bs-primary`, …) | `primary` updates Bootstrap accent CSS variables so buttons, checked controls, and other accent surfaces follow without a reload. A few Sass-baked one-offs may still need a full reload to pick up a new color. # Theme toggle helper ```{r} ui <- fluidPage( theme = glass_theme(preset = "auto"), glass_theme_toggle(), # Light / Dark / Auto buttons # ... ) server <- function(input, output, session) { observe_glass_theme_toggle(input, session) } ``` Buttons call `window.shinyglass.setPreset()` immediately and also fire Shiny inputs (`glass_toggle_light`, …) so the server can stay in sync. # Behavior knobs ```{r} glass_theme( preset = "auto", primary = "#AF52DE", blur = 36, saturation = 200, radius = "1.5rem", material = "regular", # or "clear" over media-rich content intensity = 0.45, # 0 Ultra Clear → 1 Tinted (iOS 27) tint = TRUE, # sample plot/image colors into glass surfaces specular = TRUE, # pointer specular highlight nav_morph = TRUE # compact navbar while scrolling down ) ``` `material = "regular"` is the adaptive Tahoe-style fill (default). Use `"clear"` when chrome sits over rich media and labels stay bold. Use [glass_intensity_slider()] for a live Ultra Clear → Tinted control (same idea as iOS 27 Settings → Appearance → Liquid Glass): ```{r} glass_intensity_slider("glass_intensity", value = 0.45) ``` All three JS behaviors default to `TRUE` (same as 0.1.x). They respect `prefers-reduced-motion: reduce` where relevant. Advanced escape hatch: `window.__shinyglassDisableTint = true` still disables tint. # CSS variables (migration from 0.1.x) **Breaking in 0.2.0:** light/dark surfaces are dual CSS packs, not a Bootswatch `darkly` rebuild. Prefer CSS custom properties over Sass `$glass-*` tokens when customizing: ```css :root { /* shared knobs also set by glass_theme(blur=..., radius=...) */ --glass-blur: 36px; --glass-radius: 1.25rem; --bs-primary: #007AFF; /* also updated by update_glass_theme(primary=) */ --glass-primary: #007AFF; } :root[data-glass-preset="dark"] { /* override a dark-pack value if needed */ --glass-bg: rgba(255, 255, 255, 0.1); } ``` Apps that overrode Sass-only `$glass-*` tokens should target `--glass-*` or `[data-glass-preset="dark"]` instead. # Client helpers ```js window.shinyglass.setPreset("dark"); // or "light" / "auto" window.shinyglass.getPreset(); // resolved "light" | "dark" window.shinyglass.getMode(); // requested mode incl. "auto" window.shinyglass.setTint(false); window.shinyglass.setPrimary("#AF52DE"); window.shinyglass.getPrimary(); ``` # Teal ```{r} options(teal.bs_theme = glass_theme(preset = "auto")) # then teal::init(...) as usual ``` # Reduced motion When the user prefers reduced motion (`prefers-reduced-motion: reduce`): - Navbar morph transforms are disabled (JS + CSS) - Pointer specular tracking is skipped - Content tint sampling is skipped - Decorative transitions on cards/inputs/nav are removed This is automatic — no app code required. # Active-on-accent contrast Bootstrap’s `color-contrast()` often picks **black** ink for system blue `#007AFF`. `glass_theme()` forces light ink on primary fills so checked checkboxes, radios, switches, and active pagination stay readable.