actionLink() and downloadLink(). Checkbox and radio
rows are at least 44px tall; the marks stay about 18px. Slider thumbs
keep their size and use a 44px hit area. Harbor notes also checks the
send button, edit button, composer, sidebar toggle, sources button,
history row, scroll-to-bottom, the thinking disclosure, source-pill hit
area, and the sources close button.html and body use overflow-x: clip.0.5, Apple's midpoint between Ultra Clear and
Tinted (previously 0.45). Apps that do not set intensity look slightly
more tinted, with a stronger rim, lip, and edge and a narrower, more even
blur range. Colored edge tokens (--glass-refract-a, --glass-refract-b)
and the backdrop brightness and contrast boosts scale with intensity.specular = TRUE still paints the
highlights. specular = FALSE hides them. iOS 27 does not tilt glass.
Pointer tracking is not available in 0.5.0.refraction = FALSE turns the lens off. Reduced transparency, increased
contrast, forced colors, and flatten mode do the same.morph = FALSE disables that. Click targets stay on
the original controls.press = TRUE). Reduced motion skips the spring.minimize_tabs = TRUE). Reduced motion keeps them full
size. Navbar compact-on-scroll is still nav_morph.icon_style = "clear"). Light mode uses #1d1d1f so the
glyph stays above the contrast floor. Dark mode uses near-white. Primary
buttons keep their own ink. "color" leaves author colors. "all" also
restyles icons inside cards.material = "clear" is deprecated. Pass intensity instead. Existing
calls still work: when intensity is omitted they map to 0.12, warn,
and stay clearer than the midpoint. update_glass_theme(material = "clear") and setMaterial("clear") do the same.floating_sidebar = TRUE) and off by default.vignette("migrating") lists the visual and API changes and shows how
to approximate the 0.4.0 look.shinyglass (also shinyglass-package) is
the usage guide: a minimal app, how to add glass to an existing bslib
or fluidPage() app, key options, the 0.5.0 migration, and common
pitfalls with their fixes. vignette("shinyglass") is that same guide
and is what browseVignettes("shinyglass") lists. Help pages state
types, defaults, and minimal examples, and they cross-link by topic.
Argument errors name the argument and the value to pass.glass_theme(), glass_page(), and update_glass_theme() take
refraction, morph, press, scroll_edge, minimize_tabs,
icon_style, and floating_sidebar. Tokens include --glass-ior,
--glass-refraction-scale, --glass-dispersion, --glass-diffusion,
--glass-specular-top, --glass-specular-bottom, --glass-edge,
--glass-press-scale, --glass-scroll-edge-fade, --glass-minimize-scale,
and --glass-icon-clear.scroll_edge = TRUE). This is the 0.4.0 scroll-edge
toolbar, now a theme argument.glass_page_react() and
glass_theme_dependencies() attach inst/js/shinyglass-react.js, an
ES module (html dependency shinyglass-react) that registers
useGlassTheme, GlassPage, GlassSidebar, GlassSurface,
GlassCard, GlassButton, and the small layout helpers on
window.shinyglass and as named exports. The module uses
window.shinyreact.React (it does not ship a second React or an npm
package). Paint stays on the existing --glass-* tokens via
.glass-page, .glass-sidebar, .glass-button, and .glass-surface.
useGlassTheme() follows mode, resolved preset, scene, material, and
intensity, and reports reducedTransparency, increasedContrast, and
forcedColors, plus refraction, morph, press, scroll edge, tab
minimize, icon style, and floating sidebar. GlassIntensitySlider runs
from Ultra Clear to Tinted (default 0.5). GlassSidebar floats when
floating is set or the theme opts in. See vignette("shinyreact").glass_page_react() passes glass_theme() as
theme so Liquid Glass CSS is not stripped (an unnamed theme hits
shinyreact's no-Bootstrap page and drops the compiled stylesheet).
glass_theme_dependencies() is the same asset list for
page_react_html(extra_deps = ). Opt-in .glass-surface /
.glass-muted follow the live light/dark packs. Document class rules
do not cross a shadow root; --glass-* custom properties do, and
window.shinyglass.adoptShadow() injects a .glass-surface rule into
a root the client created. Example:
system.file("examples/shinyreact-glass", package = "shinyglass").inst/js/glass-ios27.js (not copied
from the reference projects). The approach follows public
backdrop-filter: url() lens work: Surdeddd/liquidglassjs,
nikdelvin/liquid-glass, and archisvaze/liquid-glass.inst/examples/compat-glass.R with Playwright
coverage in inst/scripts/browser/compat.spec.js. It checks common
inputs, DT, reactable, plotly, leaflet, ggplot2, echarts4r,
shinyWidgets, modals, tooltips, modules, dynamic UI, dark mode, scroll,
and print under the iOS 27 defaults and with refraction fallback.inst/examples/shinychat-glass) joins the five-app demo
set with basics, dashboard, inputs, and Olympics. plotly + gt stays a
local example (inst/examples/plotly-gt-demo.R) and stays in the deploy
catalog, outside that five-app set. Harbor notes uses the dusk wallpaper,
streams a local script (thinking, a tool card, and a citation) with no API
key, and uses ellmer::chat_google_gemini() (Gemini Flash) when
GEMINI_API_KEY or GOOGLE_API_KEY is set. Each visitor gets 8
messages of at most 400 characters, shared with the phone preview.
Quota and other API errors fall back to the local script. Live Harbor
notes replies stay specific to shinyglass (glass_theme(),
glass_page(), the intensity slider, the physics, and shinychat).
Deploy key chat (shinyglass-chat). When GEMINI_API_KEY is present
in the deploy environment, the chat bundle gets a temporary .Renviron.
shinyapps.io does not accept rsconnect envVars. The value is not
logged. Hosted app:
Harbor notes.page_chat() and chat_ui() pick up the glass pack:
full-window header and history sidebar, message bubbles, a floating
composer, thinking panels, tool rows, citation chips, artifact
previews, toolbars, and the slash-command menu. Chat replies use a
neutral glass bubble, including while the reply is streaming.
User bubbles stay the tinted pill. A scrolled transcript fades under
the header and the composer instead of clipping on the bar. On a
coarse pointer the send button, edit button, and composer are at
least 44px, and the footer keeps the bottom safe-area inset. The
typed "/" in the composer stays visible when the slash menu opens.
Refraction, morph, press, and the scroll edge stay on. Example:
inst/examples/shinychat-glass (local echo, no API key). Harbor
notes shows a Live (Gemini) or Demo replies chip, lists citations
from the scripted reply (and from Gemini when grounding is present),
and keeps the theme and the conversation in the browser.offcanvas() panels use the glass material, scale in from
the trigger, and show a scroll edge on the header. Bootstrap still
owns the slide.?frame=0, coarse
pointer) on a wide fine-pointer screen. A phone never gets the control.page_sidebar() or layout_sidebar() opens as
a full-height overlay. The collapse toggle stays above the panel and
closes it, and a tap outside the panel closes it too. The open panel no
longer wears a white band across the top in dark mode.actionLink() / downloadLink(). Those links keep
their text size. Nav tabs do not scale when pressed.glass_page().plot_surface = "clear", so the text does not sit on the
wallpaper.verbatimTextOutput() inside a card uses the card surface, so a live
values panel matches the cards next to it.--glass-* values the
intensity script writes. Reduce Transparency uses opaque fills and no
backdrop blur (the same idea as flatten mode). Increase Contrast and
forced colors are watched live. The page sets data-glass-contrast,
data-glass-forced-colors, and data-glass-reduced-transparency on
<html>, and matching CSS rules use !important.bslib::input_dark_mode() switches the glass pack when it writes
data-bs-theme.materialSwitch() gets a 24px hit target so the knob stays
inside glass cards and accepts clicks.glass_theme(plot_surface = "opaque") densifies plot, plotly, gt, and DT
hosts (~94% panel fill, readable ink) while chrome stays Liquid Glass.
Default "clear" keeps wallpaper show-through. Toggle live with
glass_plot_surface_input(), update_glass_theme(plot_surface = ),
window.shinyglass.setPlotSurface(), or .glass-plot-surface-opaque.
theme_glass(), plotly_glass(), and gt_theme_glass() accept
surface = "opaque". dt_options_glass() is an optional DT helper.aurora, harbor, and grove join
tahoe / dusk / mesh. glass_scenes() / glass_scene_input() list
and switch them. User wallpaper= photos are blurred and washed
(--glass-wallpaper-wash) so body ink stays above the contrast floor
(4.5:1 text / 3:1 large chrome).glass_flatten(), glass_theme(flatten = ), update_glass_theme(),
window.shinyglass.setFlatten() / .enterFlatten() / .exitFlatten(),
and ?glass_flatten=1. @media print does the same automatically.--glass-* tokens: glass_css_tokens(), glass_token_pack(),
glass_add_tokens(), glass_theme(tokens = ), and
update_glass_theme(tokens = ) / window.shinyglass.setTokens().0.45). Dark bslib sidebars keep
rim/lip/side-stroke edges instead of a shadow-only look.body.glass-scroll-edge) when content
sits under floating nav. Contrast-only: stays on under
prefers-reduced-motion; prefers-reduced-transparency still forces
Tinted.page_navbar / navsets, nested cards and
value boxes, sidebars, accordion, tooltips/popovers, and modals.
Focus-visible rings and overlay stacking (tooltip 1090, popover 1085,
menus 1080) apply to every glass_theme() app.zIndex = 1080) unless the app sets those
options.plot_surface, scenes, flatten, persist, and JS knobs on
glass_theme().glass_theme() no longer errors when scene is the default name vector
(R 4.4+ && length check) or a subset of scene names.tokens= stay distinct from the compiled Sass pack (empty
overrides no longer inject glass_bg as a public CSS token).glass_theme(persist = TRUE) stores preset, intensity,
accent, material, and scene in localStorage per app path.theme_glass(), plotly_glass(), and gt_theme_glass() match plot and
table ink to the resolved light/dark pack. glass_plot_colors() exposes
the palette.glass_accent_input() wells (blue / purple / pink / orange /
green / teal). Named colors like primary = "purple" are accepted.glass_page() / observe_glass() assemble theme + toggle + intensity +
accent in one call. Persistence defaults to on.tahoe, dusk, mesh) and optional photo
wallpaper =. update_glass_theme() can change material,
ambient_motion, and scene live.glass_theme(ambient_motion = FALSE) disables decorative ambient
animation independently of pointer highlights and navbar motion.prefers-contrast: more) and Windows forced-colors
surfaces. Reduced-motion and reduced-transparency follow the OS live.rings.jpg.theme_glass() uses grid-safe colors (ggplot2 cannot paint CSS rgba()).[data-glass-preset]. Switching preset no longer requires recompiling Sass
or reloading the page. Apps that overrode Sass-only $glass-* tokens should
target --glass-* CSS variables (or [data-glass-preset="dark"]) instead.glass_theme() always builds on the Bootstrap 5 base theme (not Bootswatch
darkly for dark). Dark appearance comes from the CSS variable pack so
light↔dark can toggle live. Visuals should match 0.1.x closely; if you relied
on darkly-specific chrome outside glass surfaces, re-check dark mode.window.__shinyglassDisableTint still works, but the
supported API is glass_theme(tint = FALSE) / update_glass_theme(tint = …).Light / Dark / Auto is an iOS-style segmented control: theme-aware glass thumb (changes in light vs dark), body ink on every chip, and a delegated click so Auto applies even when host pages rewrite inline handlers.
Theme toggle applies the preset once (no inline handler + server echo storm), so plots no longer flicker when switching Light / Dark.
Default actionButton() / .btn-link ink follows theme tokens
(--glass-body-color / --glass-accent) instead of compiled $primary.
[glass_resolved_preset()] reports the resolved light/dark appearance so plots do not stay light-inked when the user picks Auto.
Dark pack no longer re-locks default / outline / download labels to compiled
$primary. Download links and ghost focus rings follow --glass-accent.
Accent <select id="accent"> applies setPrimary immediately.
[glass_intensity_slider()] / [observe_glass_intensity()]: iOS 27-style
Ultra Clear → Tinted control. glass_theme(intensity = ) sets the
starting value (0–1, default 0.45); [update_glass_theme()] and
window.shinyglass.setIntensity() update it live.
[glass_preset_input()] / [observe_glass_preset_input()]: client-first
Light/Dark/Auto select (applies setPreset immediately; server sync is
optional). Prefer this over a bare selectInput() + update_glass_theme()
alone on hosts that rewrite custom messages.
Theme delivery is dual-channel (shinyglass + legacy glassPreset) with a
self-healing oncustommessage proxy so preset/intensity/showModal survive
host message rewrites (e.g. shinyapps.io).
Demo app intensity-slider-demo.R; live shinyglass-demo
and shinyglass-dashboard
ship glass_intensity_slider() in the UI.
iOS 27 material polish: stronger darkened edges, brighter specular highlights, intensity-scaled fill/blur for clearer layer separation and readability.
Dark-mode nav tab ink keeps WCAG UI 3:1 on glass (inactive tabs).
Closer iOS 27 Liquid Glass: defined borders (not shadow-only), chromatic edge
refraction cues, lighter dark-mode glass, thicker elevated chrome, ambient
specular drift + pointer override, stronger diffusion blur defaults
(blur = 36, saturation = 200).
Runtime light/dark switching via document.documentElement.dataset.glassPreset
without a full reload.
glass_theme(preset = "auto") follows prefers-color-scheme and updates when
the OS theme changes.
[update_glass_theme()] to change preset, tint, and primary (live
accent via --glass-accent / --bs-primary) from the server. Recolors primary
buttons, Shiny checkboxInput() / radios / switches, ion.rangeSlider bars,
value chips, and active nav/list items without a Sass recompile.
[glass_theme_toggle()] / [observe_glass_theme_toggle()] drop-in Light / Dark / Auto buttons.
Behavior knobs on [glass_theme()]: tint, specular, and nav_morph
(all default TRUE, matching 0.1.x JS behavior).
Client helpers: window.shinyglass.setPreset() / .getPreset() /
.setTint() / .setPrimary() / .getPrimary().
Liquid Glass material fidelity (Tahoe / iOS 26 inspired): multi-layer surfaces (illumination fill, dual-edge rim/lip, ambient + pointer specular), chrome vs content hierarchy (less glass-on-glass), softer wallpaper orbs, larger concentric radii, capsule-leaning controls.
New glass_theme(material = "regular"|"clear"). Clear is more transparent
for media-rich layouts. Defaults: blur = 36, radius = "1.5rem",
saturation = 200.
Value boxes are tinted glass (theme-color wash + body ink), not solid
.bg-* slabs. Plots and DT/gt hosts inside cards drop the extra glass
frame so page orbs show through (renderPlot(bg = "transparent") in
bundled demos).
DataTables / Bootstrap pagination chips: style the visible .page-link
surface (DT uses <li.paginate_button><a.page-link>). Fixes solid black
page numbers and light-grey ellipsis chips in dark mode on the dashboard
demo / README screenshots.
Overlay chrome (datepicker popup, Shiny notification types, generic
accordion, Bootstrap tooltip/popover/toast, .btn-close, file Browse chip)
now uses the glass menu pack so dark mode is not leftover Bootstrap light
hex. Datepicker is hoisted to document.body (Shiny 1.14 bsDatepicker)
so card/navbar backdrop-filter no longer clips the calendar. Navbar
overflow is visible so nav_menu dropdowns escape the glass bar.
Kitchen-sink example: chrome-kitchen-sink.R (page_navbar,
nav_menu, showModal(), all four showNotification() types).
Visual QA harness: inst/scripts/audit-glass-contrast.R (+ .js) runs a
dual-theme computed-style audit (low contrast, solid-black chips, Bootstrap
light greys in dark, dark ink on accent, DT pagination structure) across
demo / dashboard / inputs-gallery / chrome kitchen-sink. Opens datepicker,
navbar menu, notifications, accordion, tooltip/popover, and server modal
before sampling. Checklist and definition of done in
inst/scripts/VISUAL-QA.md.
Selectize fields: force body/menu ink on inputs and multi-select tags
(selectize default near-black item text failed dark mode); opaque
--glass-menu-bg fills in dark; dropdown options use menu colors.
Ecosystem chrome: plotly modebar + frame, gt tables, waiter /
shinybusy overlays, shinyalert/SweetAlert2 popups, denser
shinydashboardPlus controlbar/dropdowns/user menu, light
rhandsontable / echarts/highcharter hosts. Examples:
plotly-gt-demo.R, shinydashboardPlus-glass-demo.R.
plotly + gt stays a local example (plotly-gt-demo.R). README
light/dark screenshots for plotly+gt. The hosted copy is archived.
CI workflow visual-qa (testthat + dual-theme contrast audit) and committed
inst/scripts/audit-baseline.json.
Navbar brand / page title ink uses --glass-body-color (no more white brand
on light glass). Plotly hosts no longer clip x-axis titles.
Active-on-accent contrast: lower Bootstrap $min-contrast-ratio to 3 so
color-contrast() picks white on brand blues/purples (white on #007AFF is
~4.02:1 and failed the old 4.5 gate). Solid .bg-* / .text-bg-* fills
(including bslib::value_box(theme=)) get luminance-based ink via
glass-on() / --glass-on-*, not body color on solid primary. Safety nets
remain for checks, radios, switches, pagination, and list-group active.
Accent rules target Shiny markup (.shiny-input-checkbox) as well as
Bootstrap .form-check-input.
Plot / image outputs get a softer glass frame (lighter nesting inside cards).
prefers-reduced-motion: reduce disables nav morph, specular, tint sampling,
and decorative transitions (JS + CSS).
Demo and bslib dashboard examples: theme toggle helper; dashboard accent
select calls update_glass_theme(primary = …).
Vignettes cover migration, primary updates, toggle helper, and reduced motion.
Unit tests cover dual CSS packs, head script knobs, active-color vars, and
update_glass_theme() messaging.
shiny-glass.js no longer crashes when the script loads in <head> before
document.body exists.bslib::sidebar() / page_sidebar() layout no longer collapses to a
full-width stack. Glass floating-sidebar rules now win over bslib's later
position/width cascade, and open sidebars reserve space in main so value
boxes and cards stay fully visible beside the chrome (not under it).layout_sidebar() no longer double-floats. Inner sidebars keep
glass styling but use bslib's in-flow grid so content is not pushed into a
large empty gutter beside a second absolute panel.shinydashboard and bs4Dash) gets stronger
glass overlay: translucent header/sidebar, soft-tinted value/info boxes, and
clearer menu active states, without breaking AdminLTE layout.glass_theme() returns a bslib theme with
Liquid Glass styling for shiny apps: translucent
surfaces, backdrop blur, soft depth, and system typography.fluidPage(), navbarPage(), bslib::page_sidebar(), and other
page functions that accept a bslib theme. Pass the theme via
theme = glass_theme(), or for teal
apps via options(teal.bs_theme = glass_theme()).