Changelog

Track the evolution of RapidRails UI with detailed release notes for every version.

v0.58.2

Latest

Released on August 13, 2026

Carousel and Pagination fixes

Two bugs reported by a client against a live StockLive site.

Fixed

  • Carousel mounted an empty nav row under the viewport. The outside-nav wrapper (carousel/component.html.erb) opened whenever show_arrows && !arrows_inside? OR show_indicators was true, but the two things inside it were gated tighter — arrows only when there was more than one slide and arrows weren't inside, indicators only when there was more than one slide and indicator_position == :outside. Any variant with overlay/hero indicators (show_arrows: false, indicator_position not :outside) opened the wrapper and rendered nothing inside it, leaving an empty flex ... gap-4 mt-4 band under the slide — and, since the root grew taller by that empty row, overlay indicators positioned with bottom-4 could land in the empty strip instead of over the slide. Single-slide carousels hit the same empty-wrapper bug for a second reason: the wrapper had no slides.size > 1 guard even though both children did. The wrapper condition now mirrors the OR of its children's conditions exactly.
  • Pagination's mobile behaviour contradicted its own USAGE.md. The docs promise the page-number series hides below sm, replaced by "Page X of Y"; only inactive page links actually carried the hide-on-mobile class — the current page and both ellipses stayed visible, so mobile users saw the current page number, both ellipses, AND the "Page X of Y" label at once, wider than a phone-width card. button_classes and ellipsis_classes (pagination/component.rb) now bake the mobile-hide into every page-series item — inactive link, current page, and ellipsis alike — composed through tw_merge rather than string-concatenated, since hidden and the item's own inline-flex both target display and raced on source order otherwise (the same defect class fixed previously in Tabs and Card). NAV_CLASSES gets min-w-0 max-w-full and the wrapper gets w-full min-w-0 (pagination/styles.rb) so justify-* aligns inside the container instead of past it once the row can actually shrink. nav_classes was subject to the same source-order race on its gap-* utility, so every size other than :base emitted two competing gap-* classes; it's now composed through tw_merge too, so the size-specific gap wins deterministically.

v0.58.1

Released on August 12, 2026

Card list item gets icon: false

0.58.0's key/value list row still forced every row through an icon tile — a fee-table row with no icon at all (1 to 250 ──────── $7.80) had no way to drop it, forcing a consumer to hand-write the whole <ul> just to omit one <span>.

Fixed

  • Card's ListItem gets icon: false. Renders the row with no icon tile <span> in the DOM at all — not an empty one, not a transparent one. Any truthy icon name (including the "check" default when icon: is omitted) renders byte-identical to 0.58.0. The <li>'s own flex items-center gap-2.5 wrapper is unchanged, so the remaining span(s) still align correctly with one fewer flex child.
  • Behaviour change: icon: nil no longer renders a "?" placeholder. Pre-0.58.1, an explicit icon: nil fell through to the icon component with an empty icon name, which caught its "unknown icon" rescue and rendered a circled "?" placeholder glyph — this was a bug, not a documented affordance. icon: nil now behaves identically to icon: false (no tile), since a nil icon is unambiguously "no icon." Any consumer passing icon: nil and relying on the placeholder circle will see the tile disappear instead.

v0.58.0

Released on August 12, 2026

Card key/value list rows, Text shade:

A consumer page hand-wrote a <ul> with three <span>s per row, four times, to get a label hard left, a value hard right, and a rule between them (pricing tiers). Separately, that page's body copy needed a lighter shade of navy than its own headings, and Text only ever emitted one fixed shade per colour, so the caller carried a hand-written class string on every text call.

Added

  • Card's ListItem gets trailing:. Content pushed to the row's right edge, a plain string or a block for markup (the block wins if both are given, same contract as the Table row and Kanban card slots). with_list threads a view context down so with_item can capture the block the same way those two already do.
  • Card's list gets a leader: flag. with_list(leader: true) draws a decorative hairline rule filling the gap between an item's text and its trailing: content on every row in that list. Off by default. Per item, the rule only draws when that item also has trailing: set — leader: true with no trailing: skips the rule for that row rather than raising, since a rule to nowhere is meaningless. Omitting both trailing: and leader: reproduces pre-0.58.0 <li> markup byte-identical.
  • Text gets shade:. Plain/default-variant text (no variant:) always resolved to a fixed light/dark shade pair. shade: picks the light-mode step instead, one of the standard Tailwind steps (50 through 950). nil (the default) keeps the existing shade unchanged; an out-of-range value logs a warning and falls back to nil rather than raising, the same warn-and-fall-back contract Tabs' active_shade: already established. shade: only reaches the plain-text path — variant: :solid/:outline/:ghost/:soft/:link resolve their colour through a fixed shade table and are unaffected. Dark mode stays pinned at the colour's existing dark step regardless of shade: — a caller wanting a specific dark shade must pass it via class: instead.

v0.57.0

Released on August 11, 2026

Card content and divider escape hatches

Driven by a consumer design measured at 1:1 against its source artboard. For the same 395px card, the design called for 20px content padding against the component's 24px, and a 1px #e7f3fb divider against zinc-200. Neither was reachable from the call site.

Added

  • content_class: on Card. Merged onto the content wrapper via tailwind_merge. Reaches the padding (the tier scale is none/sm 16px/md 24px/lg 32px, so any value between steps was previously unreachable) and the wrapper's fixed gap-4, which is what actually controls the space either side of the divider. nil by default; output is byte-identical when omitted.
  • divider_class: on Card. Merged onto the divider <hr>. DIVIDER_CLASSES was a hardcoded literal with no extra:, so the rule's colour and weight could not be changed at all. nil by default.

Fixed

  • Styles.content_wrapper_classes concatenated instead of merging. It already accepted an extra:, but joined with a space, so passing p-5 emitted "... gap-4 p-6 p-5" and left the winner to Tailwind's compiled source order rather than the call site. Now composes through tailwind_merge, so the losing token is dropped from the attribute. Same defect class fixed in Tabs (0.56.3) and Card::Footer (0.56.5); this was the last instance in Card.

v0.56.6

Released on August 11, 2026

Fixed

  • Card's wrapper_class: footer example taught a layout bug. The 0.56.5 example paired bg-transparent border-t-0 with px-0. The footer's horizontal padding is the card's gutter, so px-0 pulls the footer content out to the card's edge and out of alignment with the body copy above it — reproduced in a consumer before it was caught. The example now resets only pt/pb, and documents both the gutter caveat and the fact that dark mode needs its own dark:bg-transparent (the dark background is a separate class that bg-transparent does not conflict with, so tailwind_merge correctly leaves it standing). Because llms-full.txt embeds the component's USAGE verbatim, the bad example was also being served to AI consumers — hence a release rather than waiting. Documentation only; no component behaviour changed.

v0.56.5

Released on August 11, 2026

Card footer gets class:/wrapper_class: overrides

Card::Footer gets the same class:/wrapper_class: override pair Header got in 0.56.1 — the footer bar's muted background/top border previously had no override path at all.

Added

  • rui_card's Footer slot gets class:/wrapper_class:. The footer bar (muted bg-zinc-50 fill, border-t) had no override mechanism at all — Footer#initialize took **options documented as "reserved for future passthrough" and never rendered them. wrapper_class: (nil default) merges onto the footer's own wrapper element via tailwind_merge; class: merges onto the inner link/span, beating footer_link_classes' own color. Same convention Header established in 0.56.1: class: targets the element you actually want to repaint (the link, not its wrapper), wrapper_class: is the separate knob for the bar itself, and both merge via tw_merge so a conflicting utility (bg-transparent vs. bg-zinc-50, border-t-0 vs. border-t) replaces the losing token outright instead of leaving both classes to race on Tailwind's compiled source order. Footer#initialize now declares both as explicit named parameters instead of swallowing everything into an unused **options hash, so an unrecognized kwarg now raises rather than silently vanishing. Omitting both keeps the footer byte-identical to pre-0.56.5.

v0.56.4

Released on August 10, 2026

Tabs gets inactive_bg_color:/inactive_bg_shade:

Small follow-up to 0.56.2: the mirror-image feature for the INACTIVE trigger.

Added

  • inactive_bg_color:/inactive_bg_shade: on Tabs. :underline's and :boxed's inactive triggers render no background at all (text + a hover: highlight only). inactive_bg_color: (nil default) gives every INACTIVE trigger a light color fill instead, at inactive_bg_shade: (default 100, one of 50/100/200/300/400). Follows the exact active_bg_color: precedent from 0.56.2: same nil-means-unchanged contract, same warn-and-fall-back validation. Only :underline/:boxed read it — :spaced's inactive triggers already render their own soft tinted background, and :pills' transparent-until-hover look is that variant's established "ghost" convention rather than an accidental omission, so this release keeps the same two-variant boundary active_bg_color: already drew. Composes freely with active_bg_color: on the same tabs instance — each reads a fully independent, individually tw_merged class string, so neither can bleed into or get eaten by the other when a tab's selection toggles.

v0.56.3

Released on August 10, 2026

Tabs active trigger border/focus-ring merge fix

Follow-up to 0.56.2, found while auditing that exact release: one more tabs rendering bug.

