--- title: "Get started with bootstrict" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with bootstrict} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` `bootstrict` re-implements the **Bootstrap 5.3** layout, content, forms and component library as Shiny UI functions. Every widget mirrors the Bootstrap 5.3 HTML structure **one-to-one**, so a designer's mockup and exported SASS variables drop straight into a Shiny application — with **minimum deviation from Shiny itself**. ```{r} library(shiny) library(bootstrict) ``` ## Why bootstrict Working with a designer who does not know Shiny is awkward for two reasons: - some Shiny components are *not* plain Bootstrap; and - some Bootstrap components are *missing* from Shiny. `bootstrict` closes that gap by giving you the whole Bootstrap 5 surface, and **nothing more** — so you can tell a designer: *"use anything from [the Bootstrap 5.3 docs](https://getbootstrap.com/docs/5.3/), but nothing else."* Bootstrap 5.3 is vendored by the package itself and compiled with [`sass`](https://rstudio.github.io/sass/), so the version reaching the browser is the one `bootstrap_version()` reports. ## A first app Every app starts with a page constructor (`bs_page()`), a theme, and Bootstrap widgets composed like any other Shiny UI: ```{r} ui <- bs_page( title = "Sign in", theme = bootstrict_theme(primary = "#ff6600"), bs_container( bs_card( bs_card_header("Sign in"), bs_card_body( bs_text_input("email", "Email", placeholder = "you@example.com"), bs_password_input("pw", "Password"), bs_button("go", "Sign in", color = "primary") ) ) ) ) server <- function(input, output, session) { observeEvent(input$go, { print(input$email) print(input$pw) }) } shinyApp(ui, server) ``` ## Conventions (minimum deviation from Shiny) Once you know these four rules you know how *every* constructor in the package behaves: - **Naming.** Every constructor is `snake_case`, prefixed `bs_` (`bs_card()`, `bs_text_input()`, …). Nothing masks a Shiny function. - **`...` works exactly like Shiny/htmltools.** **Named** arguments become HTML attributes; **unnamed** arguments become children. Extra `class` values passed through `...` are merged with the component's own classes. - **Interactive widgets take a leading `id`.** Their value is then available as `input$id`, just like a Shiny input. - **Form inputs delegate to the matching `shiny::*Input()`.** So the reactive value and every `updateXxx()` keep working identically — `bootstrict` only layers Bootstrap 5 markup, sizing, help text, switches, input groups and floating labels on top. ## The map of the package `bootstrict` covers the whole Bootstrap 5.3 catalogue. Each area has its own article: | Article | What it covers | | --- | --- | | [Theming and the designer hand-off](theming.html) | `bootstrict_theme()`, SASS variable sheets, colour modes | | [Layout and the grid](layout.html) | pages, containers, the 12-column grid, stacks | | [Content: tables, media and typography](content.html) | `bs_table()`, images, figures, blockquotes, headings, lists | | [Forms and inputs](forms.html) | every input, input groups, floating labels, validation | | [Components](components.html) | accordion, alert, badge, buttons, card, carousel, collapse, list group, progress, spinner, placeholder | | [Navigation](navigation.html) | nav & tabs, navbar, breadcrumb, pagination, dropdown | | [Overlays and server-driven interactivity](overlays.html) | modal, offcanvas, toast, tooltips/popovers, the `update_bs_*()` pattern | ## How bootstrict differs from Shiny `bootstrict` stays as close to Shiny as it can, but a handful of behaviours differ **on purpose**, to follow native Bootstrap. If you already know Shiny, these are the things to watch for — each is covered in depth in the article listed. ### Overlays live in the UI — they are not built from the server Shiny builds modals and notifications on the server (`showModal(modalDialog(...))`). `bootstrict` follows the native Bootstrap pattern: the modal, toast or offcanvas is declared **once in the UI** with an `id`, and the server only **opens or closes it by id**. | Task | Shiny | bootstrict | | --- | --- | --- | | Open a modal | `showModal(modalDialog(...))` | declare `bs_modal("id", …)`, then `show_bs_modal("id")` | | Close a modal | `removeModal()` | `hide_bs_modal("id")` | | Notification | `showNotification("…")` | `bs_notify_toast("…")` | | Offcanvas / drawer | *(not in Shiny)* | declare `bs_offcanvas("id", …)`, then `show_bs_offcanvas("id")` | Place overlay widgets at the **top level of the page** (a direct child of `bs_page()` / `bs_container()`), never nested inside a `bs_card()` — Bootstrap can otherwise clip or mis-position them. See [Overlays](overlays.html). ### Server helpers take `id` first and `session` last Shiny's updaters take the session first: `updateTextInput(session, "id", …)`. Every `bootstrict` helper takes the **id first** and the **session last and optional** (it defaults to the current reactive domain), and ids are namespaced automatically inside modules: ```{r} update_bs_tabset("tabs", selected = "profile") # no session argument needed show_bs_modal("info") ``` ### Two kinds of inputs 1. **Inputs that delegate to Shiny** — `bs_text_input()`, `bs_numeric_input()`, `bs_select_input()`, `bs_radio_input()`, `bs_checkbox_input()`, `bs_checkbox_group_input()`, `bs_file_input()`, `bs_textarea_input()`, `bs_password_input()`. They wrap the matching `shiny::*Input()`, so `input$id` **and Shiny's own `updateXxx()` keep working unchanged**. 2. **Native inputs** — `bs_range_input()`, `bs_color_input()`, `bs_date_input()`, `bs_date_range_input()` and the `.btn-check` segmented controls (`bs_radio_button_input()`, `bs_checkbox_button_input()`). Either Shiny has no equivalent, or its equivalent is not Bootstrap markup. They ship their own bindings, so drive them with `update_bs_range()` / `update_bs_color()` / `update_bs_date_input()` / `update_bs_toggle_buttons()`. ### `bs_button()` is an action button only when given an id `bs_button("go", "Go")` behaves exactly like `shiny::actionButton()` — `input$go` is the click count. Called **without** an `id` it is an inert, styled button; reactivity is opt-in. ## Run the bundled apps ```{r} # The catalogue: every widget, one card each. shiny::runApp(system.file("examples/demo", package = "bootstrict")) # The showcase: Quake Watch, a seismic monitor built on datasets::quakes. shiny::runApp(system.file("examples/quakewatch", package = "bootstrict")) ```