Skip to content

Commit 5fb1f24

Browse files
authored
chore: Add AGENTS.md (#1245)
* chore: Add AGENTS.md * chore: build ignore AGENTS.md * `air format` (GitHub Actions) * Resave data (GitHub Action) * chore: Add section on server-client communication * chore: Fix tribble formatting --------- Co-authored-by: gadenbuie <[email protected]>
1 parent ba1be9a commit 5fb1f24

File tree

4 files changed

+399
-140
lines changed

4 files changed

+399
-140
lines changed

.Rbuildignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ inst/lib/bsw3/.npmignore
3232
^cran-comments\.md$
3333
^inst/examples-shiny/brand.yml/Monda\.ttf$
3434
^inst/examples-shiny/brand.yml/Monda-OFL\.txt$
35+
^AGENTS\.md$

AGENTS.md

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
# bslib
2+
3+
> Bootstrap-based UI toolkit for Shiny and R Markdown, enabling data scientists to build professional apps without web development expertise
4+
5+
## Overview
6+
7+
bslib is an R package that provides Bootstrap-based UI components and theming for Shiny web applications and R Markdown documents. It serves data scientists and analysts who want to build professional-looking apps without needing web development expertise. The package started as a way to access different Bootstrap versions in Shiny (which defaults to Bootstrap 3) and has evolved into a full-featured component library built on modern Bootstrap (v5), offering high-level UI patterns like cards, sidebars, value boxes, and responsive layouts that go beyond Bootstrap's low-level templates.
8+
9+
The package handles the complexity of Sass compilation, dependency management, and client-side interactivity, exposing a clean R API where users call functions like `card()` and `layout_column_wrap()` instead of writing HTML, CSS, or JavaScript.
10+
11+
## Purpose & Design Philosophy
12+
13+
bslib bridges the gap between R users and modern web UI design by:
14+
15+
1. **Abstracting web complexity**: Data scientists call R functions; bslib generates the HTML, manages CSS/JS dependencies, and handles Bootstrap's complexity.
16+
17+
2. **Progressive enhancement**: Components are "more than just" Bootstrap wrappers - they add features Shiny users need (like full-screen card toggles, enhanced sidebars) while maintaining Bootstrap's foundation.
18+
19+
3. **Flexible theming**: Supports both quick customization (via `brand` or `preset` arguments) and deep customization (via Sass variables), with real-time preview capability.
20+
21+
4. **Version flexibility**: Allows apps to use Bootstrap 3 (backwards compatibility), 4, or 5 (modern features) without rewriting code.
22+
23+
The central design tension: provide powerful, opinionated components that "just work" for non-web-developers, while preserving enough flexibility for advanced customization through function arguments like `class` and `style`. Components are opinionated and composable, but users with knowledge of Bootstrap can extend them comfortably.
24+
25+
## Quick Reference
26+
27+
- **Type**: R package with TypeScript components
28+
- **Languages**: R (API/components), TypeScript (interactivity), Sass (styling)
29+
- **Key Frameworks**: Bootstrap 3/4/5, Shiny, R Markdown, htmltools
30+
- **Build Tools**: roxygen2 (R docs), esbuild (TS→JS), yarn (JS deps)
31+
- **Primary Integration**: Shiny web framework for R
32+
33+
## Architecture
34+
35+
### The Two-Layer System
36+
37+
**R Layer** (`R/` directory):
38+
- **Theme API**: `bs_theme()` creates a `sass_bundle` object representing a Bootstrap theme with customizations
39+
- **UI Components**: Functions like `card()`, `sidebar()`, `value_box()` return `htmltools` tag objects
40+
- **Compilation Orchestration**: `bs_theme_dependencies()` compiles Sass to CSS, bundles JS/fonts, returns HTML dependencies
41+
42+
**TypeScript Layer** (`srcts/` directory):
43+
- **Component Enhancements**: Adds interactivity beyond Bootstrap (full-screen cards, dark mode toggle, responsive behavior)
44+
- **Web Components**: Custom elements for complex UI patterns
45+
- **Build Process**: TypeScript → esbuild → bundled JS in `inst/components/dist/`
46+
47+
### Component Architecture Pattern
48+
49+
Every bslib component follows this pattern:
50+
51+
```r
52+
my_component <- function(arg1, ..., arg2 = default) {
53+
# 1. Separate attributes and children in dots, if needed. Useful when named
54+
# attributes become attributes on a different tag than the children.
55+
dots <- separate_arguments(...) # => list(attribs = ..., children = ...)
56+
57+
# 2. Build htmltools tag structure
58+
tag <- div(
59+
class = "bslib-my-component",
60+
data-attribute = "value",
61+
!!!dots$attribs,
62+
!!!dots$children,
63+
# 3. Add component-specific JS/CSS dependencies,
64+
my_component_dependencies()
65+
)
66+
67+
# 4. Attach component dependencies (CSS/JS)
68+
tag <- tagAppendAttributes(tag, ...)
69+
tag_require(tag, version = 5) # Ensures Bootstrap 5
70+
71+
# 5. Return as HTML fragment
72+
as_fragment(tag)
73+
}
74+
```
75+
76+
Components are **composable**: `layout_sidebar(sidebar(...), card(...))` nests naturally because everything returns `htmltools` tags.
77+
78+
New components should be created in `R/{name}.R` and have a corresponding test file in `tests/testthat/test-{name}.R`.
79+
80+
### Adding TypeScript Interactivity
81+
82+
If your component needs client-side behavior, create TypeScript files in `srcts/src/components/`:
83+
84+
```
85+
srcts/src/components/
86+
my-component.ts # Component logic
87+
index.ts # Update to export your component
88+
```
89+
90+
The build process (`yarn build`) compiles all exports from `srcts/src/components/index.ts` into `inst/components/dist/components.min.js`. On the R side, attach this dependency using `component_dependencies()` so the compiled JavaScript is included when your component renders. The TypeScript code typically:
91+
- Initializes on elements with specific `data-*` attributes (e.g., `data-bslib-my-component`)
92+
- Uses Bootstrap's JavaScript API for native component integration
93+
- Registers event listeners and manages component state
94+
- Communicates with Shiny via `Shiny.setInputValue()` when needed
95+
96+
### Adding Component Styles
97+
98+
Component-specific Sass should be placed in `inst/components/scss/`, e.g. `inst/components/scss/my-component.scss`.
99+
100+
These styles are compiled as part of the theme compilation process and become themable (they can access Bootstrap variables like `$primary`, `$border-radius`, etc.).
101+
102+
The final CSS should use CSS variables (e.g. `--bslib-*`) to support runtime theming, and these variables can fall back to using Bootstrap Sass variables that are filled in during compilation.
103+
104+
### Server-Client Communication Patterns
105+
106+
There are three main ways to communicate between the Shiny server and client-side components:
107+
108+
**1. Input Bindings (for bidirectional component state)**
109+
110+
Create a full Shiny input by extending `InputBinding` and implementing `getValue()`, `setValue()`, and `receiveMessage()`. This is the most robust pattern for components that maintain state and can be updated from the server.
111+
112+
- **Example**: Sidebar (`srcts/src/components/sidebar.ts`, `R/sidebar.R`)
113+
- **Client side**: `SidebarInputBinding` extends `InputBinding`, implements `receiveMessage(el, data)` to handle server updates
114+
- **Server side**: `sidebar()` creates the input, server updates via `session$sendInputMessage(id, list(method = "open"))`
115+
- **Use when**: The component is a true input control with state that should be bookmarkable and updatable from both client and server
116+
117+
**2. Simple Input Values (for reporting client state)**
118+
119+
Use `Shiny.setInputValue()` to send values from client to server in a lightweight, non-bookmarkable way. Best for internal state reporting.
120+
121+
- **Example**: Card full-screen state (`srcts/src/components/card.ts`, `R/card.R`)
122+
- **Client side**: `Shiny.setInputValue(this.card.id + "_full_screen", isFullScreen)`
123+
- **Server side**: Read value via `input$<card_id>_full_screen` (reactive)
124+
- **Use when**: Component needs to report state changes to server but doesn't need full input binding infrastructure or bookmarking
125+
126+
**3. Custom Messages (for server-initiated updates)**
127+
128+
Use `session$sendCustomMessage()` from the server and register a handler with `Shiny.addCustomMessageHandler()` on the client. Ideal for global updates or when you don't have a specific input `id`.
129+
130+
- **Example**: Dark mode toggle (`R/input-dark-mode.R`, `srcts/src/components/webcomponents/inputDarkMode.ts`)
131+
- **Server side**: `session$sendCustomMessage("bslib.toggle-dark-mode", list(method = "toggle", value = "dark"))`
132+
- **Client side**: `Shiny.addCustomMessageHandler("bslib.toggle-dark-mode", function(data) { ... })`
133+
- **Use when**: Updates affect global state or the communication is primarily one-way (server → client)
134+
135+
### Key Design Aspects
136+
137+
**Sass Layering System**:
138+
- Bootstrap core variables (lowest priority)
139+
- Preset/Bootswatch theme overrides
140+
- User-specified variables (via `bs_theme(bg = ...)`)
141+
- Custom rules (via `bs_add_rules()`)
142+
- Highest priority wins in Sass compilation
143+
144+
**Dependency Management**:
145+
- Uses `htmltools::htmlDependency()` to track CSS/JS files
146+
- Dependencies are automatically deduplicated when tags are rendered
147+
- Each component specifies its own dependencies; htmltools resolves conflicts
148+
149+
**Version Handling**:
150+
- `inst/lib/bs3/`, `inst/lib/bs4/`, `inst/lib/bs5/` contain Bootstrap sources
151+
- Component functions check version compatibility via `tag_require(tag, version = 5)`
152+
- Some components (modern layouts, value boxes) only work with Bootstrap 5
153+
154+
## Technical Details
155+
156+
### Directory Structure
157+
158+
```
159+
bslib/
160+
├── R/ # R package source
161+
│ ├── bs-theme*.R # Theme creation and management API
162+
│ ├── bs-dependencies.R # Sass compilation and dependency bundling
163+
│ ├── card.R, sidebar.R, ... # UI component functions
164+
│ ├── layout.R, page.R # Layout containers and page templates
165+
│ ├── navs.R, nav-*.R # Navigation components (tabs, pills, navbar)
166+
│ └── utils*.R # Internal utilities
167+
168+
├── inst/ # Assets bundled with package
169+
│ ├── lib/ # Third-party libraries
170+
│ │ ├── bs3/, bs4/, bs5/ # Bootstrap source Sass and compiled JS
171+
│ │ ├── bsw3/, bsw4/, bsw5/ # Bootswatch theme Sass
172+
│ │ └── bs-colorpicker/, bs-a11y-p/ # Additional libraries
173+
│ ├── components/
174+
│ │ ├── scss/ # Custom component Sass (bslib-specific styles)
175+
│ │ └── dist/ # Compiled component JS (from srcts/)
176+
│ ├── css-precompiled/ # Precompiled CSS for stock Bootstrap builds
177+
│ │ ├── 3/, 4/, 5/ # Organized by Bootstrap version
178+
│ ├── builtin/bs5/shiny/ # Built-in "shiny" preset theme assets
179+
│ ├── examples-shiny/ # Demo Shiny apps (runExample())
180+
│ └── fonts/ # Font files used by components
181+
182+
├── srcts/ # TypeScript source (not in built package)
183+
│ ├── src/components/ # Component TypeScript implementations
184+
│ └── build/ # esbuild configuration
185+
186+
├── man/ # Generated R documentation (from roxygen2)
187+
├── tests/testthat/ # Unit tests
188+
├── vignettes/ # Package vignettes (guides/tutorials)
189+
├── _dev/ # Development scripts and notes
190+
├── docs/ # pkgdown website (generated)
191+
192+
├── DESCRIPTION # Package metadata and dependencies
193+
├── NAMESPACE # Exported functions (auto-generated)
194+
├── package.json, yarn.lock # Node.js dependencies for build process
195+
└── tsconfig.json # TypeScript compiler configuration
196+
```
197+
198+
### Key Components & Modules
199+
200+
**Theme System** (`R/bs-theme*.R`):
201+
- `bs_theme()`: Main entry point; creates `sass_bundle` with Bootstrap + customizations
202+
- `bs_theme_update()`, `bs_add_variables()`, `bs_add_rules()`: Modify existing themes
203+
- `bs_theme_preview()`: Interactive Shiny app for real-time theme editing
204+
- Preset system: Built-in themes (`builtin_themes()`), Bootswatch (`bootswatch_themes()`), brand.yml
205+
206+
**UI Components** (`R/card.R`, `R/sidebar.R`, `R/value-box.R`, etc.):
207+
- `card()` family: Container with header/body/footer, full-screen capability
208+
- `layout_*()` family: Column wrapping, sidebars, fillable containers
209+
- `value_box()`: Dashboard metric display with icons and styling
210+
- `navset_*()` family: Tabbed and navigation interfaces
211+
- `page_*()` family: Page-level templates (fillable, sidebar, navbar, fluid)
212+
213+
**Compilation & Dependencies** (`R/bs-dependencies.R`):
214+
- `bs_theme_dependencies()`: Compiles theme → CSS, bundles all dependencies
215+
- `bs_dependency()`: Creates themable component dependencies for extension authors
216+
- Precompilation logic: Checks for stock builds, falls back to dynamic compilation
217+
- Caching via `sass::sass_cache_context_dir()`
218+
219+
**Client-Side Enhancements** (`srcts/src/components/`):
220+
- Card full-screen mode toggle and state management
221+
- Dark mode input (`input-dark-mode.ts`)
222+
- Sidebar collapse/expand behavior
223+
- Web components for complex interactions
224+
225+
**Legacy Compatibility** (`R/navs-legacy.R`, `inst/bs3compat/`):
226+
- Bootstrap 3 compatibility shims for Shiny components
227+
- Migration helpers for transitioning apps from BS3 to BS4/5
228+
229+
## Development Workflow
230+
231+
### Getting Started
232+
233+
```r
234+
# Install package dependencies
235+
pak::local_install_dev_deps()
236+
```
237+
238+
```bash
239+
# For TypeScript development
240+
# (Requires Node.js 14+ and Yarn 1.22+)
241+
yarn install
242+
```
243+
244+
### Common Tasks
245+
246+
**Build TypeScript components**:
247+
```bash
248+
yarn build # Compile TS → JS (with type checking and linting)
249+
yarn watch # Auto-rebuild on file changes
250+
yarn check_types # TypeScript type checking only
251+
yarn check_lint # ESLint checks only
252+
```
253+
254+
**R package development**:
255+
```r
256+
devtools::load_all() # Load package for interactive development
257+
devtools::document() # Build documentation
258+
devtools::test() # Run tests
259+
devtools::test(filter = "{name}") # Test specific file
260+
devtools::check() # Check package (R CMD check) (use as final check)
261+
```
262+
263+
**Enable Shiny dev mode** (disables caching/precompilation):
264+
```r
265+
options(shiny.devmode = TRUE)
266+
# or
267+
shiny::devmode(TRUE)
268+
```
269+
270+
## Code Conventions & Standards
271+
272+
**R Code Style**:
273+
- Follow tidyverse style guide (function names use `snake_case`)
274+
- Exported functions documented with roxygen2 (`#'` comments)
275+
- Use `rlang` for tidy evaluation (`rlang::list2()`, `!!!` for splicing)
276+
277+
**TypeScript Code Style**:
278+
- ESLint + Prettier enforce formatting
279+
- Avoid `any` types; prefer explicit typing
280+
- Use `lit` or standard `CustomElement` for web components
281+
- Shiny uses jQuery internally, but all new components should strive to use standard browser APIs
282+
- External Bootstrap types via `@types/bootstrap`
283+
284+
**Naming Conventions**:
285+
- `bs_*()`: Core theming functions
286+
- `layout_*()`: Layout containers
287+
- `card_*()`, `value_box_*()`: Component families
288+
- `navset_*()`: Navigation components
289+
- Internal helpers do not need a prefix; they are simply not exported
290+
291+
**Documentation Patterns**:
292+
- Every exported function has `@export` and complete roxygen2 docs
293+
- `@family` tags group related functions
294+
- `@examplesIf rlang::is_interactive()` prevents CRAN issues
295+
- Cross-reference related functions with `[function_name()]`
296+
- Order roxygen2 tags: `@description`, sections, `@examples`, `@param`, `@return`, `@seealso`, `@family`, `@export`.
297+
- If multiple functions are documented in a single Rd file, use `@describeIn parent_function Description...` for all functions, including the parent function.
298+
299+
## Important Notes
300+
301+
**Bootstrap Version Compatibility**:
302+
- Modern components (e.g., `value_box()`, `layout_column_wrap()`) require Bootstrap 5
303+
- Components call `tag_require(tag, version = 5)` to enforce version requirements
304+
- bslib provides Bootstrap 3/4 dependencies, but its components all target BS5+
305+
306+
**htmltools Dependency System**:
307+
- Multiple components can declare the same dependency (e.g., Bootstrap CSS) - htmltools deduplicates automatically
308+
- Dependencies can be added directly into any `tag` object or `tagList()`, you do not need to use `attachDependencies()` manually
309+
310+
**Common Gotchas**:
311+
- TypeScript changes require `yarn build` before R package sees updates
312+
- `inst/` directory contents are copied to package root during installation
313+
- Sass variables use `$` prefix in Sass but `bs_theme()`/`bs_add_variables()` use R named arguments without `$`
314+
315+
## Resources
316+
317+
- **Package Website**: https://rstudio.github.io/bslib/
318+
- **GitHub Repository**: https://github.com/rstudio/bslib
319+
- **Key Articles** (on website):
320+
- [Theming](https://rstudio.github.io/bslib/articles/theming.html)
321+
- [Dashboards](https://rstudio.github.io/bslib/articles/dashboards.html)
322+
- [Cards](https://rstudio.github.io/bslib/articles/cards.html)
323+
- [Custom Components](https://rstudio.github.io/bslib/articles/custom-components.html)
324+
- **README.md**: Quick start and installation
325+
- **NEWS.md**: Changelog and release notes
326+
- **Sass Package**: https://rstudio.github.io/sass/ (powers theming)
327+
- **Bootstrap Documentation**: https://getbootstrap.com/ (underlying framework)
328+

R/sysdata.rda

-11 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)