Fixed

  • Tabs's active trigger could render with the wrong border/focus-ring color, or none at all. The always-on base classes and the active-state override classes were built as separate strings and joined with a plain concatenation — when both set the same CSS property (e.g. :underline/:boxed's base border-transparent alongside the active border-{color}-600), both tokens landed in the rendered class="" attribute at once, and which one actually painted came down to Tailwind's compiled source order rather than markup position. Not limited to border color — :pills/:spaced's active focus-visible:ring-{color} collided with the always-on focus-visible:ring-{color}-700 every trigger gets, present in the default case too. trigger_classes now runs its fully composed class array through tw_merge (the same mechanism Card's root/list/header classes already use) so the last class for a given CSS property wins deterministically and the losing token is dropped from the output entirely.

v0.56.2

Released on August 10, 2026

Tabs border-solid fix + active_bg_color:/active_bg_shade:

Targeted release: one border-rendering bug fix, and one feature request that came out of diagnosing it.

Fixed

  • Tabs's :underline active tab border never rendered in a host with corePlugins.preflight: false. border-b-2 only sets border-bottom-width — the border-style that actually makes a border paint comes from preflight's global reset, which a host running a legacy CSS framework alongside Tailwind commonly disables. Without it the active :underline tab's bottom border never appeared, regardless of color. Audited the whole stylesheet for every other width-only border emission and paired 4 more with an explicit border-solid: :boxed's trigger border, the boxed tablist's bottom border, and both dividers.

Added

  • active_bg_color:/active_bg_shade: on Tabs. :underline's active trigger renders no background at all (border + text only); :boxed hardcodes a plain white fill. Neither had a way to give the active tab a color highlight. active_bg_color: (nil default) gives the ACTIVE trigger a light color fill instead, at active_bg_shade: (default 100, one of 50/100/200/300/400). Follows the exact active_shade:/active_icon_color: precedent from 0.55.0: same nil-means-unchanged contract, same warn-and-fall-back validation. Only :underline/:boxed read it — :pills/:spaced already render their own solid active background and are byte-identical either way. Dark mode is intentionally untouched, same precedent as active_shade:.

v0.56.1

Released on August 9, 2026

Card Header/ListItem class: reachability fix, plus new color knobs

Follow-up to 0.56.0's component-flexibility release — 0.56.0 added class: passthrough to rui_card's Header/ListItem slots, but the merge only ever reached each slot's wrapper element, never the inner title/text nodes that actually carry the hardcoded colors.

Fixed

  • rui_card's Header/ListItem class: was unreachable for color overrides. class: merged onto the header's wrapper <div> and the list item's <li> — never the title <h*> or the row's text <span>, so a class: "text-red-500" override could never win a tailwind_merge conflict against a color it never touched. class: now merges onto the title/text element itself; the old wrapper-targeting behavior is preserved under a new wrapper_class: param on both slots.

Added

  • rui_card's Header accepts color:. The title was hardcoded to zinc with no color knob. with_header(title:, color: :primary) now resolves through ColorBuilderHelper, independent of the card's own color:. Default nil reproduces the old zinc output exactly.
  • rui_card's list items accept text_color:. The row text was hardcoded to zinc. with_item(text:, text_color: :success) now resolves through ColorBuilderHelper, independent of color: (the tile tint). Default nil reproduces the old zinc output exactly.
  • rui_card's list item icon tile supports two-tone via icon_bg_color:/icon_color:. The tile derived both its background and its glyph from one color: — one hue could never express, e.g., an amber tile with an orange glyph. Both default to nil (falling back to color:), reproducing the old single-color tile exactly.

v0.56.0

Released on August 9, 2026

Component-flexibility release — Text, Card, Tabs

Every gap below was hit building a real client page and worked around by bypassing the component and hand-rolling markup — that defeats the point of a component library. Every default output is byte-identical to 0.55.1 unless a new option is explicitly passed.

Fixed

  • Text ignored size: when as: was a heading. For :h1:h6, the heading's fixed scale unconditionally won — rui_text(as: :h3, size: :lg) silently rendered at h3's default size instead of text-lg. An explicit size: now wins over the heading's default scale.

Added

  • Card's Header/Body/ListItem/List slots now honor class:. All previously stored the option but never read it at render — now merged onto the slot's own element via tailwind_merge.
  • Card's Header accepts size:, and Body accepts color:. The title was locked to text-lg regardless of heading tag; with_header(title:, size: :xl3) now controls it directly. The description was hardcoded zinc; with_body(text:, color: :success) now resolves through ColorBuilderHelper. Both default to today's exact output.
  • Card's list items accept size:/icon_shape:/icon_size:, and the list gets a variant:/layout: pair. The icon tile was locked to a rounded-full size-5 circle; icon_shape: (:circle/:rounded/:square) and icon_size: (:sm/:base/:lg) now control it. with_list(variant: :pill) renders a bordered pill-row pattern that had been hand-rolled three separate times before this release; layout: :inline wraps rows in a flex-wrap row instead of a column. All defaults reproduce pre-0.56.0 output exactly.
  • Tabs gets an :xl trigger size tier. TRIGGER_SIZE_CLASSES topped out at :lg (16px labels); size: :xl (18px labels) is now available, with icon and badge sizes inside the trigger scaling proportionately.

v0.55.1

Released on August 9, 2026

Correctness release — divider, safelist, and @source path fixes

Two independent bugs, same consumer-visible symptom: documented component options silently produce no CSS in some build setups, so the element falls back to browser defaults. Both were proven live on rapidrails.cc itself.

Fixed

  • Card's divider rendered as an invisible 0px line in hosts without Tailwind preflight. A bare <hr> inherits the user-agent default auto cross-axis margins, which win over stretch inside the card's flex column and collapse it to zero width. It now carries an explicit w-full.
  • Helper-emitted Tailwind classes were missing from the generated safelist. rui_text(size: :xl8) renders text-8xl, but that class appeared nowhere in the manifest — the docs homepage hero rendered at 16px instead of 8xl because of exactly this. 177 of 222 audited class tokens across five helper files were missing; all were added to the schema and the manifest/safelist regenerated (3,772 → 3,896 classes). Added a generic regression test that walks every class-emitting helper and fails if any emitted class is missing from the manifest.
  • Tailwind @source paths never actually got rewritten on boot. The installed initializer is supposed to rewrite stale @source paths in the host's application.css to the currently installed gem location on every boot, but its regex required a literal / immediately after rapid_rails_ui — a real versioned gem path (rapid_rails_ui-0.52.0/...) or hyphenated dev checkout (rapid-rails-ui/...) never matched, so the rewrite silently went stale the moment the gem version changed. Extracted into a shared rewriter with a directory-segment pattern that accepts canonical, hyphenated, and versioned gem-directory names, and confirmed idempotent.

v0.55.0

Released on August 9, 2026

Tabs active-tab styling escape hatches

Two rui_tabs enhancements for StockLive's commercial page design, both confirmed gem gaps.

Added

  • active_shade: on Tabs. Override the :pills/:spaced active trigger's solid bg/hover/border/focus-ring shade — one of 500/600/700/800/900/950 (an invalid value warns in dev/test and falls back to 900). Previously every color's active shade was fixed by category (900 for :primary-style colors, 700 for standard colors, 400 for the bright/:warning family) with no escape hatch. Left at the nil default, output is byte-identical to pre-0.55.0. Hover shade derives one step darker automatically; the focus ring stays capped at 900 regardless of the override. Dark mode is unaffected — it keeps each color's own category dark shades.
  • active_icon_color:/active_icon_shade: on Tabs. Tint the active tab's icon a different color than its label text (e.g. a light accent on a dark :spaced solid background) — inactive tabs and the label itself are unaffected either way. active_icon_color: is nil by default (icon matches label, unchanged behavior); active_icon_shade: is one of 100/200/300/400/500 (default 300), ignored unless active_icon_color: is set. No JS changes — implemented as an arbitrary-variant utility ([&_svg]:text-<color>-<shade>) appended only to the active trigger's class string.

Fixed

  • Tabs::Tab#options silently swallowed unknown keywords. t.with_tab(id: "x", label: "X", colr: :primary) rendered with no warning. Now runs through the same typo-detection pattern every other component's **options already gets (Card's pattern), and warns in dev/test. Arbitrary Tab options still aren't merged into the rendered <button>'s HTML attributes — this fix only adds the missing warning.

v0.54.2

Released on August 7, 2026

Fixed

  • JS-generated markup now derives Stimulus identifiers from the controller's own registered identifier. 22 hardcoded literals across six controllers made JavaScript-built elements unresponsive in apps configured with stimulus_namespace: the Date picker's entire calendar interior, time picker options, Upload's remove-file button, recent-searches items, and the menu submenu lookup. Steps indicator keyboard navigation was non-functional in every configuration. A generalized regression test now guards this class in both directions. jsbundling consumers: run bin/rails rui:update.

v0.54.1

Security

Released on August 7, 2026

Security

  • Text gradient XSS fixed. With a String or Hash gradient:, the whole text was marked html_safe after the gradient span was inserted, so HTML in the text rendered unescaped. Output is now composed safely and all text content is escaped. The Symbol form (gradient: :ocean) was never affected. Upgrade recommended if user-influenced text flows through rui_text with String/Hash gradients.

Fixed

  • Date :picker variant (the default) now carries aria-invalid and aria-describedby error wiring, matching the native variant. Docs corrected: :picker is the default variant.
  • Installer import transform no longer rewrites doc comments.

v0.54.0

Released on August 7, 2026

Installer overhaul, llms automation, tabs polish

Behavior change for re-installers: the default install generator no longer writes the theme toggle into your application layout. It is now an explicit opt-in: rails g rapid_rails_ui:theme_toggle, which also registers the Stimulus controller.

Installer

  • importmap hosts now receive the full set of Stimulus controller copies, with relative imports transformed to pinned specifiers (previously one unresolved relative import could break every controller on the page).
  • Install output lists all AI resources: the llms.txt variants, Accept-header content negotiation, and the version-true per-component USAGE.md files inside the installed gem.

llms automation

  • rake rui:generate_llms builds llms.txt and llms-full.txt from the component schemas and USAGE files; CI fails any pull request shipping stale llms files. The files on this site are now generated, not hand-maintained.

Tabs

  • Tabs panels get default breathing room below the tablist, plus a new panel_gap: option (:none/:sm/:md/:lg). Vertical orientation gains a proper gutter between the tab column and panels.

Also in this release

  • gotchas.yml entries for the six newest components; Combobox validates shape:; focus-ring manifest gap resolved; README refreshed to all 37 components.

v0.53.0

Released on August 6, 2026

Form components hardening release

Fixes all 22 confirmed findings from a three-group form-component audit. Minor bump, not patch — badge rendering, radio Hash-enum values, and scalar radio-collection titleizing all start producing different (correct) output. Every change here aligns behavior to what each component's docs already claimed.

ARIA error-id uniformity

  • Select, Checkbox, Radio Button, Date, and Combobox — the rendered error <p> now gets a stable id, and aria-describedby references it exactly when the error renders (error id wins over help text id when both are present), replicating Dropdown's existing reference implementation.
  • Inputaria-describedby now includes validation_message_id when a server-side error is present even with validation: false set (previously gated only on the validation: flag, so a genuine model error on a validation: false field rendered but was never associated for assistive tech).

Combobox

  • Badges render by default. show_badges: true (the documented multi-select default) now actually renders individual removable badge chips in the trigger, instead of always falling back to "N items selected" summary text regardless of the setting. Badges sync on every selection change, including native form reset.
  • clear_on_select: true works. Previously accepted, stored, and documented, but never read by the controller — now actually clears the search input after a selection.
  • Native form reset resync. The controller now listens for the host <form>'s native reset event and rebuilds selection state (hidden inputs, aria-selected, checkboxes/radios, badges, trigger text) from the selection captured at connect() — a native reset alone can't handle combobox's dynamically-managed hidden inputs correctly.
  • Server-disabled options stay disabled. Options rendered with disabled: true are now marked and permanently excluded from the max-selections re-enable pass, which previously re-enabled ANY non-selected option — including permanently disabled ones — as soon as the selection count dropped back under max_selections.
  • Grouped-collection value uniqueness. Selection lookup matches by value alone, not group membership — values must be unique across all groups, not just within one. See the Grouped Options caveat.

Dropdown & Combobox: Escape focus restore

  • Escape now returns focus to the trigger button after closing the menu (WAI-ARIA APG requirement), via a new shared menu_controller.js#closeOnEscape action.
  • Dropdowncollection: plus block content (with_item slots) together no longer silently drop the block's items; collection processing moved to before_render so it no longer freezes the slot content before the caller's block is attached.

Radio Button

  • Hash-collection enums normalized correctly. collection: Post.statuses (the documented Rails-enum pattern) now renders the enum's string key as the option value and a titleized label, instead of the raw integer with lowercase text — pre-selection now matches what a Rails enum reader actually returns. If any code read the raw submitted value expecting an integer, update it to expect the string key. See the Collection Formats section.
  • Scalar collections are titleized. collection: ["technology", "design"] now titleizes option labels, matching Checkbox's equivalent behavior.
  • Checkbox & Radio Button — a formless form_with(url: ...) (no model:/as:) no longer renders a malformed, Rack-unparseable name="[method]".

Date

  • Real model-error rendering. A form/object error on a date field now renders the actual error text with the correct red error styling, instead of a visually-blank placeholder.
  • Type-cast fallback. Date attributes that fail ActiveRecord/ActiveModel type-casting on a 422 re-render now fall back to the standard _before_type_cast reader instead of blanking the field.
  • Bounds fail closed. A malformed min:/max: constraint now blocks every date instead of silently letting the constraint through unenforced.

Input

  • autoformat: false opts a type: :tel/:url input out of the auto-formatting listeners entirely — the NANP phone formatter corrupts international phone numbers.
  • value: false is now honored as an explicit value instead of being silently overridden by the object-derived value (same fix as Select's selected:, below).
  • Standalone id/aria-describedby targets now agree with each other instead of each minting a fresh random suffix per render.
  • shape: nil no longer crashes render (validated/defaulted the same way color already was — also fixed the same way for Select, Textarea, and Upload).
  • color: default is documented correctly as :default, not :primary (matches actual behavior — no behavior change, doc correction only).

Select

  • Standalone rui_select(method: :status, ...) no longer falls into **options and renders a bogus method="..." HTML attribute — method: is now an accepted keyword like every sibling component.
  • selected: false (or []) and value: false are now honored as explicit values instead of being silently overridden by the object-derived value.
  • shape: nil no longer crashes render, same fix as Input.

Editable

  • The documented turbo_stream.replace error pattern no longer detaches the Stimulus controller mid-error-handling. The controller element now carries id="<id>_root"; a new inner class="contents" wrapper carries the original id that turbo_stream.replace targets, so replacing it swaps content without ever touching the controller element itself.

Button

  • formmethod: :put/:patch/:delete now always uses the Rails _method hidden-field strategy, regardless of form_builder_mode? or whether name:/value: were also set — previously the hack only applied when both were nil, so any button also setting name:/value: emitted an invalid formmethod="delete" attribute that browsers silently ignore (submitting the wrong HTTP verb with no visible error). A caller-supplied name:/value: is now overridden with a Rails.logger.warn instead.

Also in this release

  • FormBuilder index: support. All f.rui_* methods now accept a manual nested-attributes index: option (e.g. inside a hand-rolled fields_for loop), threading it into name/id derivation instead of leaking out as a bogus index="3" HTML attribute.
  • FormBuilder string method names. f.rui_input("email") and friends now normalize a String method name to a Symbol instead of raising ArgumentError, matching vanilla Rails' own form helpers.
  • Uploadmax_files is now enforced cumulatively across selections/drops, not just against the latest event in isolation; color validation now goes through the shared color-validation helper like every sibling component.
  • Textarea — the validation controller now captures server-rendered message classes on connect (matching Input), instead of overwriting them with a hardcoded class list that caused a layout shift after the first interaction.

v0.52.1

Released on August 6, 2026

Tabs identifier hotfix

Fixes rui_tabs active-tab highlighting, deep links, and the change event's detail being dead in apps that don't set config.stimulus_namespace.

Fixed

  • The tabs Stimulus controller hardcoded data-rui--tabs-* attribute names, while the Ruby side correctly emitted them relative to the configured namespace (bare data-tabs-* with no namespace set). Panels and ARIA still toggled correctly (attribute-based), so the breakage looked like "clicks work but the tab never highlights." All attribute reads now derive from this.identifier, matching whatever namespace is configured.
jsbundling consumers: re-run bin/rails g rapid_rails_ui:install (or bin/rails rui:update) to pick up the new controller copy — a plain bundle update only touches the Ruby gem.

v0.52.0

Released on August 6, 2026

New rui_tabs component

WAI-ARIA APG-compliant tabs. Selecting a tab shows its panel — four visual variants, horizontal and vertical orientation, icons and badges in triggers, optional URL deep-linking, and a single Stimulus controller. Requested by Hugh Gordon (StockLive) for the commercial landing page. Pro tier — see the Tabs docs.

Added

  • rui_tabs (Pro). Slot-based: t.with_tab(id:, label:, icon:, icon_position:, active:, disabled:, color:, badge:, badge_color:, badges:) { panel content }. Panel content is arbitrary HTML/ERB/components — nested rui_tabs inside a panel works out of the box.
  • 4 visual variants: :underline (default, connected row with a 2px active border), :pills (rounded-full, solid active), :boxed (enclosed box merging into the panel), :spaced (standalone rounded blocks, solid dark active / soft tinted inactive, divider on by default).
  • orientation: :horizontal | :vertical, with tablist_position: :start | :end for vertical (flipped via flex-row-reverse, not DOM reordering, so keyboard focus order always matches DOM order).
  • mobile_layout: :grid — 2-column grid below md, row from md up (horizontal only). full_width stretches tabs equally via flex-1.
  • Multi-badge support per tab: a badges: array ({text:, color:, position:}) plus a badge:/badge_color: shortcut for the common single-counter case.
  • deep_link: true reads location.hash on connect and writes it back (via history.replaceState, no extra history entries) on every switch.
  • animation: :fade | :none — fade-in panel reveal, fully suppressed under prefers-reduced-motion through a centralized _motionSuppressed() gate in the new rui--tabs controller.
  • Full WAI-ARIA APG contract: role="tablist"/"tab"/"tabpanel", aria-selected set explicitly on every tab, aria-controls/aria-labelledby, roving tabindex, aria-disabled + disabled tabs skipped entirely during arrow traversal, inactive panels hidden via the hidden attribute so no-JS degrades to the first panel visible.
  • panels: false renders the tablist only, for consumers wiring their own external panels against the rendered aria-controls ids.

Also in this release

  • Gemspec post_install_message. Reminds jsbundling-rails consumers to run bin/rails g rapid_rails_ui:install (or the new rui:update) after bundle update rapid_rails_ui, since a plain bundle update only touches the Ruby gem, not the host's local Stimulus controller copies.
  • rui:update rake task. Runs the install generator's controller-sync step, regenerates the JS controller copies and the gem-managed index.js registration block, then yarn build if package.json defines a build script. Touches only the gem-owned namespace; no-ops gracefully outside a Rails app or on importmap hosts (which resolve controllers straight from the gem).

v0.51.2

Released on August 6, 2026

Fixed

  • RRUI-001: Input/Textarea ignored Rails model errors. f.rui_input/f.rui_textarea had no error path at all — no red border, no error message — while Select/Checkbox/RadioButton/Date/Combobox/Upload/Dropdown/Editable already handled this. Both now detect server-side model errors (form-builder only — standalone rui_input(object:, method:) still doesn't check model errors), render the error inline with red border/focus styling, and set aria-invalid="true". The error text renders inside the same validation-message element the real-time validation Stimulus controllers already target, so there's no double-rendering on a 422 re-render — the server error wins on first paint, then client-side validation takes back over once the user edits the field.
  • RRUI-002: no way to exclude RRUI's native form controls from legacy select/form enhancers. Materialize, Select2, Chosen, and similar libraries typically re-scan the whole document for select/input elements on every Turbo navigation, which can wrap RRUI's own markup and break its styling and wiring. Select, Input, Textarea, Checkbox, RadioButton, Upload, and Date now render a data-rui="<component>" marker on their control element(s), so a host app can exclude them with document.querySelectorAll("select:not([data-rui])") (or the input/textarea equivalent). Purely additive. See the Select docs' Legacy Select Enhancers section.
  • RRUI-003: f.rui_button (and 9 other f.rui_* form builder methods) silently dropped their block. f.rui_button { |b| b.with_icon(:check) } rendered a button with no icon — the block was never forwarded to render_in, unlike every standalone rui_* helper. All 10 affected form builder methods (rui_button, rui_checkbox, rui_radio_button, rui_date, rui_date_range, rui_textarea, rui_upload, rui_dropdown, rui_select, rui_combobox) now accept and forward their block.

v0.51.1

Released on August 4, 2026

Fixed

  • Text semantic colors - rui_text(color: :muted) emitted nonexistent text-muted-* classes, leaving muted text (including rui_stat labels) unstyled. :muted now resolves through the semantic color system and is host-configurable via config.colors[:muted] (defaults to gray). The same resolution hole was fixed for :default (zinc) and :error (red).

v0.51.0

Released on August 2, 2026

New rui_card component

Flexible, plug-and-play card system for service and feature cards. Six independent slots — media, icon, header, body, list, and footer — all optional, rendered in a fixed order. Includes a shortcut API (title:/description:/icon:) for the common icon + heading + description case, whole-card link mode via href:, four variants, semantic colors, and no JS controller. Starter tier — see the Card docs.

Added

  • rui_card (Starter). Icon tile with semantic tinting, checklist rows, full-width footer bar, media slot flush to the top edge, and href: whole-card link mode with hover shadow lift and automatic aria-label.

v0.50.7

Released on August 2, 2026

Combobox, dropdown, and input fixes

Bug-fix release focused on invisible borders, layout shifts, and combobox keyboard/selection behavior.

Fixed

  • Dropdown outline trigger used border-1 (not a Tailwind class) — border never painted; now border.
  • Input valid state shows the green border only; the "✓ Valid …" message no longer renders, so valid input never shifts layout.
  • Input/Textarea live-validation message no longer reserves space below the field — empty:hidden, only visible when a message is shown. JS controller preserves server-rendered message classes.
  • Combobox trigger had no visible border or focus ring — rebuilt border/focus classes to match Select.
  • Combobox/Select triggers invisible under host button { border: 0 } resets — all button-element triggers now pair border-width utilities with border-solid (also applied to Dropdown, Button, Clipboard, and Date clear button).
  • tailwind_classes.yml manifest additions for border, border-solid, and focus-visible classes emitted by Combobox/Select triggers.
  • Combobox JS — keyboard nav no longer focuses filtered-out options; max-selections re-enable restores pointer-events; single-select placeholder restores after clear; clearAll() now works for single-select.

v0.50.6

Released on June 1, 2026

More components honor config.colors

Follow-up to v0.50.5. Three remaining spots resolved the configurable semantic colors from a hardcoded family (one never compiled at all) and now route through ColorBuilderHelper.

Fixed

  • Dropdown active menu item — used a literal bg-primary-50 (primary is not a Tailwind color, so an active item rendered with no highlight); now resolves the dropdown's color through config.colors.
  • Dropdown menu checkbox/radio checked state — was hardcoded blue; now follows the dropdown's color.
  • Link inactive nav state — produced an invalid text-primary-700; now resolves through config.colors (mirroring the active state).

Notes

  • No API changes. Status, neutral, and platform colors are unchanged. Upgrade with bundle update rapid_rails_ui.

v0.50.5

Released on May 31, 2026

Semantic colors honour config.colors across all components

A few components resolved the configurable semantic colors from a hardcoded Tailwind family instead of your app's config.colors, so a configured palette was ignored — for example rui_text(color: :secondary) rendered a fixed purple instead of your brand color. They now resolve through ColorBuilderHelper, exactly like button and badge already did.

Fixed

  • Text — the default text variant now resolves :primary/:secondary/:tertiary/:accent through config.colors.
  • Dropdown — the trigger focus ring follows config.colors (and :secondary no longer emits an invalid class).
  • Date — the input border and focus ring follow config.colors.
  • Avatar — ring colors follow config.colors.

Added

  • Avatar rings now accept all four configurable colors (:primary, :secondary, :tertiary, :accent), not just :primary. See the Avatar ring styles.

Changed

  • With the default palette, :primary now resolves to zinc everywhere (matching button/badge). Apps that set config.colors get their configured family; apps relying on the old hardcoded blue/neutral for :primary will see it follow the configured (or default) color.

Notes

  • No API changes. Status colors (:success, :danger, :warning, :info) remain fixed by convention.

v0.50.0

Released on May 16, 2026

New `rui_carousel` component (Pro)

First-class carousel primitive for landing-page banner rotators, hero splashes, multi-card sliders, and testimonial blocks. CSS scroll-snap track plus a single Stimulus controller; full ARIA carousel pattern; native touch swipe.

Added

  • rui_carousel (Pro). Six variants — :default, :banner, :hero, :cards, :testimonials, :fullscreen — cover the common landing-page patterns without per-page CSS overrides. See the Carousel docs.
  • Auto-rotate with configurable interval:, pause_on_hover:, and respect_reduced_motion: (default true — honours OS prefers-reduced-motion: reduce).
  • Responsive items_per_view. Pass an Integer (always N) or a { base:, sm:, md:, lg: } hash so the same component renders 1-up on phones, 3-up on tablets, 4-up on desktops without breakpoint forks in the view.
  • Toggleable controls. show_arrows: and show_indicators: independently on/off. loop: false stops at the ends with aria-disabled="true" on the boundary arrow.
  • Full ARIA carousel pattern. role="region", aria-roledescription="carousel", indicator aria-current="true" tracking, live region announcing "Slide N of M" on every change. Keyboard nav (←/→, Home, End) when focus is inside the carousel.
  • Native touch swipe. The track is plain CSS scroll-snap, so phone/tablet swipe gestures work out of the box with no extra JS gesture controller.

Notes

  • Carousel is a Pro-tier component (matches dialog, popover, kanban — JS-heavy interactive). Starter licenses see the standard upgrade notice.
  • No breaking changes. Pure addition.

v0.48.0

Released on May 13, 2026

Tooltip subtitles, Kanban card headers, Combobox grouped options

Three additive features surfaced by real-world integration work. All additions are opt-in; existing call sites are unaffected.

Added

  • Tooltip subtitle: param renders a visible secondary line below the main text:. Distinct from the existing description:, which remains screen-reader-only (sr-only). The two can be combined. Useful for icon-only buttons that need a one-line explanation or shortcut hint (e.g. subtitle: "⌘S"). See the Tooltip docs.
  • Kanban col.with_card(title:, subtitle:) renders a built-in card header. Pass either or both; block content stacks below. Cards with only a header work with no block. Fully custom card layouts (badges, avatars, meta rows) are still possible by omitting both and rendering inside the block. See the Kanban Card Features section.
  • Combobox grouped collections. Pass collection: [{ group: "Label", items: [...] }, ...] to render labeled groups inside the dropdown. Items within each group accept any of the existing item shapes (string, [text, value] pair, or hash). Group headers are non-focusable, so keyboard navigation (↑/↓) lands only on real options. See the Combobox Grouped Options section.
  • Combobox per-item disabled: on hash items. { text:, value:, disabled: true } makes a single option non-selectable, keyboard-skipped, and announced with aria-disabled. Works in flat and grouped collections. Useful for out-of-stock items, plan-tier gating, or seasonal availability.

Notes

  • All four additions are non-breaking. Existing rui_tooltip, col.with_card, and rui_combobox call sites continue to work without changes.
  • tailwind_merge dependency constraint bumped to ~> 1.1 (was ~> 0.13) to follow the gem's own bump.

v0.47.0

Released on May 6, 2026

Configurable Stimulus IDs

Resolves Stimulus ID collisions when a host app already registers a controller under a name the gem also uses (e.g. "checkbox", "dialog", "menu"). The gem's components can now emit namespaced data-controller attributes that match a host's namespaced registrations.

Added

  • config.stimulus_namespace (#70). When set (e.g. "rui"), components emit data-controller="rui--checkbox", data-action="evt->rui--dialog#open", data-rui--menu-target, etc. Default nil preserves current behavior.
  • StimulusHelper mixin (lib/rapid_rails_ui/helpers/stimulus_helper.rb). Provides stimulus_id(:foo), stimulus_action(:foo, "click->#toggle"), stimulus_attr(:foo, :target) to component classes and plain data holders (Kanban::Card, Kanban::Column).
  • Install generator --stimulus-namespace=rui flag. Bakes config.stimulus_namespace = "rui" into the generated initializer AND uses the same value for application.register("rui--checkbox", ...) calls in app/javascript/controllers/index.js, so ERB output matches JS registration.

Changed

  • All ~20 components with Stimulus controllers refactored to thread emitted IDs through the helper. Components covered: alert, dialog, kanban (board/card/column), table, dropdown (menu/switch/submenu), tooltip, popover, date (date-picker/time-picker), combobox, textarea, editable, upload, clipboard, input (input-validation), live-search. Default behavior is identical to v0.46.x.

Notes

  • Foundation release. Default config.stimulus_namespace = nil means zero behavior change for existing consumers. Opt in only if you have host-controller name collisions; pass --stimulus-namespace=rui when running the install generator (or set the config and update application.register calls manually).

v0.46.0

Released on May 6, 2026

Bundler-Aware Install Generator

Two install generator flags that solve common host integration friction. Both are install-time only and additive. Existing installations are untouched on upgrade.

Added

  • Bundler-aware import paths (#68). inject_controller_registrations now detects host bundler from the Gemfile (importmap-rails vs jsbundling-rails) and emits the correct path style. Importmap hosts keep bare specifiers (from "controllers/rapid_rails_ui/foo_controller"); jsbundling hosts (esbuild/webpack/rollup) get relative paths (from "./rapid_rails_ui/foo_controller") so module resolution succeeds without manual edits. New --bundler=importmap|jsbundling flag overrides auto-detection.
  • Optional class-name namespace prefix (#69). New --namespace-imports=Rui flag (any PascalCase string accepted) prefixes the JS local variable names of the gem's controller imports, avoiding "The symbol AccordionController has already been declared" errors on hosts with same-named controllers. Stimulus IDs (data-controller="accordion") are unchanged.

v0.45.1

Released on May 6, 2026

Install Generator: Copy Non-Controller Helpers

Fixed

  • Install generator copies all shared/ helpers (#67). The previous copy_stimulus_controllers globbed shared/*_controller.js, missing shared/turbo_fetch.js (the helper imported by alert_controller, editable_controller, and kanban_controller). esbuild and webpack hosts failed to build with Could not resolve "./shared/turbo_fetch". Glob now matches shared/*.js so all helpers are copied alongside the controllers. Stimulus registration logic still only registers *_controller.js files, leaving helpers as plain importable modules.

v0.45.0

Released on May 5, 2026

Tailwind Class Manifest

Eliminates the per consumer safelist maintenance burden. The gem now ships app/components/rapid_rails_ui/_tailwind_manifest.rb containing every literal class string the components can emit. Tailwind's content scanner picks it up via the existing gem content glob with zero consumer side configuration.

Added

  • Tailwind class manifest (app/components/rapid_rails_ui/_tailwind_manifest.rb). Auto generated. Around 3,400 unique classes covering 22 colors, scoped shade groups, and variant patterns.
  • Single source of truth (lib/rapid_rails_ui/schema/tailwind_classes.yml). Hand maintained YAML defining colors, shade groups, brace patterns, and literal classes. Drives all generated outputs.
  • Brace expander (lib/rapid_rails_ui/schema/brace_expander.rb). Mirrors Tailwind v4 @source inline() ergonomics. Supports {name} list references and {a,b,c} literal CSV. Multiple braces produce a cartesian product.
  • rake rui:generate_tailwind_classes. Single command produces four outputs from the YAML: the .rb manifest, a regenerated safelist.css (v4), a v3_safelist.js CommonJS module (v3 backup), and a markdown LLM agent reference.
  • CI drift detection. bin/generate-schemas now runs the new task. .github/workflows/validate-schemas.yml enforces no drift between tailwind_classes.yml and its generated outputs.
  • LLM agent reference. Canonical machine readable list of classes the gem emits. Agents no longer need to trace ColorBuilderHelper interpolation logic to know what classes exist.

Changed

  • safelist.css is now generated from tailwind_classes.yml (was hand maintained). Same content, single source. v4 installer behavior unchanged.

Notes

  • v3 explicit safelist injection into tailwind.config.js is deferred. The _tailwind_manifest.rb is already covered by the v3 content glob added in v0.44.2, so class detection works without a separate safelist edit. The generated v3_safelist.js ships as an opt in backup.

v0.44.2

Released on May 4, 2026

Install Generator Bug Fixes

Three fixes discovered while integrating into a production Rails 7.2 app on Tailwind v3 with jsbundling/esbuild.

Fixed

  • Inject only into content: array (Tailwind v3). The previous before: /\s*\]/ anchor matched any closing bracket, so on a real world tailwind.config.js with content, theme.fontFamily, and plugins arrays the gem path landed inside fontFamily AND inside plugins (with a doubled comma producing a JS syntax error). Now scans for the content: array specifically with balanced bracket matching.
  • Skip by class name false positives. inject_controller_registrations previously skipped controllers when their class name (for example AccordionController, DialogController, MenuController, SearchController) collided with an existing import in the host app, silently dropping 8 of 28 gem controllers. Now matches on the unique import path ("controllers/rapid_rails_ui/accordion_controller") instead of the class name.
  • Portable gem path in tailwind.config.js (v3). Previously embedded a hardcoded absolute path (machine, Ruby version, and gem version specific), which broke for every other developer and after bundle update rapid_rails_ui. The v3 path now prepends a bundle show rapid_rails_ui resolver at the top of tailwind.config.js and references it via JS template literals inside content:, so the path resolves at build time. (Tailwind v4 was already portable via the runtime initializer.)

Documented

  • Tailwind v3 (jsbundling/esbuild) feature gap. Added section to INSTALLATION.md listing pieces still requiring manual setup on v3: safelist, dialog component CSS, theme toggle CSS Sass load path resolution. v4 path remains fully automated.

v0.44.1

Released on May 4, 2026

tailwind_merge Dependency Relax

Changed

  • tailwind_merge dependency. Relaxed constraint from ~> 0.13 to ~> 1.1 to allow consumers on tailwind_merge 1.x. No API changes.

v0.44.0

Released on March 1, 2026

Kanban Component

Drag and drop board for cards across columns. HTML5 native DnD plus full keyboard accessibility (Space grab, Arrow keys, Escape cancel).

Added

  • rui_kanban. Column accent colors (top border), card accent colors (left border), badge counts, max cards per column, optional auto PATCH with Turbo Stream support. 3 sizes (sm/base/lg), 2 shapes (rounded/square). ARIA live region for screen reader announcements. Pro exclusive tier.
  • Shared turbo_fetch.js utility. Extracted CSRF token and Turbo aware fetch into shared/turbo_fetch.js. Eliminates duplicate CSRF and Turbo Stream handling across alert, editable, and kanban controllers.

Changed

  • Alert controller. Refactored sendUndoRequest() to use shared turboFetch and handleTurboStream utilities.
  • Editable controller. Refactored _submitToServer() and _handleResponse() to use shared utilities. Removed _getCSRFToken().

v0.43.0

Released on February 27, 2026

Progress & Stat Components

Two new Starter tier components for data visualization: a lightweight rui_progress bar and a metric card ui_stat for dashboards and KPI displays.

Added

  • rui_progress(value) - Horizontal progress bar. 5 sizes (xs/sm/base/md/lg), 2 variants (solid/soft), optional label: and show_value:, CSS animated: transition. Full color palette. Zero JavaScript. Starter tier.
  • rui_stat(label, value) - Metric card for dashboards. Optional change badge with auto-detected direction (+/- prefix), optional progress bar, composable icon/sparkline/footer slots. Composes rui_text, rui_badge, rui_progress. Zero JavaScript. Starter tier.
  • rui_kanban - Drag-and-drop kanban board. HTML5 native drag API + Stimulus controller. Columns with with_column(id:, title:), cards with col.with_card(id:). Column accents, card accents, WIP limits (max_cards:), custom headers/footers, scrollable columns (max_height:), static mode (draggable: false). Server persistence via move_url: auto-PATCH with Turbo Stream support. Full keyboard navigation (Space/Arrows/Escape). WCAG 2.2 accessible. Pro tier.

Usage

Progress Bar
<%= rui_progress(75, label: "Open tickets", show_value: true, color: :primary) %>
<%= rui_progress(42, color: :warning, size: :sm, animated: true) %>
Stat Card
<%= rui_stat("Monthly Revenue", "$32,209",
  change: "+24% vs last month",
  change_color: :emerald,
  progress: 78
) do |s|
  s.with_icon(:trending_up)
end %>
Dashboard Grid
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  <%= rui_stat("Total Users", "12,847", change: "+12%", change_color: :emerald, progress: 72) do |s|
    s.with_icon(:users)
  end %>
  <%= rui_stat("Revenue", "$94,200", change: "+8%", change_color: :emerald) do |s|
    s.with_icon(:dollar_sign)
  end %>
  <%= rui_stat("Active Sessions", "3,421", change: "-2%", change_color: :red) do |s|
    s.with_icon(:activity)
  end %>
  <%= rui_stat("Conversion", "3.2%", change: "+0.4%", change_color: :emerald, progress: 32) do |s|
    s.with_icon(:target)
  end %>
</div>

Website

  • Landing page - Replaced badge and class-extension demos with live interactive rui_dropdown and rui_accordion components in the "Live Components" section. Showcases slot-based composition pattern (with_item, with_divider, with_header) instead of simple single-line components.

v0.42.0

Released on February 25, 2026

Minor API Improvements: New Link Helpers & Deprecations

New dedicated rui_mailto, rui_tel, and rui_sms helpers replace the deprecated communication params on rui_link. Several params renamed with deprecation warnings for clarity. Accordion gets a single_open: alias. Combobox close_on_select: now defaults to :auto.

Added

  • rui_mailto(text, address) - Dedicated email link helper. Supports subject:, body:, cc:, bcc:, reply_to:. Full rui_link styling.
  • rui_tel(text, number) - Dedicated phone link helper. Supports country_code:. Full rui_link styling.
  • rui_sms(text, number) - Dedicated SMS link helper. Supports body:, country_code:. Full rui_link styling.
  • Accordion single_open: - Friendly alias for exclusive:. Both accepted; single_open: takes priority.
  • rake rui:sync_llms - New rake task to sync LLMS documentation files to sibling apps (rapidrails + rapidfy). Auto-invoked by generate_llms_mini.

Changed

  • Dialog backdrop_close: - Renamed from dismissible: for clarity. Old param still works with deprecation warning. Stimulus value renamed to backdropCloseValue.
  • Upload max_file_size_bytes: - Renamed from max_file_size: to make unit explicit. Old param still works with deprecation warning.
  • Date input_type: - Renamed from type: to avoid shadowing Ruby's Object#type. Old param still works with deprecation warning.
  • Combobox close_on_select: :auto - Default changed from true to :auto. Closes in single-select, stays open in multi-select. Pass true/false to override.
  • Pagination conflict detection - Dev/test warning when both pagy: and manual params (current_page:, total_pages:, total_count:) are provided. pagy: always wins.

Deprecated

  • rui_link email: → use rui_mailto(text, address)
  • rui_link phone: → use rui_tel(text, number)
  • rui_link sms: → use rui_sms(text, number)
  • rui_link method: :delete (non-GET) → use data: { turbo_method: :delete } or rui_button_to
  • rui_dialog dismissible: → use backdrop_close:
  • rui_upload max_file_size: → use max_file_size_bytes:
  • rui_date type: → use input_type:

Usage

New Link Helpers
<%= rui_mailto("Contact Sales", "sales@example.com",
  subject: "Sales Inquiry",
  color: :primary) do |link|
  link.with_icon(:mail)
end %>

<%= rui_tel("Call Us", "555-1234", country_code: "1") do |link|
  link.with_icon(:phone)
end %>

<%= rui_sms("Text Support", "555-HELP", body: "Order #123 - ") %>

v0.41.0

Released on February 17, 2026

Checkbox Smart Features & Text Aliases

Major Checkbox enhancements: strikethrough for todo lists, auto-submit, input_html pass-through, smart labels, and unique IDs. Plus Text component gets leading:/tracking: shorthand aliases.

Added

  • strikethrough: - Line-through + muted color on label when checked. Pure CSS via group-has-[:checked]. Perfect for todo/task lists.
  • label_class: - Append custom CSS classes to the checkbox label element.
  • auto_submit: - Auto-submit parent form on change via Stimulus. CSP-safe, no inline JS.
  • input_html: - Pass attributes directly to the <input> element. Stimulus actions are space-concatenated, not overwritten.
  • Smart labels - In form mode, labels auto-derived from human_attribute_name. label: false suppresses label.
  • Unique IDs - Record ID included in input ID (user_42_completed) for safe use in lists.
  • Text leading: / tracking: - Shorthand aliases for line_height: and letter_spacing:. Works on headings.
  • Schema system - compositions.yml and gotchas.yml for component composability docs.

Changed

  • Checkbox controller - Added submitForm() action for auto-submit.
  • JS controller cleanup - Removed duplicated Stimulus controllers from component directories.

Strikethrough Example

Strikethrough for Todo Lists
<%= form_with model: todo do |f| %>
  <%= f.rui_checkbox(:completed,
    label: todo.title,
    auto_submit: true,
    strikethrough: true
  ) %>
<% end %>

v0.40.0

Released on February 8, 2026

Table Collection Mode

New declarative API for the Table component. Pass a collection as the first argument and define columns with table.column(:key) — no manual each loop needed. Existing slot-based API unchanged.

Added

  • Collection mode - rui_table(@users) { |t| t.column(:name) } for declarative, 2-level table API
  • Custom cell blocks - table.column(:role) { |user| rui_badge(user.role) } for custom cell content
  • row_color: - Proc or Symbol for conditional row coloring in collection mode
  • id_method: - Customize which method/key extracts row IDs (default: :id)
  • Supports all existing features: formatters, sorting, selection, pagination, responsive modes

Usage

Collection Mode
<%= rui_table(@users, striped: true, selectable: true) do |table| %>
  <% table.column(:name, sortable: true) %>
  <% table.column(:email) %>
  <% table.column(:role) { |user| rui_badge(user.role, color: :primary) } %>
  <% table.column(:balance, formatter: :currency) %>
<% end %>

v0.39.0

Released on February 8, 2026

API Audit: Typo Detection & Deprecation Warnings

Developer experience improvements across all components. Typo detection warns when **options contains misspelled keyword params. Badge and Alert emit deprecation warnings for implicit behaviors being removed in v1.0.

Added

  • Typo detection - Dev/test warning for misspelled params (e.g. colr: → "Did you mean color:?"). All 31 components.
  • Badge deprecation - status: param warns to use color: instead. Will be removed in v1.0.
  • Alert cascade deprecation - Warns when undo_url: auto-enables auto_dismiss:, and when auto_dismiss:/dismissible: auto-enables animate: and show_progress_bar:. Pass explicitly to silence.

Removed

  • llms.txt / llms-full.txt - Removed from gem. Canonical copies at rapidrails.cc/llms.txt.
  • rake generate_llms_full - Removed rake task.

v0.38.0

Released on February 8, 2026

Built-in Pagination Service

RapidRailsUI now ships with its own pagination service. No need to install Pagy, Kaminari, or any external pagination gem. rui_paginate is automatically available in all controllers.

Added

  • rui_paginate(collection, limit:, page_param:) - built-in pagination for all controllers
  • PageInfo result object - duck-types with Pagy (.page, .pages, .count, .from, .to)
  • Paginator service - handles ActiveRecord relations and plain Arrays
  • Page clamping, empty collection handling, grouped query support
  • Zero configuration - auto-included via Engine initializer

Usage

Controller
# Nothing to install, include, or configure
class PostsController < ApplicationController
  def index
    @pagy, @posts = rui_paginate(Post.all, limit: 10)
  end
end
View
<%= rui_pagination(pagy: @pagy) %>
<%= rui_table(pagy: @pagy) do |table| %>
  ...
<% end %>

v0.37.1

Released on February 7, 2026

Positional API for All Collection Components

Extended the Rails-compatible positional argument API from Select to all collection-based components: Checkbox, Combobox, and RadioButton.

Added

  • f.rui_combobox(:category_id, Category.all, :id, :name) - positional API
  • f.rui_radio_button(:status, ["Draft", "Published"]) - positional API
  • f.rui_checkbox(:category_ids, Category.all, :id, :name) - positional API
  • Shared NormalizeRailsArgs module used by all four components
  • Rails-Compatible API documentation section added to all four component docs pages

v0.37.0

Released on February 6, 2026

Select: Rails-Compatible Positional API

rui_select now accepts standard Rails f.select and collection_select positional argument patterns alongside the existing keyword API.

Added

  • f.rui_select(:status, ["Draft", "Published"]) - Rails f.select style
  • f.rui_select(:category_id, Category.all, :id, :name) - Rails collection_select style
  • Supports options hash and HTML options hash positional args
  • Standalone helper also supports positional args
  • Keyword API (collection:) remains fully backward-compatible

Fixed

  • Hash collections (optgroups) no longer broken by .to_a conversion

v0.36.0

Released on January 29, 2026

Black & White Color Support

Added :black and :white as pure colors to ColorBuilderHelper, enabling simple black and white styling across all components.

✨ Added

  • Added :black and :white as pure colors (no shade variants)
  • Works across all components: buttons, badges, text, dropdowns, etc.
  • Perfect for white text on dark backgrounds or black text on light backgrounds
  • Smart dark mode handling with slight color adjustments for visibility
  • Pure colors bypass shade system (no -500, -700 variants needed)

📝 Usage Examples

White text on dark backgrounds
<%= rui_text(text: "Hero Title", as: :h1, color: :white) %>
<%= rui_button(text: "White Button", color: :white, variant: :solid) %>
<%= rui_badge(text: "Featured", color: :white) %>
Black text styling
<%= rui_text(text: "Heading", as: :h2, color: :black) %>
<%= rui_button(text: "Black Button", color: :black, variant: :solid) %>

💡 Use Case:

Perfect for hero sections with dark backgrounds where you need pure white text that remains white in both light and dark modes.

v0.35.0

Released on January 29, 2026

Dropdown Active State Support

Major improvements to dropdown navigation menus with active state support and enhanced positioning.

✨ Added

  • Added active: parameter to mark current/active page items
  • Added active_class: parameter for custom active styling (only applies when active)
  • Active items automatically styled with primary color and medium font weight by default
  • Active state takes precedence over danger state
  • Developers control matching logic (exact path, prefix, or Rails helpers)
  • Eliminates need for conditional class ternaries in navigation menus

🚀 Improved

  • Smart positioning with collision detection
  • Auto-flips horizontally when near viewport edges
  • Auto-flips vertically when near viewport bottom
  • Improved Turbo-compatible link navigation
  • Better support for custom classes and data attributes on items

🎯 Migration Guide:

Before (messy):

class: "\#{current_page?(path) ? 'text-red-700' : 'text-gray-700'}"

After (clean):

active: current_page?(path), active_class: "text-red-700"

v0.34.2

Released on January 27, 2026

Live Search Keyboard Navigation

Improved keyboard navigation for Live Search component.

🐛 Fixed

  • Enhanced up/down arrow key navigation for better result selection
  • Updated css_highlight_nav_controller to find clickable elements inside containers

v0.34.1

Released on January 19, 2026

License Activation Improvements

Multi-environment activation with enhanced domain normalization.

🐛 Fixed

  • Fixed subdomain normalization (staging.myapp.com → myapp.com)
  • Enhanced multi-part TLD support (.co.uk, .com.au, .co.nz)
  • Resolved linting issues in license activation code
  • Leading dot normalization for consistent domain matching

v0.34.0

Released on January 18, 2026

AI-Assisted Development Documentation

Comprehensive guides for developers using AI agents with RapidRailsUI.

🎨 Added

  • Integration with llms.txt and llms-full.txt for accurate code generation
  • Real-world examples: Marketing sections, component building, design feedback
  • Troubleshooting guide for AI-assisted development

📚 Enhanced

  • Complete documentation of CodeBlock inline mode in llms.txt
  • Added inline: true parameter to CodeBlock component
  • Clear distinction between block and inline modes

v0.33.1

Released on January 18, 2026

Code Quality Fix

🐛 Fixed

  • Fixed style violation in Text component styles (if/else to elsif conversion)

v0.33.0

Released on January 18, 2026

CodeBlock Inline Mode & Typography Helper

New inline mode for CodeBlock and consolidated typography system.

🎨 Added

  • CodeBlock Inline Mode: inline: true for code mentions within text
  • Lightweight <code> tag for inline code references
  • Automatically escapes HTML for XSS safety
  • Usage: rui_code_block("<a>", inline: true)

📚 Typography Helper Consolidation

  • Added font sizes (xs through xl6) for body text
  • Added text overflow properties (truncate, ellipsis, line-clamp)
  • Added whitespace handling and word break utilities
  • Expanded style presets from 5 to 15+ patterns
  • Validation methods for all typography properties

🐛 Fixed

  • Link Component: Fixed flex to inline-flex for proper inline text flow
  • Input Component: Darker borders, improved prefix/suffix integration
  • Text Component: Paragraphs default to font-normal

v0.32.0

Released on January 17, 2026

AI-Ready Documentation System

Comprehensive llms.txt and llms-full.txt for AI-assisted development.

🎨 Added

  • llms.txt: 200-line condensed documentation
  • llms-full.txt: Auto-generated comprehensive reference
  • Rake Task: rake rapid_rails_ui:llms:generate
  • AI-Ready Badge in README
  • Install generator points to https://rapidrails.cc/llms.txt

v0.31.0

Released on January 17, 2026

Controller Upgrade System

New generator for upgrading Stimulus controllers after gem updates.

🎨 Added

  • Upgrade Generator: rails g rapid_rails_ui:upgrade
  • Detects controller changes after gem updates
  • Shows diffs before overwriting
  • Prompts for each modified file
  • --force flag to skip prompts
  • Version tracking in all controllers

⚠️ Breaking Changes

  • Link Component: Changed signature to match Rails link_to
  • Before: rui_link("/posts", "View Posts")
  • After: rui_link("View Posts", "/posts")
  • Keyword arguments still supported

v0.30.0

Released on January 16, 2026

Steps Component (PRO)

Comprehensive multi-step wizard with Turbo Frame integration.

🎨 Added

  • 7 Layout Variants: Horizontal, Vertical, Minimal, Progress, Breadcrumb, Timeline, Cards
  • Navigation Modes: Linear, Free, Completed-only
  • Step States: Pending, Active, Completed, Error
  • Session-based step tracking
  • Form integration with data persistence
  • Client validation before transitions
  • LocalStorage caching for instant UI restore
  • Full WAI-ARIA accessibility
  • Comprehensive Stimulus controller

v0.29.4

Released on January 12, 2026

LiveSearch Tailwind Fix

🐛 Fixed

  • Added missing padding classes to Tailwind safelist (pl-8, pl-10, pl-11, pr-8, pr-10, pr-11)
  • Fixed icon/text overlap in production builds

v0.29.3

Released on January 12, 2026

LiveSearch Improvements

🐛 Fixed

  • Modal trigger button now respects placeholder: parameter
  • Simplified LiveSearch controller (removed buggy auto-save)
  • Removed automatic search saving on keystroke
  • Controller reduced from 600 to 377 lines

v0.29.2

Released on January 11, 2026

Tailwind Safelist & Testing

🎨 Added

  • LiveSearch modal classes to Tailwind safelist
  • Dialog open state variants
  • Size utilities for icons

🔄 Changed

  • CI Workflow fixed to trigger on main branch
  • Added auto-detect test for missing components
  • Added bin/release_check script

v0.29.1

Released on January 9, 2026

LiveSearch Component Registry

🎨 Added

  • Added LiveSearch to tier registry and README
  • Added to STARTER_COMPONENTS (21 total)
  • Not a PRO-exclusive component

🐛 Fixed

  • Added missing component descriptions to generator
  • Updated JS config for components with Stimulus controllers

v0.29.0

Released on January 10, 2026

LiveSearch Modal Mode

Command palette / spotlight search UX for LiveSearch.

🎨 Added

  • modal: true opens search in centered dialog
  • Triggered by button click or ⌘K / Ctrl+K shortcut
  • Native <dialog> element with backdrop blur
  • Custom trigger slot
  • Branding footer with logo and name
  • All existing features work in modal

🔄 Changed

  • Extracted shared CSS highlight navigation module
  • ComboBox now uses CSS highlighting for navigation

v0.28.2

Released on January 8, 2026

Component Registry Update

🐛 Fixed

  • Added code_block and editable to component lists
  • Both added to STARTER_COMPONENTS (20 total)
  • Updated tier tests for new counts

v0.28.1

Released on January 8, 2026

Button & Table Improvements

🎨 Added

  • Button loading_text: Display different text while loading
  • Works with Turbo form submissions
  • Table Bulk Actions: Complete bulk action system
  • SVG checkboxes for consistent styling
  • rui_bulk_action helper
  • Selected IDs tracked as JSON array

v0.28.0

Released on January 8, 2026

Editable Component

Inline editing with Turbo integration.

🎨 Added

  • Click to edit, blur/Enter to save, Escape to cancel
  • Support for text, textarea, and number inputs
  • Client-side validation (required, min/max length, number range)
  • Server-side validation via Turbo Streams
  • Placeholder text when empty
  • Customizable wrapper tag with automatic typography
  • Pencil icon indicator on hover
  • Success flash animation
  • View helper: rui_editable(@model, :attribute, url: path)

v0.27.0

Released on January 8, 2026

CodeBlock Component

Display code snippets with syntax highlighting.

🎨 Added

  • Basic syntax highlighting for ERB, Ruby, JavaScript, CSS, JSON, Bash
  • Language badge with auto-detection
  • Copy-to-clipboard button
  • Dark theme styling with horizontal scroll
  • XSS-safe HTML escaping
  • Three usage patterns: block, string, keyword argument
  • Support for displaying ERB code with escaped tags
  • View helper: rui_code_block(language: :ruby, title: "Example") { code }

v0.26.2

Released on January 6, 2026

Tier Configuration Update

🐛 Fixed

  • Added missing components to tier registry
  • Table and Dialog added to Pro-exclusive
  • Pagination and Select moved to Starter
  • Pro-exclusive: 9 components, Starter: 18, Total: 27

v0.26.1

Released on January 6, 2026

Zeitwerk Compatibility Fix

🐛 Fixed

  • Renamed TableCellCell
  • Renamed TableColumnColumn
  • Renamed TableRowRow
  • Classes now match file names (Zeitwerk requirement)
  • Fixes 500 errors on production deploy

v0.26.0

Released on January 5, 2026

Table & Pagination Components

Full-featured data table with sorting, selection, pagination, and formatters.

🎨 Table Component (PRO)

  • Sortable columns (client-side and server-side)
  • Row selection with select-all and bulk actions
  • Pagy pagination integration
  • Responsive horizontal scroll
  • Sticky header option
  • 10 Built-in Formatters: date, datetime, currency, number, percentage, boolean, truncate, badge, etc.
  • Custom Proc formatters
  • Data-driven rows with auto-generated cells
  • Multiple variants: default, striped, bordered, hoverable
  • View helper: rui_table(variant: :striped, size: :sm)

🎨 Pagination Component

  • First-class Pagy integration
  • Four variants: simple, numbered, full, compact
  • Icon-only mode
  • Page jumper input
  • Turbo Frame support for AJAX pagination
  • Per-page selector option
  • View helper: rui_pagination(pagy: @pagy, variant: :full)

🔧 Install Generator

  • Dynamic controller discovery
  • Per-controller registration checking
  • Safe re-runs without duplicates

v0.25.0

Released on January 1, 2026

Dialog Component & Unified Color Validation

Major release introducing the Dialog component and comprehensive color validation improvements across all components.

🎨 New: Dialog Component

Native HTML <dialog> element with rich overlay functionality.

  • Modal mode for focused tasks (centered overlay)
  • Drawer mode for edge-positioned panels (right, left, top, bottom)
  • Native browser focus trapping and Escape key handling
  • Turbo Frame integration for dynamic content loading
  • CSS-only animations using @starting-style
  • Multiple size and position variants (sm, md, lg, xl, 2xl, full)
  • Full dark mode support
  • Auto-close on successful form submission
  • View helper: rui_dialog(title: "...", position: :center)
Dialog Examples
<%# Basic modal dialog %>
<%= rui_dialog(id: "edit-user", title: "Edit Profile") do %>
  <%= render "users/form", user: @user %>
<% end %>

<%# Drawer from right %>
<%= rui_dialog(id: "sidebar", mode: :drawer, position: :right) do %>
  <%= render "navigation/sidebar" %>
<% end %>

<%# With trigger button %>
<%= rui_dialog(id: "confirm", title: "Confirm Action") do |dialog| %>
  <% dialog.with_trigger { rui_button("Open", color: :primary) } %>
  Are you sure you want to continue?
<% end %>

🔒 Security Fixes

  • ColorBuilderHelper - Fixed unvalidated color fallback in resolve_color method
  • Invalid colors now safely default to "zinc" instead of returning unvalidated user input
  • Prevents potential CSS class injection via malformed color parameters
  • Added ColorValidationHelper as single source of truth for all color validation

🎨 New Semantic Colors

  • :default → maps to zinc (neutral, safe default)
  • :muted → maps to gray (for muted text/backgrounds)
  • :white → maps to neutral (light/white-like alternative)

🐛 Fixed

  • Color Validation - Unified across all 12+ components using ColorValidationHelper
  • Tailwind Safelist - Added missing shade 200 for soft variant hover states
  • Link Component - Default variant changed from :no_underline to :hover_underline (better UX)
  • Alert Component - Fixed color override logic when custom color is nil

Breaking Change: Link component default variant changed from :no_underline to :hover_underline. Users needing old behavior can explicitly specify variant: :no_underline.

v0.24.4

Released on December 26, 2025

ButtonTo Component Fix

Fixed

  • ButtonTo Form Wrapper - Fixed form wrapper not rendering due to ERB template referencing undefined local variable url instead of instance variable @url
  • Form now properly wraps button with correct action and method attributes
  • Fixes DELETE/PATCH/PUT requests that were failing due to missing form tag
  • CSRF token and _method hidden inputs are now inside a valid form tag

v0.24.3

Released on December 26, 2025

Dropdown Keyboard Navigation & Placement Fixes

Fixed critical dropdown component issues with keyboard navigation and placement positioning in production environments.

Fixed

  • Dropdown Keyboard Navigation - Fixed arrow key navigation that was skipping items due to complex CSS selectors
  • Simplified _getNavigableItems() selector to use explicit element type matching instead of complex :not() pseudo-selectors
  • Added explicit JavaScript filtering for disabled and hidden items (more reliable than CSS)
  • Placement Positioning - Fixed top, bottom, left, right placement classes not showing in production
  • Added focus-visible states to safelist for proper keyboard navigation visual feedback
  • Added Home/End key support for jumping to first/last menu items

New Features

  • Claude Code Skills - Created reusable diagnostic skills for Tailwind CSS issues:
    • /tailwind-safelist-audit - Diagnoses Tailwind CSS safelist issues with interview-based methodology
    • /color-safelist-sync - Synchronizes dynamic color generation with Tailwind safelist

v0.24.2

Released on December 24, 2025

Test Assertion Updates

Updated test assertions to match actual component rendering output after component refactoring.

Fixed

  • Dropdown Stimulus Controller Tests - Updated assertions to verify dual-controller setup (menu keyboard)
  • Avatar Fallback Icon Test - Updated to check for inline SVG rendering instead of references
  • SocialButton Color Tests - Updated assertions for inline style format
  • All 1393 tests passing - workflow publishing automated

v0.24.1

Released on December 24, 2025

Icon Component Refactoring

Refactored Icon component color logic to use ColorBuilderHelper for consistency across all components.

Fixed

  • Icon Component Color Logic - Refactored to use ColorBuilderHelper for consistency
  • Removed hardcoded color mappings - now dynamically supports all 22+ Tailwind colors
  • Automatically generates intelligent color shades via ColorBuilderHelper
  • All colors now have automatic dark mode variants
  • Enhanced documentation with comprehensive color examples

v0.24.0

Released on December 24, 2025

Component Fixes & Enhancements

Major fixes and improvements across multiple components with improved production compatibility and positioning.

SocialButton Component

  • Fixed ERB template constant resolution for Styles namespace
  • Fixed component.rb Styles method references to use fully qualified namespace
  • Added error handling for missing platform icons
  • Fixed icon mappings for social button platforms
  • Added cursor-pointer and focus rings to buttons

Avatar Component

  • Added sizing classes to Tailwind safelist (w-32, h-32, w-14, h-14, w-28, h-28)
  • Refactored borders from ring classes to standard border classes
  • Improved styling consistency

Dropdown Component

  • Added keyboard navigation support (Enter key selection)
  • Improved menu item accessibility
  • Fixed Stimulus controller naming conflicts

Tooltip & Popover Components

  • Improved positioning alignment and arrow placement
  • Fixed positioning calculations for edge cases
  • Updated to use inline CSS styles for production compatibility
  • Fixed Stimulus static values access with proper 'Value' suffix

Checkbox Component

  • Fixed checkmark rendering using peer-checked selector
  • Fixed icon positioning within checkboxes
  • Proper SVG rendering via IconProvider

Switch Component

  • Implemented animation using Stimulus controller
  • Added checkbox_switch_controller to installer generator

Select Component

  • Replaced arbitrary Tailwind classes with standard classes
  • Fixed chevron rendering for production

New Features

  • Docker License Validation Skip - New RAPID_RAILS_UI_SKIP_LICENSE_VALIDATION environment variable for CI/CD pipelines

v0.23.0

Released on December 21, 2025

Test Infrastructure Overhaul

Complete restructuring of component tests following Fizzy/Basecamp patterns for improved maintainability and organization.

New Test Helper Modules

  • LucideTestHelper - Mock for lucide-rails icons
  • ComponentTestHelper - Common assertions (render_html, assert_includes_class, etc.)
  • MockObjectsHelper - Factory methods for test fixtures
  • SharedExamples - Automatic variant testing patterns

Component Tests Restructured

All 19 component test files reorganized into focused modules:

  • Each component has dedicated folder with specific test files (basic, styling, accessibility, etc.)
  • 118 focused test files replacing 19 monolithic files
  • Components: accordion, alert, avatar, badge, checkbox, clipboard, combobox, date, image, link, popover, radio_button, select, text, text_fmt, textarea, tooltip, upload

test_helper.rb Refactored

  • Auto-discovery of components via Dir.glob
  • Optional VCR/WebMock configuration for HTTP mocking
  • Parallel test execution support

Bug Fixes

  • Fixed ViewComponent ActiveSupport::Configurable deprecation warning for Rails 8.1+
  • Silenced test suite warnings for cleaner output

Test Results

1393 runs, 2537 assertions, 0 failures, 0 errors

v0.22.0

Licensing

Released on December 21, 2025

New: Licensing System

Production license validation with tier support for Starter, Pro, and Agency tiers.

License Key Format

  • 7-segment format: RRUI-TIER-CUSTOMER-SEATS-DOMAINS-DATE-CHECKSUM
  • Tier codes: STR (Starter), PRO (Pro), AGY (Agency)
  • Seats & Domains: Configurable limits with :unlimited support
  • Legacy support: 4-segment keys still work (default to Starter tier)

Tier Module

  • RapidRailsUI.pro? / RapidRailsUI.starter? - Check gem tier
  • RapidRailsUI.component_available?(:dropdown) - Check component availability
  • RapidRailsUI.pro_exclusive?(:accordion) - Check if Pro-only
  • RapidRailsUI::INCLUDED_COMPONENTS - All 24 components
  • RapidRailsUI::PRO_EXCLUSIVE_COMPONENTS - 8 Pro-only components

TierGate Concern

Runtime access control for Pro-exclusive components:

  • Auto-skip: Disabled in development/test environments
  • Applied to: Accordion, Alert, Combobox, Date, Dropdown, Popover, Select, Upload
  • Error: TierAccessDenied with component name and current tier

Domain Normalization

  • Subdomains are FREE: staging.myapp.com → myapp.com (counts as 1 domain)
  • Multi-part TLDs: .co.uk, .com.au, .co.nz handled correctly
  • Examples: api.myapp.co.uk → myapp.co.uk

Tests Added

  • 98 new tests for licensing system
  • TierTest (28), TierGateTest (9), ActivationClientTest (30), LicenseTest (31)

Documentation

  • INSTALLATION.md: Domain counting section, troubleshooting table
  • LICENSE.txt: Domain counting examples, clearer tier descriptions
  • README.md: Commercial terms, tier comparison table

Bug Fix

  • Accordion Component - Various improvements and fixes

v0.21.0

Popover

Released on December 19, 2025

New: Popover Component (rui_popover)

Rich, interactive content on hover or click. Unlike Tooltip (text-only), Popover supports complex layouts with headers, bodies, footers, and interactive elements like buttons and links.

Structured Slots

  • with_trigger - The element that activates the popover (required)
  • with_header - Header section (optional)
  • with_body - Main content section (optional)
  • with_footer - Footer section, great for action buttons (optional)
  • with_custom_content - Flexible alternative to structured slots

Position & Width

  • Positions: top, right, bottom (default), left
  • Widths: sm (200px), base (280px), lg (320px), xl (400px)
  • Custom max-width: max_width: "350px" for precise control

Colors & Styling

  • default - Light background with subtle border
  • dark - Dark background for contrast
  • Tailwind colors - primary, success, warning, danger, blue, etc. (colored border)
  • Arrow - Optional arrow pointing to trigger (arrow: true)

Trigger Modes

  • hover (default) - Show on mouseenter, hide on mouseleave
  • click - Toggle on click, dismiss on click-outside or Escape
  • Delays: delay: 200 (show delay), hide_delay: 100 (hide delay)

Accessibility

  • role="dialog" on popover content
  • aria-describedby, aria-expanded, aria-haspopup on trigger
  • aria-hidden on popover when closed
  • Focusable trigger with tabindex="0"
  • Focus events (focusin/focusout) for keyboard users
  • Escape key dismissal for click mode

Usage Examples

Popover Examples
<% # Basic popover with structured slots %>
<%= rui_popover do |p| %>
  <% p.with_trigger do %>
    <%= link_to "@username", "#" %>
  <% end %>
  <% p.with_header do %>
    <strong>User Name</strong>
  <% end %>
  <% p.with_body do %>
    <p>Bio text here...</p>
  <% end %>
  <% p.with_footer do %>
    <%= rui_button("Follow", size: :sm) %>
  <% end %>
<% end %>

<% # Click triggered with custom width %>
<%= rui_popover(trigger: :click, width: :lg) do |p| %>
  <% p.with_trigger do %>Click me<% end %>
  <% p.with_body do %>Click outside to close<% end %>
<% end %>

<% # With arrow and position %>
<%= rui_popover(position: :right, arrow: true, color: :primary) do |p| %>
  <% p.with_trigger { rui_icon(:info) } %>
  <% p.with_body { "Information panel" } %>
<% end %>

Bug Fix

  • Popover Styles - Fixed nil color handling in validate_color method

v0.20.0

TailwindMerge

Released on December 18, 2025

New: TailwindMerge Helper

Intelligent Tailwind CSS class merging that resolves conflicts when users customize component styles.

  • Added tailwind_merge gem dependency for class conflict resolution
  • Created TailwindMergeHelper module with tw_merge method
  • Later classes override earlier conflicting classes (e.g., tw_merge("p-4", "p-6") returns "p-6")
  • Enables users to customize component styles via class parameters
  • Comprehensive test coverage (13 tests)

New: Input Class Override Support

Customize internal element styles with new class parameters:

  • label_class - Custom label styling
  • prefix_class - Custom prefix container styling
  • suffix_class - Custom suffix container styling
  • help_text_class - Custom help text styling
  • container_class - Input container styling
  • All use tw_merge for intelligent class conflict resolution

Bug Fix

  • Button - Icon-only buttons no longer show "Button" text fallback

v0.19.0

Tooltip

Released on December 18, 2025

New: Tooltip Component (rui_tooltip)

Contextual information on hover or focus. Display supplementary hints, labels, or explanations.

Positions & Sizes

  • Positions: top (default), right, bottom, left
  • Sizes: sm, base, lg

Colors

  • dark (default) - Dark background for contrast
  • light - Light background
  • Semantic: primary, success, warning, danger, info

Features

  • CSS arrow pointing to trigger element (uses border technique)
  • Arrow can be disabled with arrow: false
  • Two trigger modes: hover (default) and click
  • Configurable show delay via delay parameter (milliseconds)
  • Configurable hide delay via hide_delay parameter (prevents flickering)
  • Max-width support for long text with wrapping (max_width parameter)
  • Screen reader description via description parameter

Accessibility

  • role="tooltip" on tooltip content
  • aria-describedby and aria-hidden attributes
  • Focusable trigger with focus events
  • Smooth 150ms fade animation
  • Full dark mode support
  • 48 comprehensive tests

Usage Example

Tooltip Examples
<%= rui_tooltip(text: "This is helpful information", position: :top) do %>
  <%= rui_button("Hover me", variant: :outline) %>
<% end %>

<%= rui_tooltip(text: "Click to copy", trigger: :click, color: :primary) do %>
  <%= rui_icon(:copy) %>
<% end %>

New: Shared Popup Controller

Reusable Stimulus controller for hover/focus popups. Powers Tooltip and Popover components.

  • Configurable show delay and hide delay for smooth UX
  • Hover and click trigger modes
  • Smooth opacity transitions
  • ARIA attribute management
  • Click-outside dismissal for click triggers
  • Escape key handling
  • Dispatches cancelable events: popup:show, popup:shown, popup:hide, popup:hidden

v0.18.0

Combobox

Released on December 18, 2025

New: Combobox Component (rui_combobox, f.rui_combobox)

Searchable dropdown with custom styling and JavaScript interactivity. A powerful alternative to Select with enhanced UX features.

Selection Modes

  • Single select - Choose one option from the list
  • Multi-select - Choose multiple options with badge display (show_badges: true) or summary ("X items selected")
  • Max selections - Limit multi-select with max_selections parameter

Search & Navigation

  • Real-time search input for filtering options
  • Keyboard navigation (Arrow keys, Enter, Escape, Tab)
  • Icon support in options via icon_method parameter

Styling Options

  • Sizes: xs, sm, base, lg, xl (matches Select trigger styling)
  • Colors: Semantic + Tailwind colors for focus ring and borders
  • Shapes: square, rounded, pill
  • Variants: :default (bordered) and :underline

Form Integration

  • Collection support: Array, Hash, ActiveRecord::Relation
  • Form builder: f.rui_combobox(:method, collection: items, multiple: true)
  • View helper: rui_combobox(:name, collection: items)
  • Auto-detects selected value(s) from model
  • Auto-displays errors from model.errors

Behavior Options

  • searchable - Enable/disable search input
  • clear_on_select - Clear search after selection
  • close_on_select - Close dropdown after selection

Usage Example

Combobox Examples
<% # Single select with search %>
<%= rui_combobox(:country, collection: Country.all, label: "Select Country") %>

<% # Multi-select with badges %>
<%= f.rui_combobox(:tags, collection: Tag.all, multiple: true, show_badges: true) %>

<% # With icons in options %>
<%= rui_combobox(:category, collection: categories, icon_method: :icon_name) %>

v0.17.0

Select

Released on December 18, 2025

New: Select Component (rui_select, f.rui_select)

Native HTML <select> element with ViewComponent + Tailwind styling. A lightweight, JavaScript-free alternative to Dropdown for form submissions.

Styling Options

  • Variants: :default (bordered) and :underline (bottom border only)
  • Sizes: xs, sm, base, lg, xl (matches Dropdown sizing)
  • Colors: Semantic + Tailwind colors for focus ring
  • Shapes: square, rounded, pill

Features

  • Collection support: Array, Hash, ActiveRecord::Relation
  • Optgroup support for grouped options (nested hash format)
  • Multiple selection with rows parameter
  • Prompt and blank option support
  • Auto-detects selected value from model
  • Auto-displays errors from model.errors

Form Integration

  • Form builder: f.rui_select(:method, collection: items)
  • View helper: rui_select(:name, collection: items)
  • Full accessibility (native browser support)
  • Native mobile OS picker

Usage Example

Select Examples
<% # Basic select %>
<%= rui_select(:status, collection: Post.statuses, label: "Status") %>

<% # With optgroups %>
<%= f.rui_select(:category, collection: {
  "Fruits" => [["Apple", 1], ["Banana", 2]],
  "Vegetables" => [["Carrot", 3], ["Broccoli", 4]]
}) %>

<% # Multiple selection %>
<%= rui_select(:tags, collection: tags, multiple: true, rows: 5) %>

v0.16.1

Security

Released on December 17, 2025

Security

  • Avatar XSS Protection - Block potentially unsafe src attributes (javascript:, data:, vbscript: URIs)

Changed

  • JS Controller Reorganization - All Stimulus controllers moved to rapid_rails_ui/ namespace for better organization
  • Badge Component - Added memoization for improved performance
  • Component Cleanup - Minor documentation and code improvements across accordion, link components

Removed

  • Deprecated controller locations (old badge_controller.js paths)

v0.16.0

Dropdown

Released on December 17, 2025

New: Dropdown Component (rui_dropdown, f.rui_dropdown)

Versatile menu system for navigation and form selection with comprehensive features.

Two Modes

  • Action Dropdowns - Navigation menus, context menus, user menus
  • Selection Dropdowns - Form integration with hidden inputs for single/multi-select

Variants & Styles

  • Variants: solid, outline, ghost, soft
  • Colors: zinc, primary, success, warning, danger, info
  • Sizes: xs, sm, base, lg, xl
  • Shapes: square, rounded, pill

Rich Slot System

  • Items - Standard menu items with icons, descriptions, danger styling
  • Headers - Section headers for grouping
  • Dividers - Visual separators
  • Checkbox Items - Multi-select with checkmarks
  • Radio Items - Single-select with radio indicators
  • Toggle Items - Switch-style toggles
  • User Headers - User profile display with avatar
  • Avatar Items - Items with user avatars
  • Submenus - Nested menu panels
  • Search - Filter items as you type
  • Footer - Action buttons at bottom

Features

  • Keyboard Navigation - Arrow keys, Escape, Enter
  • Full ARIA Accessibility - role="menu", aria-expanded, etc.
  • Collection Support - Hash, Array, ActiveRecord::Relation
  • Form Builder Integration - f.rui_dropdown(:field, collection: [...])
  • Placement Options - top, right, bottom, left
  • Trigger Options - click, hover
  • Avatar Trigger - User menu with avatar display
  • Icon-Only Mode - Compact action menus

Usage Examples

Dropdown Examples
<% # Action dropdown (navigation) %>
<%= rui_dropdown(text: "Options") do |dropdown| %>
  <% dropdown.with_header(text: "Actions") %>
  <% dropdown.with_item(text: "Edit", href: edit_path, icon: :edit) %>
  <% dropdown.with_divider %>
  <% dropdown.with_item(text: "Delete", href: delete_path, icon: :trash_2, danger: true) %>
<% end %>

<% # Selection dropdown (form) %>
<%= rui_dropdown(:status, collection: Post.statuses, label: "Status") %>

<% # Form builder %>
<%= form_with(model: @post) do |f| %>
  <%= f.rui_dropdown(:category_id, collection: Category.all, label: "Category") %>
<% end %>

<% # With search and checkbox items %>
<%= rui_dropdown(text: "Filter") do |dropdown| %>
  <% dropdown.with_search(placeholder: "Search...") %>
  <% dropdown.with_checkbox_item(text: "Draft", value: "draft") %>
  <% dropdown.with_checkbox_item(text: "Published", value: "published") %>
<% end %>

Architecture Improvements

  • New JS controller organization under rapid_rails_ui/ namespace
  • Shared controllers: keyboard, search, switch
  • Component JS config extracted to YAML for easier maintenance

v0.15.0

API Update

Released on December 14, 2025

Form Component API Modernization

Checkbox, RadioButton, and Date components now support the positional first argument pattern (like Textarea and Input), making standalone usage cleaner.

New Syntax (recommended)

New Positional Syntax
<% # Checkbox - positional syntax %>
<%= rui_checkbox(:newsletter, label: "Subscribe") %>
<%= rui_checkbox(:terms, label: "I accept", required: true) %>
<%= rui_checkbox(:category_ids, collection: Category.all, label: "Categories") %>

<% # RadioButton - positional syntax %>
<%= rui_radio_button(:status, collection: Post.statuses, label: "Status") %>
<%= rui_radio_button(:priority, collection: priorities, variant: :card) %>

<% # Date - positional syntax %>
<%= rui_date(:birthday, label: "Birthday") %>
<%= rui_date(:event_date, variant: :picker, label: "Event Date") %>

<% # Form builder - unchanged (already clean) %>
<%= f.rui_checkbox(:terms, label: "I agree") %>
<%= f.rui_radio_button(:status, collection: statuses) %>
<%= f.rui_date(:start_date) %>

Backward Compatible

Old keyword-only syntax still works:

Legacy Keyword Syntax
<%= rui_checkbox(object: :user, method: :terms, label: "I agree") %>
<%= rui_radio_button(object: @post, method: :status, collection: statuses) %>
<%= rui_date(name: :birthday, label: "Birthday") %>

Why This Change?

  • Cleaner, more Rails-like syntax for standalone usage
  • Consistent API across all form components (Textarea, Input, Checkbox, RadioButton, Date)
  • Form builder usage was already clean - this brings standalone up to par

v0.14.0

Upload

Released on December 13, 2025

New: Upload Component (rui_upload, f.rui_upload)

New file upload component with two modes: styled file input and drag & drop dropzone.

Two Modes

  • Default - Styled native file input (consistent with Input component)
  • Dropzone - Drag & drop area with visual feedback (dropzone: true)

Usage

Upload Examples
<% # Basic file upload %>
<%= rui_upload(:avatar, label: "Profile Picture", accept: [:image]) %>

<% # Dropzone with multiple files %>
<%= rui_upload(:documents,
  label: "Documents",
  dropzone: true,
  multiple: true,
  accept: [:pdf, :document],
  max_files: 5,
  max_file_size: 10.megabytes
) %>

<% # Form builder %>
<%= f.rui_upload(:attachments, dropzone: true, multiple: true) %>

Features

  • File type restrictions - Accept symbols (:image, :pdf, :document, :video, :audio, :spreadsheet, :archive)
  • Size validation - Client-side max file size check (max_file_size)
  • Count limits - Limit number of files (max_files)
  • File preview - Image thumbnails or file icons with names and sizes
  • Remove files - Remove files from preview before upload
  • Real-time validation - Instant feedback for violations
  • 3 variants - default, filled, outline
  • 5 sizes - xs, sm, base (default), lg, xl
  • Dark mode - Complete dark mode support
  • Accessibility - Full ARIA support, keyboard navigation

View Upload Documentation →

v0.13.0

Textarea

Released on December 13, 2025

New: Textarea Component (rui_textarea, f.rui_textarea)

New multi-line text input component with character counting, auto-resize, and real-time validation.

Positional Argument Support

Cleaner syntax with positional first argument:

Textarea Examples
<% # Standalone - positional (recommended) %>
<%= rui_textarea(:notes, label: "Notes", rows: 4) %>

<% # Form builder %>
<%= f.rui_textarea(:body, label: "Content", rows: 8) %>

Variants

  • default - White background with border
  • filled - Gray background, no visible border
  • outline - Transparent background with border

Features

  • Character counter - Live count with "X / max" format (show_count: true)
  • Auto-resize - Grows/shrinks with content (auto_resize: true, max_rows)
  • Real-time validation - Visual feedback (green/red borders)
  • Resize control - :none, :vertical, :horizontal, :both
  • Wrap control - :soft, :hard, :off
  • 5 sizes - xs, sm, base (default), lg, xl
  • 3 shapes - square, rounded (default), pill (uses rounded-2xl)
  • All colors - Semantic + Tailwind colors for focus rings

View Textarea Documentation →

Fixed: Input & Textarea Stimulus Actions

Fixed Stimulus data-action attributes being HTML-encoded, which broke validation, password toggle, auto-resize, and character counter functionality.

  • Root cause - Rails tag helper escapes > to &gt; in attributes
  • Solution - Added .html_safe to action strings to preserve -> arrow syntax

v0.12.0

Input

Released on December 12, 2025

New: Input Component (rui_input, f.rui_input)

New text-based input component for form fields with real-time validation and rich customization options.

Input Types

  • text (default) - Standard text input
  • email - Email with browser validation
  • password - Obscured text with automatic show/hide toggle
  • number - Numeric input with min/max/step support
  • tel - Phone number (mobile keypad on touch devices)
  • url - URL with browser validation
  • search - Search with native clear button

Variants

  • default - White background with border
  • filled - Gray background, no visible border
  • outline - Transparent background with border

Features

  • Label with optional required indicator (*)
  • Help text below input
  • Real-time validation with visual feedback (Stimulus controller)
  • Password show/hide toggle (automatic for password inputs)
  • Prefix and suffix slots for icons or text
  • 5 sizes (xs, sm, base, lg, xl)
  • 3 shapes (square, rounded, pill)
  • All semantic colors + Tailwind colors for focus ring with colored backgrounds
  • Validation attributes: pattern, maxlength, minlength, min, max, step
  • Autocomplete & inputmode support for better UX
  • Dark mode support

Accessibility

  • Label association via for attribute
  • aria-describedby for help text and validation messages
  • aria-required and aria-invalid states
  • Password toggle has aria-label
  • Validation messages use role="alert"
Input Examples
<% # Basic usage %>
<%= rui_input(name: :email, type: :email, label: "Email Address") %>

<% # Form builder integration %>
<%= form_with model: @user do |f| %>
  <%= f.rui_input(:username, label: "Username", required: true) %>
  <%= f.rui_input(:email, type: :email, label: "Email") %>
  <%= f.rui_input(:password, type: :password, label: "Password") %>
<% end %>

<% # With prefix/suffix %>
<%= rui_input(name: :website, label: "Website") do |input| %>
  <% input.with_prefix do %>
    <span>https://</span>
  <% end %>
<% end %>

Bug Fixes

  • Fixed FormHelper#model_value to check respond_to? before calling method on form object
  • f.rui_input(:any_field) now works even if model doesn't have that attribute

View Input Documentation →

v0.11.1

Radio Button

Released on December 11, 2025

New: Radio Button Component (rui_radio_button, f.rui_radio_button)

New radio button component for single-selection form inputs with multiple variants.

Variants

  • radio (default) - Standard circular radio button
  • card - Bordered card with full-width click target
  • pill - Compact, tag-like rounded selections
  • button - Toolbar-style segmented controls

Features

  • Collection rendering from arrays, hashes, or ActiveRecord
  • Form builder integration (f.rui_radio_button)
  • Description support for each option
  • Required field indicator with asterisk
  • Help text and error message display
  • Vertical and horizontal layouts
  • 5 sizes (xs, sm, base, lg, xl)
  • All semantic colors + Tailwind colors
  • Dark mode support

Accessibility

  • role="radiogroup" on wrapper
  • aria-labelledby pointing to legend
  • aria-describedby for help text
  • aria-required and aria-invalid states
  • Proper label/input associations
Radio Button Examples
<% # Basic usage with enum %>
<%= rui_radio_button(
  object: @post,
  method: :status,
  collection: Post.statuses,
  label: "Post Status"
) %>

<% # Form builder with card variant %>
<%= form_with model: @post do |f| %>
  <%= f.rui_radio_button :priority,
    collection: [["Low", 1], ["Medium", 2], ["High", 3]],
    variant: :card,
    label: "Priority Level"
  %>
<% end %>

<% # Button variant (segmented control) %>
<%= f.rui_radio_button :view_mode,
  collection: [["List", "list"], ["Grid", "grid"]],
  variant: :button,
  layout: :horizontal
%>

Critical Bug Fix: Radio Button Styling

  • Fixed radio buttons displaying as filled circles when unchecked
  • Implemented MDN-recommended border-only styling approach
  • Unchecked radios now correctly display as hollow circles
  • Checked radios fill with color using thick borders (no background)
  • Added dedicated build_radio method in ColorBuilderHelper

View Radio Button Documentation →

v0.11.0

Image

Released on December 10, 2025

New: Image Component (rui_image)

Semantic HTML image component for displaying content images with captions, links, and visual effects.

Core Features

  • Semantic HTML: Uses <figure> and <figcaption> when caption is present
  • Performance: Built-in lazy loading, async decoding, fetchpriority
  • Responsive: srcset, sizes, width/height for layout shift prevention
  • Sizes: auto, full, xs-xl3
  • Shapes: default, rounded, circle, square
  • Aspect ratios: auto, square, video, portrait, landscape, wide, ultrawide
  • Object fit: cover, contain, fill, none, scale_down
  • Visual effects: grayscale, blur, sepia, zoom, shine, overlay
  • Link wrapping: Make images clickable with url parameter
  • Security: crossorigin and referrerpolicy support

Picture Element Support

  • New sources parameter for <picture> element
  • Art Direction: Different images for different viewports using media queries
  • Format Fallback: AVIF/WebP with automatic JPEG/PNG fallback
  • Dark Mode: Different images based on prefers-color-scheme
  • Retina/High DPI: Density descriptors (1x, 2x, 3x) support

Social Media Platform Sizes

  • Built-in presets for 2025 social media dimensions
  • Instagram: :ig_post, :ig_story, :ig_reel
  • Facebook: :fb_post, :fb_story, :fb_cover
  • X/Twitter: :x_post, :x_header
  • LinkedIn: :linkedin_post, :linkedin_cover
  • Plus: Pinterest, TikTok, YouTube presets
Image Examples
<% # Basic image (positional src argument) %>
<%= rui_image("photo.jpg", alt: "A beautiful sunset") %>

<% # With caption (uses figure/figcaption) %>
<%= rui_image("landscape.jpg", alt: "Mountain vista", caption: "View from the summit") %>

<% # Picture element with format fallback %>
<%= rui_image("photo.jpg", alt: "Photo", sources: [
  { srcset: "photo.avif", type: "image/avif" },
  { srcset: "photo.webp", type: "image/webp" }
]) %>

<% # Social media size %>
<%= rui_image("campaign.jpg", alt: "Campaign", size: :ig_post) %>

Clipboard Component - Enhanced Code Variant

  • Updated :code variant with Flowbite-style design for code blocks
  • Minimal transparent button with icon + "Copy" text
  • Success state turns green (emerald-400) with check icon + "Copied!"

View Clipboard Documentation →

v0.10.0

Clipboard

Released on December 10, 2025

New: Clipboard Component (rui_clipboard)

Copy text to clipboard with visual feedback and success states.

Variants

  • default: Standard button with icon and text
  • minimal: Subtle, text-only button
  • github: Icon-only button (GitHub style)
  • toolbar: GitHub PR-style with label + icon
  • code: Flowbite-style for code blocks

Features

  • Positional argument support: rui_clipboard("element-id")
  • Tooltip support with success state
  • Auto-detects input.value or element.textContent
  • Success feedback with icon swap for 2 seconds
  • All semantic colors and sizes (xs-xl)
  • Clean Stimulus controller (modern Clipboard API)
Clipboard Examples
<% # Basic usage %>
<%= rui_clipboard("code-block", text: "Copy Code") %>

<% # GitHub style (icon only) %>
<%= rui_clipboard("sha", variant: :github, tooltip: "Copy SHA") %>

<% # Code block style %>
<%= rui_clipboard("code-example", variant: :code) %>

View Date Component Documentation →

v0.9.1

Released on December 8, 2025

Date Component Improvements

  • Improved datetime time picker to match standalone time picker style with scrollable list UI
  • Removed max-w-sm constraint from calendar dropdown for better flexibility
  • Increased cell spacing (gap-1) in calendar for less crowded appearance
  • Implemented <dialog> element for semantic HTML (following Flux UI pattern)
  • Calendar now stays open when clicking date range presets for better UX

Checkbox Component

  • Added 11 new comprehensive tests covering icon slot (6 tests) and error state (5 tests)
  • Total test coverage: 711 tests, 1260 assertions

Bug Fixes

  • Fixed datetime picker time selection UI to use consistent scrollable list interface
  • Fixed calendar dropdown width constraint that was limiting larger date ranges
  • Fixed calendar closing unexpectedly when selecting presets in range mode

View Checkbox Component Documentation →

v0.9.0

Checkbox

Released on December 3, 2025

New: Checkbox Component (rui_checkbox, f.rui_checkbox)

New checkbox component with support for single checkboxes, collections, and switch variants.

Variants

  • checkbox (default) - Standard checkbox with checkmark
  • switch - Toggle switch with smooth animations
  • card - Bordered card with full-width click target
  • pill - Compact, tag-like rounded selections
  • button - Toolbar-style toggle controls

Features

  • Single checkbox with label and description
  • Collection checkboxes from arrays or ActiveRecord relations
  • Select all functionality with indeterminate state
  • Form builder integration (f.rui_checkbox)
  • Required field indicator with asterisk
  • Help text and error message display
  • Vertical and horizontal layouts
  • 5 sizes (xs, sm, base, lg, xl)
  • 2 shapes (square, rounded)
  • All semantic colors + Tailwind colors
  • Stimulus controller for select all interactivity
  • Dark mode support

Accessibility

  • Proper label association with for attribute
  • aria-describedby for help text
  • aria-required for required fields
  • aria-invalid for validation errors
  • role="group" with aria-labelledby for collections
  • Keyboard navigation support
Checkbox Examples
<% # Single checkbox %>
<%= rui_checkbox(name: "terms", label: "I agree to terms") %>

<% # Switch variant %>
<%= rui_checkbox(name: "notifications", label: "Enable notifications", variant: :switch) %>

<% # Collection with form builder %>
<%= form_with model: @user do |f| %>
  <%= f.rui_checkbox :interests, collection: ["Sports", "Music", "Art"] %>
<% end %>

<% # With select all %>
<%= rui_checkbox(name: "categories[]", collection: @categories, select_all: true) %>

View Checkbox Component Documentation →

v0.8.0

Alert

Released on December 2, 2025

New: Alert Component (rui_alert, rui_alert_turbo)

New contextual feedback message component for user notifications.

Types & Variants

  • Types: default, info, success, warning, danger, error, primary
  • Variants: solid, soft, outline
  • Sizes: xs, sm, base, lg, xl
  • Shapes: square, rounded, pill

Features

  • Dismissible alerts with close button
  • Auto-dismiss with synchronized countdown timer and progress bar
  • Pause/resume on hover for auto-dismiss alerts
  • Undo action support with automatic countdown (for soft-delete patterns)
  • Countdown buffer for better UX (shows 3s when actual is 5s)
  • Clickable alerts with Turbo navigation support
  • Single or multiple messages support
  • Border accent positions (left, top, bottom, all)
  • Custom icon slot (default icons per type)
  • Action buttons slot
  • Enter/exit animations
  • Full accessibility with ARIA attributes
Alert Examples
<% # Basic usage %>
<%= rui_alert(:success, title: "Success!") do %>
  Your changes have been saved.
<% end %>

<% # Dismissible with auto-dismiss %>
<%= rui_alert(:info, dismissible: true, auto_dismiss: true) do %>
  This will disappear in 5 seconds. Hover to pause.
<% end %>

<% # Undo action (great for delete operations) %>
<%= rui_alert(:warning, title: "Post deleted", undo_url: undo_post_path(@post)) do %>
  Click undo to restore.
<% end %>

<% # In Turbo Stream responses %>
<%= turbo_stream.prepend "flash" do %>
  <%= rui_alert_turbo(:success, messages: "Saved!") %>
<% end %>

View Alert Component Documentation →

v0.7.0

Accordion

Released on December 1, 2025

New: Accordion Component (rui_accordion)

New collapsible content sections component with smooth animations.

Variants & Options

  • Variants: default, bordered, separated, flush, ghost
  • Sizes: xs, sm, base, lg, xl
  • Shapes: none, rounded, square, pill
  • Colors: All semantic colors (zinc, primary, success, warning, danger, info)

Features

  • Multiple collapsible items with headers and content
  • Single or multiple open items (exclusive mode)
  • Smooth expand/collapse animations
  • Icon support in headers (leading and trailing positions)
  • Subtitle support for additional context
  • Custom expand/collapse icons (plus/minus swap)
  • Chevron position control (left or right)
  • Dividers between items
  • Always-open (non-collapsible) items
  • Turbo Frame support for lazy loading content
  • Keyboard navigation support
  • Full accessibility with ARIA attributes
Accordion Examples
<% # Basic usage %>
<%= rui_accordion do |accordion| %>
  <% accordion.with_item(title: "Section 1") do %>
    Content for section 1
  <% end %>
  <% accordion.with_item(title: "Section 2") do %>
    Content for section 2
  <% end %>
<% end %>

<% # With icons and expanded item %>
<%= rui_accordion(color: :primary, variant: :bordered) do |accordion| %>
  <% accordion.with_item(title: "Settings", icon: :settings, expanded: true) do %>
    Settings content
  <% end %>
<% end %>

<% # Multiple items can be open %>
<%= rui_accordion(exclusive: false) do |accordion| %>
  <% accordion.with_item(title: "Item 1") { "Content 1" } %>
  <% accordion.with_item(title: "Item 2") { "Content 2" } %>
<% end %>

View Accordion Component Documentation →

v0.6.0

Avatar

Released on November 30, 2025

New: Avatar Component (rui_avatar)

New versatile avatar component for displaying user profile images, initials, or placeholder icons.

Content Types

  • Image display with automatic fallback to initials or icon
  • Auto-generated initials from full names ("John Doe" → "JD")
  • Custom initials override
  • Fallback user icon when no image or name provided

Social Media Platform Sizes

  • Instagram: :ig_profile, :ig_story, :ig_post, :ig_comment
  • X/Twitter: :x_profile, :x_tweet, :x_reply
  • LinkedIn: :linkedin_profile, :linkedin_post, :linkedin_message
  • Discord: :discord_avatar, :discord_profile, :discord_server
  • GitHub: :github_avatar, :github_profile, :github_contrib
  • Plus: Facebook, YouTube, Slack, Twitch, TikTok, WhatsApp, Telegram, Dribbble, Behance, Pinterest

Features

  • Shapes: Circle (default), rounded, square
  • Ring Styles: White, gray, black, primary, success, warning, danger, Instagram story/live
  • Status Indicators: Online, offline, busy, away, dnd, streaming, idle, invisible
  • Clickable avatars with link to user profiles
  • Avatar groups with proper z-index stacking
  • Full accessibility with ARIA roles and labels
Avatar Examples
<% # Basic usage %>
<%= rui_avatar(src: user.avatar_url, alt: user.name) %>
<%= rui_avatar(name: "John Doe") %>

<% # Platform-specific sizes %>
<%= rui_avatar(src: url, size: :ig_story, ring: :ig_story) %>
<%= rui_avatar(src: url, size: :discord_avatar, status: :online) %>

<% # Avatar group %>
<div class="flex">
  <%= rui_avatar(src: url1, ring: :white, group_position: 1) %>
  <%= rui_avatar(src: url2, ring: :white, group_position: 2) %>
  <%= rui_avatar(src: url3, ring: :white, group_position: 3) %>
</div>

View Avatar Component Documentation →

v0.5.0

Text & TextFmt

Released on November 26, 2025

New: Text Component (rui_text)

Comprehensive typography component for headings, paragraphs, and inline text.

  • Full semantic HTML support (h1-h6, p, span, div, section, article, blockquote)
  • Complete typography control (size, weight, color, alignment, font family)
  • Visual variants (solid, outline, ghost, soft, link, strong, subtle)
  • Icon support with leading/trailing positions
  • 5 beautiful gradient styles: sunset, ocean, forest, sunrise, aurora
  • Blockquote support with citations
  • Prose mode for long-form content
  • Responsive headings that scale on larger screens

New: TextFmt Component (rui_text_fmt)

Rails text formatting helpers wrapped in typography component.

  • Text truncation with smart ellipsis
  • Text highlighting for search terms
  • HTML sanitization for user-generated content
  • Number formatting (currency, percentage, human-readable, phone)
  • Time formatting ("X ago" style timestamps)
  • Pluralization with count support
  • Auto-linkify URLs and emails
  • Default fallback for nil/empty values

View Text Component Documentation →

v0.4.0

Badge

Released on November 23, 2025

New: Badge Component (rui_badge)

  • Full Stimulus controller integration for dismissible badges
  • Pulsing animation support
  • Counter display
  • Icon support (leading/trailing positions)
  • Close button functionality
  • All variants (solid, outline, ghost, soft)
  • All sizes and shapes

Link Component Navigation Variant

  • New :nav variant for sidebar and navigation menus
  • Automatic current page detection (no manual checks required)
  • Color-themed navigation support
  • Block-level rendering with appropriate padding
Navigation Links
<% # Clean, automatic navigation links %>
<%= rui_link("Button", docs_button_path, variant: :nav) %>
<%= rui_link("Link", docs_link_path, variant: :nav, color: :sky) %>

Bug Fixes

  • License re-activation when license key changes (renewals)
  • Icon component default size changed to :sm

View Badge Component Documentation →

v0.3.0

Link

Released on November 20, 2025

New: Link Component (rui_link)

Styled link elements with Rails link_to syntax and features.

  • Conditional rendering support (if:, unless:, unless_current:)
  • Full Turbo integration (frames, streams, methods, prefetch)
  • External link security (automatic target="_blank")
  • Icon slot support with color inheritance/override
  • Active state highlighting for navigation
  • Text truncation with automatic tooltips
  • Email links (mail_to compatibility): subject, body, cc, bcc, reply_to
  • Phone links (phone_to compatibility): country_code
  • SMS links (sms_to compatibility): body, country_code
  • Underline variants (hover, animated, none)
Link Examples
<% # Basic link %>
<%= rui_link("View Posts", "/posts", color: :primary) %>

<% # Conditional rendering %>
<%= rui_link("Admin Panel", "/admin", if: admin?) %>

<% # External link with security %>
<%= rui_link("GitHub", "https://github.com", external: true) %>

<% # Email with all options %>
<%= rui_link(email: "contact@example.com", subject: "Inquiry", text: "Contact Sales") %>

Icon Color Override

Icons now automatically inherit parent component color by default with ability to override.

View Link Component Documentation →

v0.2.0

Icon & Social

Released on November 19, 2025

New: Icon Component (rui_icon)

  • Lucide icon integration with 1500+ icons
  • Size options: xs, sm, base, lg, xl, xl2-xl6
  • Color customization with full Tailwind palette
  • Position support: leading/trailing for flex layouts
  • Standalone vs inherited color modes
  • ARIA labels with auto-generation

New: Social Button Component (rui_social_button)

  • Pre-styled buttons for 18+ platforms
  • Auth: Google, GitHub, Facebook, Apple, Twitter/X, LinkedIn, Discord
  • Payments: PayPal, Stripe, Apple Pay
  • Official brand colors and styling
  • Icon-only mode with accessibility
  • Full width option
Icon & Social Button
<%= rui_icon(:heart, size: :lg, color: :danger) %>
<%= rui_social_button(:google, path: "/auth/google") %>
<%= rui_social_button(:paypal, "Pay with PayPal", path: checkout_path, method: :post) %>

Enhanced: Button with Icon Slot

  • Added with_icon slot support
  • Multiple icons (leading + trailing)
  • Icon-only button mode

View Social Button Component Documentation →

v0.1.0

Initial Release

Released on November 18, 2025

Button Component (rui_button)

  • Auto-detects button type (type="button" standalone, type="submit" in forms)
  • Auto-generates labels in forms ("Create Post" / "Update Post")
  • FormBuilder integration (f.rui_button)
  • Multiple submit buttons with name and value attributes
  • HTML5 form attributes: formaction, formtarget, form_id, formnovalidate
  • Block content support for custom layouts
  • Variants: solid, outline, ghost, soft
  • Sizes: xs, sm, base, md, lg, xl
  • Shapes: square, rounded, pill, circle
  • Full color palette support (semantic + Tailwind)
  • Loading and disabled states with ARIA

ButtonTo Component (rui_button_to)

  • Creates <form><button></form> wrapper automatically
  • HTTP method spoofing (POST, PATCH, PUT, DELETE)
  • CSRF token protection (automatic)
  • Hidden params support
  • Turbo/AJAX support
  • Shares all styling options with Button

Core Features

  • ViewComponent-based architecture
  • Pure Tailwind CSS (no custom CSS)
  • Zero JavaScript dependencies
  • Dark mode support
  • Turbo/Stimulus compatible
  • WCAG AA accessible

View Button Component Documentation →