Changes in version 0.0.0.9003 Components - New block_accordion() / block_accordion_item() / update_block_accordion(): vertically stacked collapsible content sections (shadcn accordion) for FAQs, grouped settings, and filters. Supports type = "single" (one open at a time, optionally collapsible to none) and type = "multiple" (independent), an initial open set, per-item icon and disabled, and animated panel height. Panel bodies are arbitrary live Shiny/htmltools content, so reactive outputs inside a closed section keep working. ... supports rlang's !!! splice, so a programmatically built list of items can be passed directly (block_accordion(!!!items)). When id is set, input$ reports the open value(s) - a string/NULL (single) or a character vector (multiple) - and update_block_accordion() opens/closes items from the server. Triggers carry aria-expanded/aria-controls, closed panels are inert, and arrow/home/end move focus between headers. The lower-level block_collapsible() is deferred (#91). - New block_dropdown_menu() / update_block_dropdown_menu(): a portal-rendered action menu (shadcn dropdown-menu) built from dropdown_menu_item(), dropdown_menu_label(), and dropdown_menu_separator() parts. The trigger accepts a string label or an arbitrary tag (icon button, avatar); items support leading icons, right-aligned shortcut hints, disabled, and a destructive variant. Choosing an item reports its value to input$ as an event (fires again on a repeat choice, like an action button that carries a value). Full keyboard support — arrow/home/end navigation, enter/space to activate, escape/tab to close, and typeahead — with role="menu" semantics and focus return to the trigger. Checkbox/radio items and sub-menus are deferred to follow-up work (#86). - New block_combobox() / update_block_combobox(): a searchable select (shadcn Command + Popover) with type-to-filter over choices, keyboard navigation, and a no-results empty state. Supports single and multiple modes (chips with an optional max_items cap), search_placeholder/empty_message text, and the same hidden-native value bridge and updater contract as block_select(). Closes the biggest catalog gap for selectizeInput() migration (#85). - New block_toggle_group() / update_block_toggle_group(): a segmented single/multiple toggle input (view switchers, formatting toolbars) reporting a string (single) or character vector (multiple) to input$. Supports default/outline variants, three sizes, per-choice icons (vendored names or tags) with optional icon-only items, and group-wide or per-item disabling (#93). - block_breadcrumb(), block_breadcrumb_item(), and block_breadcrumb_ellipsis() are new (issue #92): a static page-shell breadcrumb trail following the shadcn breadcrumb pattern. The trail renders a nav[aria-label="breadcrumb"] landmark; separators (default chevron, customizable per trail via separator = as a string or tag) are auto-inserted between entries and hidden from assistive technology; current = TRUE marks the current page with a non-interactive aria-current="page" span; and block_breadcrumb_ellipsis() collapses long middles behind a decorative glyph with a visually hidden label. Pure htmltools markup — no runtime payload or Shiny binding (static links are v1 per the issue; nav-input integration was deliberately deferred). - block_input(type = "number") now has a first-class numeric contract (issue #96). New min, max, and step arguments (number type only) drive increment/decrement stepper buttons rendered inside the control's right edge, the native arrow-key stepping, and stepper bound clamping; update_block_input() accepts the same arguments (NULL clears a bound). input$ for number inputs now reports a numeric value like shiny::numericInput() — NA while the field is empty or unparseable — instead of a character string; other types are unchanged. Bug fixes - block_date_picker() and block_date_range_picker() now format POSIXct/POSIXlt values in the value's own timezone. Previously they routed through as.Date(), which assumes UTC and could report the wrong calendar date (e.g. a morning timestamp in a UTC+ timezone normalized to the previous day). - block_field_invalid() now generates its error-message ids from an internal counter instead of runif(). Ids stay unique when the app seeds the RNG (previously two fields built after the same set.seed() shared an id, breaking aria-describedby wiring), and building UI no longer consumes from the user's RNG stream. - update_block_*() helpers no longer fail when called through a ...-forwarding wrapper (function(...) update_block_input(...)), a common pattern in module helper code. - Runtime mount ids are now collision-free across input ids that sanitize to the same slug (e.g. "a.b" vs "a-b"); a short hash disambiguates them, so server updates always reach the right element. Ids without special characters are unchanged. - block_select() and update_block_select() now reject grouped (nested) choices with a clear error instead of silently flattening them into mangled "Group.label" labels. Grouped choices were never supported; previously they produced wrong output without warning. - Choice-style arguments are matched exactly everywhere: passing a partial vector of otherwise-valid values (e.g. size = c("lg", "sm")) or a partial string (e.g. type = "pass") is now an error instead of silently resolving to one of them. Function-signature defaults are unaffected. - Server updates for update_block_dialog(class = ) and update_block_file_input(class = ) now use the same payload key as every other component. - The dependency asset-version stamp is computed once per R process instead of stat-ing every bundled asset file on each block_*() call. Set options(shinyblocks.asset_version_cache = FALSE) to recompute per call during asset development. 0.0.0.9003 Components - block_nav() now accepts block_nav_group() collapsible groups and block_nav_label() section captions, letting sidebars express nested navigation while preserving the existing leaf-only Shiny input contract. Nested block_nav_item() values are validated for non-empty unique values in input navs (#80). - block_nav() can now act as a Shiny input. Passing block_nav(id = "page") registers input$page; clicking a block_nav_item("Users", value = "users") reports that value, moves the selected highlight, and no longer follows the link — so a sidebar can drive page navigation the way a Shiny tabset does, with pure-R shiny::conditionalPanel() / renderUI() and no custom JavaScript. The new update_block_nav() selects an item from the server (mirroring update_block_tabs()), and block_nav_item() gains a value argument (defaulting to a string label). Without an id the items stay plain links, unchanged. The layout showcase and docs-site playground use it for a working Dashboard/Users switch. - The standalone Nav Item playground has been retired from both the showcase and the docs site; block_nav() / block_nav_item() are now demonstrated and documented inside the Layout shell, whose playground (showcase and docs-site) carries a matching "Toggle nav" server-update action and folds the block_nav / block_nav_item API reference into the Layout page. - block_tabs(id = ...) and block_nav(id = ...) are now backed by real Shiny InputBindings (shinyblocks.tabs / shinyblocks.nav) instead of the previous Shiny.setInputValue + MutationObserver + sb:tabs/sb:nav custom-message wiring. Behaviour is unchanged for app authors — the inputs still report the selected value, update_block_tabs() / update_block_nav() still select from the server (now via sendInputMessage()), and notify = FALSE still moves the highlight silently — but selection and keyboard handling are delegated at the document, so dynamically inserted tabsets and navs bind and re-bind through Shiny.bindAll with no per-element rewiring. Selecting the already-active item no longer re-fires the input (change semantics, matching Shiny's own tabset input). - Fixed block_card() spacing after its conversion to plain htmltools markup. Shared style-profile defaults now live on .sb-app as well as runtime roots, restoring card padding, region gaps, heading font, and surface shadow while preserving block_style() overrides. - Fixed the responsive block_sidebar() drawer so its token-backed surface and explicit shell layer remain opaque and interactive above the backdrop, form controls, and plot outputs in narrow app and embedded Shinylive viewports. Desktop collapse styling is now breakpoint-scoped, so a collapsed sidebar opened as a mobile drawer restores its title and navigation labels instead of rendering as an oversized icon-only panel. - block_spinner() now supports an icon argument for choosing among loader, pinwheel, refresh, rotate, and dashed-circle spinner glyphs. - block_task_button() and update_block_task_button() are new. A package-owned action input that locks itself synchronously on click (rejecting a same-tick double click before React reconciles), shows a busy label and decorative spinner, and reports a shinyActionButtonValue so input$ reads like shiny::actionButton(). By default it returns to ready after the reactive flush the click triggered; auto_reset = FALSE keeps it busy until the server releases it. update_block_task_button(state = "busy") takes manual control (an automatic reset will not release it) until state = "ready". The busy state is announced once via a persistent visually-hidden role="status" region; the spinner is decorative. The showcase and docs-site ship an interactive playground (issue #69). - block_image_output() and block_plot_output() are new. They add shadcn-styled frames (aspect box, object-fit, border, radius, optional caption) around Shiny's two reactive raster outputs — block_image_output() wraps shiny::imageOutput() (renderImage()); block_plot_output() wraps shiny::plotOutput() (renderPlot(), covering base graphics, ggplot2, and lattice). They are R-side composition primitives: app-author server code stays vanilla Shiny (output$id <- renderImage(...) / renderPlot(...) unchanged) and Shiny keeps owning content serving, resize/recalc, and click/hover/brush coordinates. fill defaults match each underlying Shiny output (FALSE for image, !inline for plot). The image's accessible name (alt) remains server-controlled via renderImage(alt=) / renderPlot(alt=). The showcase and docs-site ship a full interactive playground (Content/State/Styling controls, live renderPlot()/renderImage() demos, and an API table) (issue #67). - Added block_stack(), block_cluster(), and block_grid() as package-owned R layout primitives with semantic spacing, alignment, wrapping, and responsive grid composition (issue #72). block_grid(min_width =) is strictly validated as a single non-negative CSS length or percentage — it rejects calc(), CSS-wide keywords, and any value containing extra declarations, closing the inline-style injection vector that validateCssUnit() left open — and the grid lets its items shrink to their track so wide or unbreakable children no longer overflow on narrow viewports. 0.0.0.9002 Highlights - shinyblocks is now safer to embed in existing Shiny and bslib apps: package resets, design tokens, and theme storage are scoped to shinyblocks instead of the whole page (issue #40). - The showcase is no longer included in the built package tarball. run_showcase() remains available from a source checkout and now explains clearly when showcase assets are absent. - The docs site and local showcase now share more consistent interactive playgrounds, including better light/dark behavior in embedded Shinylive examples (issue #21, issue #32). Components - block_alert() gained an action slot via block_alert_action(), plus success, warning, and info variants backed by semantic theme tokens (issue #32, issue #36). - block_button() can now be updated from the server with update_block_button(), including label, variant, size, icon, disabled state, style, and class. - block_code() gained line numbers, copy-to-clipboard support, an optional editor-style header, and broader syntax highlighting for HTML/XML/SVG, CSS/SCSS/Less, JSON, SQL, shell, R, Python, and JavaScript/TypeScript (issue #32). - block_date_picker() and update_block_date_picker() are new. A package-owned date input whose input$ is a length-1 Date, matching shiny::dateInput(), but rendered as a shadcn-style trigger button + portaled calendar instead of wrapping the native widget. The calendar is hand-rolled (no react-day-picker/date-fns) with month navigation, keyboard movement (arrows / PageUp / PageDown / Enter / Escape), and min/max day disabling. Accepts Date/POSIX/"yyyy-mm-dd" values, min/max bounds, weekstart, display format, and disabled/invalid states; value = NULL starts empty. The updater supports a clear = TRUE sentinel and clearable min/max. The trigger and portaled calendar are token-driven for full light/dark and block_theme() parity (selected day uses --primary, today and hover use --accent). The showcase and docs-site ship a full interactive playground (Content/State/Actions/Styling controls, live input$ value, and an API table) (issue #59). - block_date_range_picker() and update_block_date_range_picker() are new. A package-owned range date input whose input$ is a length-2 Date c(start, end), matching shiny::dateRangeInput(), transported over the same shiny.date binding (an empty or incomplete range reports NULL). Accepts Date/POSIX/"yyyy-mm-dd" endpoints, min/max bounds, a display separator, weekstart, display format, and disabled/invalid states; start = NULL/end = NULL starts empty and a reversed start/end is silently ordered. The updater supports independent start/end changes, a clear = TRUE sentinel, and clearable min/max. The trigger and portaled range calendar are token-driven for full light/dark and block_theme() parity (the committed range uses --primary/--accent, the in-progress hover band uses a reduced-emphasis tint). The showcase and docs-site ship a full interactive playground (Content/State/Actions/Styling controls, live input$ value, and an API table) (issue #61). - block_file_input() is new. It uses Shiny's native upload binding, supports multiple, accept, disabled/invalid states, custom button text, placeholder text, upload progress, and an optional drag-and-drop variant = "dropzone" (issue #54, issue #56). - update_block_file_input() is new. It can update file-input labels, variant, dropzone content, accept/multiple settings, disabled/invalid state, style, class, and can clear the current selection with reset = TRUE (issue #54, issue #56). - block_icon() gained semantic color choices and a real size argument ("sm", "default", "lg", and "xl") instead of relying on ad hoc inline dimensions (issue #32). - block_progress(), update_block_progress(), and inc_block_progress() are new. An embedded, shadcn-style progress bar that renders inline where it is placed rather than as a Shiny Progress notification panel. It is display-only and server-addressable (addressed by id, with no meaningful input$ value): update_block_progress() sets any field (omitted args preserve the client value, NULL clears text fields) and inc_block_progress() applies a signed, saturating increment. Supports a distinct label/message/detail text model, show_value percent, a min/max scale, an indeterminate sweep, and default/success/warning/info/destructive variants. The runtime owns merged-state validity (clamps into the merged [min, max] and repairs an inverted range), the determinate fill animation and the indeterminate sweep both honor prefers-reduced-motion, and the bar is token-driven for full light/dark and block_theme() parity. The showcase and docs-site ship a full interactive playground with Content/State/Actions/Styling controls and an API table (issue #62). - block_select() gained multiple = TRUE, matching Shiny's selectInput(multiple = TRUE) value semantics (input$ is a length-1 string in single mode, a character vector in multiple mode). The trigger shows selected items as wrapping removable chips and the popup is a checkable, multi-selectable listbox that stays open on toggle, with an optional max_items cap and character(0) defaults. The max_items cap is enforced everywhere: an over-cap initial selected is an error and any later over-cap server selected is clamped. A vector selected reaching a single select collapses to its first element (never "a,b"). For assistive tech, aria-activedescendant lives on the focused combobox trigger. Chips and the multi trigger are fully token-driven for light/dark and block_theme() parity (issue #64). - block_select() dropdowns now measure and clamp to the available viewport, fixing clipped options in short embedded playgrounds and under roomier style profiles. - block_slider() gained vertical orientation, optional current-value and min/max labels, improved drag update throttling, and better shrink-wrapped layout behavior (issue #22, issue #25). - block_spinner() now supports the same semantic foreground colors as block_icon(). - block_switch() gained size = c("default", "sm", "lg") support in both block_switch() and update_block_switch(). - block_table() is new. It renders data frames with column formatting, captions, alignment, width overrides, NA handling, max-row truncation notes, reactive server updates via update_block_table(), optional row selection, and theme-safe styling intents (issue #49, issue #51, issue #53). - block_textarea() and block_input() now report their initial server value correctly even before React has finished mounting. - block_toaster(), show_toast(), dismiss_toast(), and update_block_toaster() are new. Mount a block_toaster() and fire transient, auto-dismissing toast notifications from the server with show_toast(). Toasts stack, pause on hover/focus, support Escape-to-dismiss, and reuse the block_alert() variants (default, destructive, success, warning, info) and icon system. update_block_toaster() moves the region to a new position without re-mounting it, and input$ reports the latest toast lifecycle event as a list {action, id, seq}. - block_tooltip() is new. It supports hover/focus activation, configurable side and alignment, delayed opening, Escape-to-close, and portal rendering. - block_value_box() gained variant = c("default", "accent", "destructive") for token-backed metric emphasis. Theming - block_theme() gained scoped overrides, dark-mode-specific token values, and built-in semantic color presets: neutral, stone, zinc, mauve, olive, mist, and taupe (issue #32). - block_style() is new, with block_page(style = ) support for visual style profiles that control sizing, spacing, surfaces, radius, elevation, focus rings, and disabled treatment separately from semantic colors (issue #33). - block_style_profiles() now reflects the official shadcn/ui v4 style registry: default, luma, lyra, maia, mira, nova, rhea, sera, and vega (issue #48). - block_style("luma"), block_style("lyra"), and block_style("rhea") now have broader component and shell coverage, including tables, form controls, tabs, navigation, sidebars, fields, and input groups (issue #33, issue #36). Showcase and docs - Added or refreshed interactive docs-site playgrounds for Alert, Card, Code, Empty, File Input, Input Group, Radio Group, Skeleton, Slider, Spinner, Switch, Tabs, Table, and Value Box pages (issue #21). - Component specs were refreshed across the runtime components to describe the shipped R APIs, update contracts, accessibility behavior, and known divergences from shadcn/ui. - The old dev-only Quarto gallery and copied vignette articles were removed; the custom docs site is now the maintained component gallery. Developer infrastructure - Runtime input updates now use a shared sendInputMessage() path, stricter component-name validation, synchronous DOM value writes, and less duplicated Shiny binding boilerplate (issue #23, issue #24, issue #26, issue #27, issue #28, issue #30, issue #41). - The theme and style-profile test harnesses now check token usage, light/dark theme response, palette coverage, style-profile parity, and style-profile leanness so future components are less likely to drift (issue #32, issue #33, issue #34). - Runtime CSS/JS, showcase smoke tests, parity tests, spell checks, and package checks were tightened so generated assets and browser behavior are easier to verify before release. - update_block_*() helpers now deliver server-driven updates correctly inside a moduleServer(). Previously the runtime mount id (which already carries the module namespace) was re-namespaced by the module session, double-prefixing the target so updates were silently dropped; updates now route through the root session. A new module-routing regression test and a runtime-shiny smoke assertion guard it (issue #63). 0.0.0.9000 - Architecture pivot: ADR 0017 adopts the full runtime shadcn port. Future component work moves to an R-facing adapter over package-local shadcn/Radix runtime assets, with scoped CSS, Shiny input/update semantics, per-component showcase pages, cleanup gates, and bundle-size reporting. The earlier native-CSS and wrap-by-default input decisions remain historical migration scaffolding. - Added the first runtime foundation slice: package-local runtime CSS/JS assets, internal R payload and update-message helpers, scoped mount markup, runtime build targets, a browser smoke-test scaffold, and tests for dependency attachment and Shiny updater semantics. - Added Phase 1 runtime asset guardrails: runtime CSS isolation tests block unscoped selectors and host-framework selectors, and tools/budget.R now reports runtime JS/CSS raw and gzip sizes separately from legacy compatibility assets. - Added Phase 1 runtime lifecycle guardrails: block_page() now includes a package-owned portal root, runtime mounts without Shiny input ids receive unique ids, and static runtime JS tests cover the Shiny bridge, dynamic UI hooks, child binding hooks, and portal setup. - Added a Shiny-backed runtime browser smoke fixture that verifies runtime input initialization, server updates, disabled state, dynamic renderUI() removal/reinsertion, and Shiny children inside runtime mounts. - Expanded the Shiny-backed runtime browser smoke fixture to cover explicit insertUI() / removeUI() and id reuse. - Expanded the runtime input-update browser fixture to cover clear/reset updates, enable-after-disable, stale update rejection, and Shiny module namespacing. - Added browser-backed runtime collision coverage for host Bootstrap-style selectors, Selectize-style selectors, bslib card markup, DT tables, plotly-style htmlwidget hosts, htmlwidget children inside runtime mounts, and pre-existing portal-root content. - Switched the runtime JavaScript foundation from concatenated vanilla browser scripts to a Vite-built React/ReactDOM bundle, with React-owned mount slots kept separate from Shiny child slots so Shiny outputs and htmlwidgets continue to bind inside runtime components. - Added scoped runtime token defaults on [data-shinyblocks-root] and [data-shinyblocks-portal-root], plus static checks that the runtime asset does not write theme tokens to :root. - Added hard Phase 1 runtime asset budgets for the Vite-built runtime bundle and scoped runtime CSS, recorded in ADR 0017 and enforced by tools/budget.R. - block_badge() and block_button() now render through the package-local React runtime while preserving their R-facing variant, size, icon, custom class, disabled, and passthrough-attribute contracts. Native badge CSS was removed from the legacy stylesheet; native button CSS remains temporarily for block_dark_mode_toggle(). - block_select() now renders a package-local shadcn-style overlay backed by a hidden native for form-submission, a dedicated shinyblocks.radio-group input binding, full arrow-key navigation (Up/Down/Left/Right), orientation arg ("vertical" / "horizontal"), invalid flag, and a server updater covering selected/choices/disabled/invalid/orientation/style/class with optional notify semantics. Scoped .sb-radio-group-control styles under [data-shinyblocks-root] follow the shadcn radio contract. - Phase 5.8 text input: introduced block_input() and update_block_input(). Single-line text input rendered through the package runtime (component = "input") with a hidden native , a dedicated shinyblocks.input binding (debounced 250 ms), type argument supporting text / password / email / url / tel / search / number, invalid flag, and full server updater for value/placeholder/type/disabled/invalid/style/class with optional notify semantics. Scoped .sb-input-control styles under [data-shinyblocks-root] mirror the shadcn input contract. Showcase ships a full Content/State/Actions/Styling playground. - Phase 5.7 textarea runtime migration: block_textarea() now renders through the package runtime (component = "textarea") with a hidden native