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.
Working with a designer who does not know Shiny is awkward for two reasons:
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,
but nothing else.” Bootstrap 5.3 is vendored by the package itself
and compiled with sass, so the
version reaching the browser is the one bootstrap_version()
reports.
Every app starts with a page constructor (bs_page()), a
theme, and Bootstrap widgets composed like any other Shiny UI:
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 = "[email protected]"),
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)Once you know these four rules you know how every constructor in the package behaves:
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.id.
Their value is then available as input$id, just like a
Shiny input.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.bootstrict covers the whole Bootstrap 5.3 catalogue.
Each area has its own article:
| Article | What it covers |
|---|---|
| Theming and the designer hand-off | bootstrict_theme(), SASS variable sheets, colour
modes |
| Layout and the grid | pages, containers, the 12-column grid, stacks |
| Content: tables, media and typography | bs_table(), images, figures, blockquotes, headings,
lists |
| Forms and inputs | every input, input groups, floating labels, validation |
| Components | accordion, alert, badge, buttons, card, carousel, collapse, list group, progress, spinner, placeholder |
| Navigation | nav & tabs, navbar, breadcrumb, pagination, dropdown |
| Overlays and server-driven interactivity | modal, offcanvas, toast, tooltips/popovers, the
update_bs_*() pattern |
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.
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.
id first and session
lastShiny’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:
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.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().