Changelog
Track the evolution of RapidRails UI with detailed release notes for every version.
v0.58.2
LatestReleased 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 whenevershow_arrows && !arrows_inside?ORshow_indicatorswas 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 andindicator_position == :outside. Any variant with overlay/hero indicators (show_arrows: false,indicator_positionnot:outside) opened the wrapper and rendered nothing inside it, leaving an emptyflex ... gap-4 mt-4band under the slide — and, since the root grew taller by that empty row, overlay indicators positioned withbottom-4could 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 noslides.size > 1guard 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 belowsm, 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_classesandellipsis_classes(pagination/component.rb) now bake the mobile-hide into every page-series item — inactive link, current page, and ellipsis alike — composed throughtw_mergerather than string-concatenated, sincehiddenand the item's owninline-flexboth targetdisplayand raced on source order otherwise (the same defect class fixed previously in Tabs and Card).NAV_CLASSESgetsmin-w-0 max-w-fulland the wrapper getsw-full min-w-0(pagination/styles.rb) sojustify-*aligns inside the container instead of past it once the row can actually shrink.nav_classeswas subject to the same source-order race on itsgap-*utility, so every size other than:baseemitted two competinggap-*classes; it's now composed throughtw_mergetoo, 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
ListItemgetsicon: 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 whenicon:is omitted) renders byte-identical to 0.58.0. The<li>'s ownflex items-center gap-2.5wrapper is unchanged, so the remaining span(s) still align correctly with one fewer flex child. - Behaviour change:
icon: nilno longer renders a "?" placeholder. Pre-0.58.1, an expliciticon: nilfell 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: nilnow behaves identically toicon: false(no tile), since anilicon is unambiguously "no icon." Any consumer passingicon: niland 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
ListItemgetstrailing:. 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_listthreads a view context down sowith_itemcan 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 itstrailing:content on every row in that list. Off by default. Per item, the rule only draws when that item also hastrailing:set —leader: truewith notrailing:skips the rule for that row rather than raising, since a rule to nowhere is meaningless. Omitting bothtrailing:andleader:reproduces pre-0.58.0<li>markup byte-identical. - Text gets
shade:. Plain/default-variant text (novariant:) 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 tonilrather 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/:linkresolve their colour through a fixed shade table and are unaffected. Dark mode stays pinned at the colour's existing dark step regardless ofshade:— a caller wanting a specific dark shade must pass it viaclass: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 viatailwind_merge. Reaches the padding (the tier scale isnone/sm16px/md24px/lg32px, so any value between steps was previously unreachable) and the wrapper's fixedgap-4, which is what actually controls the space either side of the divider.nilby default; output is byte-identical when omitted.divider_class:on Card. Merged onto the divider<hr>.DIVIDER_CLASSESwas a hardcoded literal with noextra:, so the rule's colour and weight could not be changed at all.nilby default.
Fixed
Styles.content_wrapper_classesconcatenated instead of merging. It already accepted anextra:, but joined with a space, so passingp-5emitted"... gap-4 p-6 p-5"and left the winner to Tailwind's compiled source order rather than the call site. Now composes throughtailwind_merge, so the losing token is dropped from the attribute. Same defect class fixed in Tabs (0.56.3) andCard::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 pairedbg-transparent border-t-0withpx-0. The footer's horizontal padding is the card's gutter, sopx-0pulls 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 onlypt/pb, and documents both the gutter caveat and the fact that dark mode needs its owndark:bg-transparent(the dark background is a separate class thatbg-transparentdoes not conflict with, sotailwind_mergecorrectly leaves it standing). Becausellms-full.txtembeds 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'sFooterslot getsclass:/wrapper_class:. The footer bar (mutedbg-zinc-50fill,border-t) had no override mechanism at all —Footer#initializetook**optionsdocumented as "reserved for future passthrough" and never rendered them.wrapper_class:(nildefault) merges onto the footer's own wrapper element viatailwind_merge;class:merges onto the inner link/span, beatingfooter_link_classes' own color. Same conventionHeaderestablished 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 viatw_mergeso a conflicting utility (bg-transparentvs.bg-zinc-50,border-t-0vs.border-t) replaces the losing token outright instead of leaving both classes to race on Tailwind's compiled source order.Footer#initializenow declares both as explicit named parameters instead of swallowing everything into an unused**optionshash, 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 + ahover:highlight only).inactive_bg_color:(nildefault) gives every INACTIVE trigger a light color fill instead, atinactive_bg_shade:(default100, one of50/100/200/300/400). Follows the exactactive_bg_color:precedent from 0.56.2: same nil-means-unchanged contract, same warn-and-fall-back validation. Only:underline/:boxedread 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 boundaryactive_bg_color:already drew. Composes freely withactive_bg_color:on the same tabs instance — each reads a fully independent, individuallytw_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 baseborder-transparentalongside the activeborder-{color}-600), both tokens landed in the renderedclass=""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 activefocus-visible:ring-{color}collided with the always-onfocus-visible:ring-{color}-700every trigger gets, present in the default case too.trigger_classesnow runs its fully composed class array throughtw_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
:underlineactive tab border never rendered in a host withcorePlugins.preflight: false.border-b-2only setsborder-bottom-width— theborder-stylethat 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:underlinetab'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 explicitborder-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);:boxedhardcodes a plain white fill. Neither had a way to give the active tab a color highlight.active_bg_color:(nildefault) gives the ACTIVE trigger a light color fill instead, atactive_bg_shade:(default100, one of50/100/200/300/400). Follows the exactactive_shade:/active_icon_color:precedent from 0.55.0: same nil-means-unchanged contract, same warn-and-fall-back validation. Only:underline/:boxedread it —:pills/:spacedalready render their own solid active background and are byte-identical either way. Dark mode is intentionally untouched, same precedent asactive_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'sHeader/ListItemclass: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 aclass: "text-red-500"override could never win atailwind_mergeconflict against a color it never touched.class:now merges onto the title/text element itself; the old wrapper-targeting behavior is preserved under a newwrapper_class:param on both slots.
Added
rui_card'sHeaderacceptscolor:. The title was hardcoded to zinc with no color knob.with_header(title:, color: :primary)now resolves throughColorBuilderHelper, independent of the card's owncolor:. Defaultnilreproduces the old zinc output exactly.rui_card's list items accepttext_color:. The row text was hardcoded to zinc.with_item(text:, text_color: :success)now resolves throughColorBuilderHelper, independent ofcolor:(the tile tint). Defaultnilreproduces the old zinc output exactly.rui_card's list item icon tile supports two-tone viaicon_bg_color:/icon_color:. The tile derived both its background and its glyph from onecolor:— one hue could never express, e.g., an amber tile with an orange glyph. Both default tonil(falling back tocolor:), 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:whenas: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 oftext-lg. An explicitsize:now wins over the heading's default scale.
Added
- Card's
Header/Body/ListItem/Listslots now honorclass:. All previously stored the option but never read it at render — now merged onto the slot's own element viatailwind_merge. - Card's
Headeracceptssize:, andBodyacceptscolor:. The title was locked totext-lgregardless of heading tag;with_header(title:, size: :xl3)now controls it directly. The description was hardcoded zinc;with_body(text:, color: :success)now resolves throughColorBuilderHelper. Both default to today's exact output. - Card's list items accept
size:/icon_shape:/icon_size:, and the list gets avariant:/layout:pair. The icon tile was locked to arounded-fullsize-5circle;icon_shape:(:circle/:rounded/:square) andicon_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: :inlinewraps rows in a flex-wrap row instead of a column. All defaults reproduce pre-0.56.0 output exactly. - Tabs gets an
:xltrigger size tier.TRIGGER_SIZE_CLASSEStopped 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 explicitw-full. - Helper-emitted Tailwind classes were missing from the generated safelist.
rui_text(size: :xl8)renderstext-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
@sourcepaths never actually got rewritten on boot. The installed initializer is supposed to rewrite stale@sourcepaths in the host'sapplication.cssto the currently installed gem location on every boot, but its regex required a literal/immediately afterrapid_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/:spacedactive trigger's solid bg/hover/border/focus-ring shade — one of500/600/700/800/900/950(an invalid value warns in dev/test and falls back to900). Previously every color's active shade was fixed by category (900 for:primary-style colors, 700 for standard colors, 400 for the bright/:warningfamily) with no escape hatch. Left at thenildefault, 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:spacedsolid background) — inactive tabs and the label itself are unaffected either way.active_icon_color:isnilby default (icon matches label, unchanged behavior);active_icon_shade:is one of100/200/300/400/500(default300), ignored unlessactive_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#optionssilently 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**optionsalready gets (Card's pattern), and warns in dev/test. ArbitraryTaboptions 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: runbin/rails rui:update.
v0.54.1
SecurityReleased 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 throughrui_textwith String/Hash gradients.
Fixed
- Date
:pickervariant (the default) now carriesaria-invalidandaria-describedbyerror wiring, matching the native variant. Docs corrected::pickeris 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_llmsbuilds 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, andaria-describedbyreferences it exactly when the error renders (error id wins over help text id when both are present), replicating Dropdown's existing reference implementation. - Input —
aria-describedbynow includesvalidation_message_idwhen a server-side error is present even withvalidation: falseset (previously gated only on thevalidation:flag, so a genuine model error on avalidation: falsefield 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: trueworks. 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 nativeresetevent and rebuilds selection state (hidden inputs,aria-selected, checkboxes/radios, badges, trigger text) from the selection captured atconnect()— a native reset alone can't handle combobox's dynamically-managed hidden inputs correctly. - Server-disabled options stay disabled. Options rendered with
disabled: trueare 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 undermax_selections. - Grouped-collection value uniqueness. Selection lookup matches by
valuealone, 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#closeOnEscapeaction. - Dropdown —
collection:plus block content (with_itemslots) together no longer silently drop the block's items; collection processing moved tobefore_renderso 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 optionvalueand 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: ...)(nomodel:/as:) no longer renders a malformed, Rack-unparseablename="[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_castreader 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: falseopts atype: :tel/:urlinput out of the auto-formatting listeners entirely — the NANP phone formatter corrupts international phone numbers.value: falseis now honored as an explicit value instead of being silently overridden by the object-derived value (same fix as Select'sselected:, below).- Standalone
id/aria-describedbytargets now agree with each other instead of each minting a fresh random suffix per render. shape: nilno longer crashes render (validated/defaulted the same waycoloralready 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**optionsand renders a bogusmethod="..."HTML attribute —method:is now an accepted keyword like every sibling component. selected: false(or[]) andvalue: falseare now honored as explicit values instead of being silently overridden by the object-derived value.shape: nilno longer crashes render, same fix as Input.
Editable
- The documented
turbo_stream.replaceerror pattern no longer detaches the Stimulus controller mid-error-handling. The controller element now carriesid="<id>_root"; a new innerclass="contents"wrapper carries the original id thatturbo_stream.replacetargets, so replacing it swaps content without ever touching the controller element itself.
Button
formmethod: :put/:patch/:deletenow always uses the Rails_methodhidden-field strategy, regardless ofform_builder_mode?or whethername:/value:were also set — previously the hack only applied when both were nil, so any button also settingname:/value:emitted an invalidformmethod="delete"attribute that browsers silently ignore (submitting the wrong HTTP verb with no visible error). A caller-suppliedname:/value:is now overridden with aRails.logger.warninstead.
Also in this release
- FormBuilder
index:support. Allf.rui_*methods now accept a manual nested-attributesindex:option (e.g. inside a hand-rolledfields_forloop), threading it into name/id derivation instead of leaking out as a bogusindex="3"HTML attribute. - FormBuilder string method names.
f.rui_input("email")and friends now normalize a String method name to a Symbol instead of raisingArgumentError, matching vanilla Rails' own form helpers. - Upload —
max_filesis 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 (baredata-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 fromthis.identifier, matching whatever namespace is configured.
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 — nestedrui_tabsinside 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, withtablist_position: :start | :endfor vertical (flipped viaflex-row-reverse, not DOM reordering, so keyboard focus order always matches DOM order).mobile_layout: :grid— 2-column grid belowmd, row frommdup (horizontal only).full_widthstretches tabs equally viaflex-1.- Multi-badge support per tab: a
badges:array ({text:, color:, position:}) plus abadge:/badge_color:shortcut for the common single-counter case. deep_link: truereadslocation.hashon connect and writes it back (viahistory.replaceState, no extra history entries) on every switch.animation: :fade | :none— fade-in panel reveal, fully suppressed underprefers-reduced-motionthrough a centralized_motionSuppressed()gate in the newrui--tabscontroller.- Full WAI-ARIA APG contract:
role="tablist"/"tab"/"tabpanel",aria-selectedset explicitly on every tab,aria-controls/aria-labelledby, roving tabindex,aria-disabled+ disabled tabs skipped entirely during arrow traversal, inactive panels hidden via thehiddenattribute so no-JS degrades to the first panel visible. panels: falserenders the tablist only, for consumers wiring their own external panels against the renderedaria-controlsids.
Also in this release
- Gemspec
post_install_message. Reminds jsbundling-rails consumers to runbin/rails g rapid_rails_ui:install(or the newrui:update) afterbundle update rapid_rails_ui, since a plainbundle updateonly touches the Ruby gem, not the host's local Stimulus controller copies. rui:updaterake task. Runs the install generator's controller-sync step, regenerates the JS controller copies and the gem-managedindex.jsregistration block, thenyarn buildifpackage.jsondefines abuildscript. 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_textareahad 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 — standalonerui_input(object:, method:)still doesn't check model errors), render the error inline with red border/focus styling, and setaria-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/inputelements on every Turbo navigation, which can wrap RRUI's own markup and break its styling and wiring.Select,Input,Textarea,Checkbox,RadioButton,Upload, andDatenow render adata-rui="<component>"marker on their control element(s), so a host app can exclude them withdocument.querySelectorAll("select:not([data-rui])")(or theinput/textareaequivalent). Purely additive. See the Select docs' Legacy Select Enhancers section. - RRUI-003:
f.rui_button(and 9 otherf.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 torender_in, unlike every standalonerui_*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 nonexistenttext-muted-*classes, leaving muted text (includingrui_statlabels) unstyled.:mutednow resolves through the semantic color system and is host-configurable viaconfig.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, andhref:whole-card link mode with hover shadow lift and automaticaria-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; nowborder. - 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 withborder-solid(also applied to Dropdown, Button, Clipboard, and Date clear button). tailwind_classes.ymlmanifest additions forborder,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(primaryis not a Tailwind color, so an active item rendered with no highlight); now resolves the dropdown's color throughconfig.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 throughconfig.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/:accentthroughconfig.colors. - Dropdown — the trigger focus ring follows
config.colors(and:secondaryno 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,
:primarynow resolves tozinceverywhere (matchingbutton/badge). Apps that setconfig.colorsget their configured family; apps relying on the old hardcoded blue/neutral for:primarywill 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:, andrespect_reduced_motion:(defaulttrue— honours OSprefers-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:andshow_indicators:independently on/off.loop: falsestops at the ends witharia-disabled="true"on the boundary arrow. - Full ARIA carousel pattern.
role="region",aria-roledescription="carousel", indicatoraria-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 maintext:. Distinct from the existingdescription:, 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 witharia-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, andrui_comboboxcall sites continue to work without changes. tailwind_mergedependency 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 emitdata-controller="rui--checkbox",data-action="evt->rui--dialog#open",data-rui--menu-target, etc. Defaultnilpreserves current behavior.StimulusHelpermixin (lib/rapid_rails_ui/helpers/stimulus_helper.rb). Providesstimulus_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=ruiflag. Bakesconfig.stimulus_namespace = "rui"into the generated initializer AND uses the same value forapplication.register("rui--checkbox", ...)calls inapp/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 = nilmeans zero behavior change for existing consumers. Opt in only if you have host-controller name collisions; pass--stimulus-namespace=ruiwhen running the install generator (or set the config and updateapplication.registercalls 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_registrationsnow detects host bundler from the Gemfile (importmap-railsvsjsbundling-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|jsbundlingflag overrides auto-detection. - Optional class-name namespace prefix (#69). New
--namespace-imports=Ruiflag (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 previouscopy_stimulus_controllersglobbedshared/*_controller.js, missingshared/turbo_fetch.js(the helper imported byalert_controller,editable_controller, andkanban_controller). esbuild and webpack hosts failed to build withCould not resolve "./shared/turbo_fetch". Glob now matchesshared/*.jsso all helpers are copied alongside the controllers. Stimulus registration logic still only registers*_controller.jsfiles, 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 regeneratedsafelist.css(v4), av3_safelist.jsCommonJS module (v3 backup), and a markdown LLM agent reference.- CI drift detection.
bin/generate-schemasnow runs the new task..github/workflows/validate-schemas.ymlenforces no drift betweentailwind_classes.ymland its generated outputs. - LLM agent reference. Canonical machine readable list of classes the gem emits. Agents no longer need to trace
ColorBuilderHelperinterpolation logic to know what classes exist.
Changed
safelist.cssis now generated fromtailwind_classes.yml(was hand maintained). Same content, single source. v4 installer behavior unchanged.
Notes
- v3 explicit safelist injection into
tailwind.config.jsis deferred. The_tailwind_manifest.rbis already covered by the v3 content glob added in v0.44.2, so class detection works without a separate safelist edit. The generatedv3_safelist.jsships 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 previousbefore: /\s*\]/anchor matched any closing bracket, so on a real worldtailwind.config.jswithcontent,theme.fontFamily, andpluginsarrays the gem path landed insidefontFamilyAND insideplugins(with a doubled comma producing a JS syntax error). Now scans for thecontent:array specifically with balanced bracket matching. - Skip by class name false positives.
inject_controller_registrationspreviously skipped controllers when their class name (for exampleAccordionController,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 afterbundle update rapid_rails_ui. The v3 path now prepends abundle show rapid_rails_uiresolver at the top oftailwind.config.jsand references it via JS template literals insidecontent:, 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.mdlisting 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_mergedependency. Relaxed constraint from~> 0.13to~> 1.1to 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.jsutility. Extracted CSRF token and Turbo aware fetch intoshared/turbo_fetch.js. Eliminates duplicate CSRF and Turbo Stream handling across alert, editable, and kanban controllers.
Changed
- Alert controller. Refactored
sendUndoRequest()to use sharedturboFetchandhandleTurboStreamutilities. - 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), optionallabel:andshow_value:, CSSanimated: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, composableicon/sparkline/footerslots. Composesrui_text,rui_badge,rui_progress. Zero JavaScript. Starter tier.rui_kanban- Drag-and-drop kanban board. HTML5 native drag API + Stimulus controller. Columns withwith_column(id:, title:), cards withcol.with_card(id:). Column accents, card accents, WIP limits (max_cards:), custom headers/footers, scrollable columns (max_height:), static mode (draggable: false). Server persistence viamove_url:auto-PATCH with Turbo Stream support. Full keyboard navigation (Space/Arrows/Escape). WCAG 2.2 accessible. Pro tier.
Usage
<%= rui_progress(75, label: "Open tickets", show_value: true, color: :primary) %>
<%= rui_progress(42, color: :warning, size: :sm, animated: true) %>
<%= rui_stat("Monthly Revenue", "$32,209",
change: "+24% vs last month",
change_color: :emerald,
progress: 78
) do |s|
s.with_icon(:trending_up)
end %>
<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_dropdownandrui_accordioncomponents 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. Supportssubject:,body:,cc:,bcc:,reply_to:. Fullrui_linkstyling.rui_tel(text, number)- Dedicated phone link helper. Supportscountry_code:. Fullrui_linkstyling.rui_sms(text, number)- Dedicated SMS link helper. Supportsbody:,country_code:. Fullrui_linkstyling.- Accordion
single_open:- Friendly alias forexclusive:. 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 bygenerate_llms_mini.
Changed
- Dialog
backdrop_close:- Renamed fromdismissible:for clarity. Old param still works with deprecation warning. Stimulus value renamed tobackdropCloseValue. - Upload
max_file_size_bytes:- Renamed frommax_file_size:to make unit explicit. Old param still works with deprecation warning. - Date
input_type:- Renamed fromtype:to avoid shadowing Ruby'sObject#type. Old param still works with deprecation warning. - Combobox
close_on_select: :auto- Default changed fromtrueto:auto. Closes in single-select, stays open in multi-select. Passtrue/falseto 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:→ userui_mailto(text, address)rui_link phone:→ userui_tel(text, number)rui_link sms:→ userui_sms(text, number)rui_link method: :delete(non-GET) → usedata: { turbo_method: :delete }orrui_button_torui_dialog dismissible:→ usebackdrop_close:rui_upload max_file_size:→ usemax_file_size_bytes:rui_date type:→ useinput_type:
Usage
<%= 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 viagroup-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: falsesuppresses label. - Unique IDs - Record ID included in input ID (
user_42_completed) for safe use in lists. - Text
leading:/tracking:- Shorthand aliases forline_height:andletter_spacing:. Works on headings. - Schema system -
compositions.ymlandgotchas.ymlfor component composability docs.
Changed
- Checkbox controller - Added
submitForm()action for auto-submit. - JS controller cleanup - Removed duplicated Stimulus controllers from component directories.
Strikethrough Example
<%= 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 modeid_method:- Customize which method/key extracts row IDs (default::id)- Supports all existing features: formatters, sorting, selection, pagination, responsive modes
Usage
<%= 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 meancolor:?"). All 31 components. - Badge deprecation -
status:param warns to usecolor:instead. Will be removed in v1.0. - Alert cascade deprecation - Warns when
undo_url:auto-enablesauto_dismiss:, and whenauto_dismiss:/dismissible:auto-enablesanimate:andshow_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 controllersPageInforesult object - duck-types with Pagy (.page,.pages,.count,.from,.to)Paginatorservice - handles ActiveRecord relations and plain Arrays- Page clamping, empty collection handling, grouped query support
- Zero configuration - auto-included via Engine initializer
Usage
# Nothing to install, include, or configure
class PostsController < ApplicationController
def index
@pagy, @posts = rui_paginate(Post.all, limit: 10)
end
end
<%= 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 APIf.rui_radio_button(:status, ["Draft", "Published"])- positional APIf.rui_checkbox(:category_ids, Category.all, :id, :name)- positional API- Shared
NormalizeRailsArgsmodule 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 stylef.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_aconversion
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
:blackand:whiteas 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,-700variants needed)
📝 Usage Examples
<%= rui_text(text: "Hero Title", as: :h1, color: :white) %>
<%= rui_button(text: "White Button", color: :white, variant: :solid) %>
<%= rui_badge(text: "Featured", color: :white) %>
<%= 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: trueparameter 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: truefor 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
--forceflag 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_checkscript
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: trueopens 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_actionhelper- 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
TableCell→Cell - Renamed
TableColumn→Column - Renamed
TableRow→Row - 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)
<%# 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_colormethod - Invalid colors now safely default to "zinc" instead of returning unvalidated user input
- Prevents potential CSS class injection via malformed color parameters
- Added
ColorValidationHelperas single source of truth for all color validation
🎨 New Semantic Colors
:default→ maps tozinc(neutral, safe default):muted→ maps togray(for muted text/backgrounds):white→ maps toneutral(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_underlineto: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
urlinstead 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
_methodhidden 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
ColorBuilderHelperfor 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_VALIDATIONenvironment 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 iconsComponentTestHelper- Common assertions (render_html, assert_includes_class, etc.)MockObjectsHelper- Factory methods for test fixturesSharedExamples- 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::Configurabledeprecation 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
LicensingReleased 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
:unlimitedsupport - Legacy support: 4-segment keys still work (default to Starter tier)
Tier Module
RapidRailsUI.pro?/RapidRailsUI.starter?- Check gem tierRapidRailsUI.component_available?(:dropdown)- Check component availabilityRapidRailsUI.pro_exclusive?(:accordion)- Check if Pro-onlyRapidRailsUI::INCLUDED_COMPONENTS- All 24 componentsRapidRailsUI::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:
TierAccessDeniedwith 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
PopoverReleased 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 contentaria-describedby,aria-expanded,aria-haspopupon triggeraria-hiddenon popover when closed- Focusable trigger with
tabindex="0" - Focus events (focusin/focusout) for keyboard users
- Escape key dismissal for click mode
Usage 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_colormethod
v0.20.0
TailwindMergeReleased on December 18, 2025
New: TailwindMerge Helper
Intelligent Tailwind CSS class merging that resolves conflicts when users customize component styles.
- Added
tailwind_mergegem dependency for class conflict resolution - Created
TailwindMergeHelpermodule withtw_mergemethod - 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 stylingprefix_class- Custom prefix container stylingsuffix_class- Custom suffix container stylinghelp_text_class- Custom help text stylingcontainer_class- Input container styling- All use
tw_mergefor intelligent class conflict resolution
Bug Fix
- Button - Icon-only buttons no longer show "Button" text fallback
v0.19.0
TooltipReleased 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
delayparameter (milliseconds) - Configurable hide delay via
hide_delayparameter (prevents flickering) - Max-width support for long text with wrapping (
max_widthparameter) - Screen reader description via
descriptionparameter
Accessibility
role="tooltip"on tooltip contentaria-describedbyandaria-hiddenattributes- Focusable trigger with focus events
- Smooth 150ms fade animation
- Full dark mode support
- 48 comprehensive tests
Usage Example
<%= 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
ComboboxReleased 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_selectionsparameter
Search & Navigation
- Real-time search input for filtering options
- Keyboard navigation (Arrow keys, Enter, Escape, Tab)
- Icon support in options via
icon_methodparameter
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 inputclear_on_select- Clear search after selectionclose_on_select- Close dropdown after selection
Usage Example
<% # 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
SelectReleased 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
rowsparameter - 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
<% # 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
SecurityReleased on December 17, 2025
Security
- Avatar XSS Protection - Block potentially unsafe
srcattributes (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
DropdownReleased 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
<% # 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 UpdateReleased 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)
<% # 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:
<%= 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
UploadReleased 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
<% # 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
v0.13.0
TextareaReleased 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:
<% # 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
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>in attributes - Solution - Added
.html_safeto action strings to preserve->arrow syntax
v0.12.0
InputReleased 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
forattribute aria-describedbyfor help text and validation messagesaria-requiredandaria-invalidstates- Password toggle has
aria-label - Validation messages use
role="alert"
<% # 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_valueto checkrespond_to?before calling method on form object f.rui_input(:any_field)now works even if model doesn't have that attribute
v0.11.1
Radio ButtonReleased 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 wrapperaria-labelledbypointing to legendaria-describedbyfor help textaria-requiredandaria-invalidstates- Proper label/input associations
<% # 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_radiomethod in ColorBuilderHelper
v0.11.0
ImageReleased 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
urlparameter - Security: crossorigin and referrerpolicy support
Picture Element Support
- New
sourcesparameter 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
<% # 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
:codevariant with Flowbite-style design for code blocks - Minimal transparent button with icon + "Copy" text
- Success state turns green (emerald-400) with check icon + "Copied!"
v0.10.0
ClipboardReleased 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)
<% # 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) %>
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-smconstraint 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
v0.9.0
CheckboxReleased 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
forattribute aria-describedbyfor help textaria-requiredfor required fieldsaria-invalidfor validation errorsrole="group"witharia-labelledbyfor collections- Keyboard navigation support
<% # 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) %>
v0.8.0
AlertReleased 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
<% # 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 %>
v0.7.0
AccordionReleased 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
<% # 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 %>
v0.6.0
AvatarReleased 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
<% # 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>
v0.5.0
Text & TextFmtReleased 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
v0.4.0
BadgeReleased 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
:navvariant for sidebar and navigation menus - Automatic current page detection (no manual checks required)
- Color-themed navigation support
- Block-level rendering with appropriate padding
<% # 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
v0.3.0
LinkReleased 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)
<% # 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.
v0.2.0
Icon & SocialReleased 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
<%= 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_iconslot support - Multiple icons (leading + trailing)
- Icon-only button mode
v0.1.0
Initial ReleaseReleased 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
nameandvalueattributes - 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