<div> # ODT UI for Ruby on Rails (`odt-ui-rails`) **Standalone CSS-first design tokens, Hotwire-ready UI components, and ActionView helpers for Ruby on Rails.** [](https://badge.fury.io/rb/odt-ui-rails) [](LICENSE) [](https://www.ruby-lang.org) [](https://rubyonrails.org) [](https://hotwired.dev) [](sig/odt_ui.rbs) </div> --- ## Overview `odt-ui-rails` brings the **ODT Design System** directly into Ruby on Rails. It is architected from the ground up to provide a **Standalone CSS First** experience: - 📦 **Zero Host Build Tools Required:** Ships pre-compiled CSS custom properties and `.odt-*` component styles. Works out-of-the-box with Propshaft, Sprockets, or no asset pipeline at all. - ⚡ **Seamless Tailwind CSS v4 Interoperability:** Includes a complete `@theme` token configuration for Tailwind v4 projects, allowing custom utility classes (`class: "..."`) to merge effortlessly onto components. - 🚀 **Hotwire & Turbo Native:** First-class helpers for Turbo Streams (`odt_toast_stream`), Flash message auto-toasts (`odt_flash_toasts`), and Stimulus-powered Modals (`odt_modal`). - 💎 **Strict Type Signatures:** Full RBS definitions included for Ruby LSP, Steep, and Solargraph IDE autocompletion. --- ## Table of Contents - [Installation](#installation) - [Asset Pipeline Configuration](#asset-pipeline-configuration) - [Component Reference](#component-reference) - [Feedback & Overlays](#feedback--overlays) - [Toasts & Toaster](#toasts--toaster) - [Modals](#modals) - [Alerts](#alerts) - [Actions & Indicators](#actions--indicators) - [Buttons](#buttons) - [Badges](#badges) - [Form Controls](#form-controls) - [Input Field](#input-field) - [Textarea](#textarea) - [Select Dropdown](#select-dropdown) - [Checkbox & Radio](#checkbox--radio) - [Form Group](#form-group) - [Layout & Typography](#layout--typography) - [Cards](#cards) - [Typography (Heading & Text)](#typography-heading--text) - [Avatars](#avatars) - [Theming & Dark Mode](#theming--dark-mode) - [License](#license) --- ## Installation Add the gem to your application's `Gemfile`: ```ruby gem "odt-ui-rails", "~> 2.2.0" ``` Install dependencies: ```bash bundle install ``` Run the automated install generator: ```bash bin/rails g odt:install ``` The generator will: 1. Detect your asset pipeline (Tailwind CSS v4, Propshaft, or Sprockets) and safely configure the required `@import` statements. 2. Copy `sig/odt_ui.rbs` for Ruby LSP IDE autocompletion. 3. Copy Stimulus controllers (`odt_toast_controller.js`, `odt_modal_controller.js`) to `app/javascript/controllers/` if Stimulus is present. --- ## Asset Pipeline Configuration If configuring stylesheets manually, use the configuration below: ### Option A: Tailwind CSS v4 (`app/assets/tailwind/application.css`) ```css @import "tailwindcss"; @import "odt_ui/tokens.css"; @import "odt_ui/styles.css"; @import "odt_ui/theme.css"; ``` ### Option B: Propshaft / Sprockets / Standard Rails (`app/assets/stylesheets/application.css`) ```css @import "odt_ui/tokens.css"; @import "odt_ui/styles.css"; ``` --- ## Component Reference All helpers are globally available in all ERB templates, layouts, and helpers. --- ### Feedback & Overlays #### Toasts & Toaster Provides viewport stacking containers, automated Flash message toasts on redirects, and Turbo Stream responses for async form actions. ```erb <%# In app/views/layouts/application.html.erb %> <%= odt_toaster position: :top_right do %> <%= odt_flash_toasts %> <% end %> <%= yield %> ``` **Controller Turbo Stream Action:** ```ruby def create @todo = Todo.new(todo_params) if @todo.save respond_to do |format| format.turbo_stream do render turbo_stream: [ turbo_stream.prepend("todos", @todo), odt_toast_stream("Todo created successfully!", variant: :success) ] end format.html { redirect_to todos_path, notice: "Todo created successfully!" } end end end ``` **Individual Toast Helper:** ```erb <%= odt_toast "Settings updated", title: "Saved", variant: :success, duration: 4000 %> <%= odt_toast "Failed to sync records", variant: :danger %> ``` | Parameter | Type | Default | Description | | :------------ | :-------- | :--------- | :--------------------------------------------------------------------------- | | `message` | `String` | `nil` | Toast description or body text | | `title` | `String` | `nil` | Optional bold header title | | `variant` | `Symbol` | `:success` | `:success`, `:warning`, `:danger`, `:error`, `:info`, `:loading`, `:frosted` | | `duration` | `Integer` | `4000` | Auto-dismiss duration in ms (`0` to disable) | | `dismissible` | `Boolean` | `true` | Renders close button (`×`) | --- #### Modals Accessible dialog containers with backdrop overlay, ESC key handling, scroll locking, and smooth scale transitions. ```erb <%# Trigger Button %> <%= odt_button "New Task", color: :primary, data: { action: "click->odt-modal#open" } %> <%# Standard Modal %> <%= odt_modal id: "new_task_modal", title: "Create Task", subtitle: "Add a task to your board", size: :md do %> <%= render "form", todo: Todo.new %> <% end %> <%# Custom Composition Modal %> <%= odt_modal id: "confirm_delete_modal", size: :sm do %> <%= odt_modal_header title: "Delete Item?", subtitle: "This action cannot be undone." %> <%= odt_modal_body do %> <p class="text-sm text-fg-muted">Are you sure you want to permanently remove this record?</p> <% end %> <%= odt_modal_footer do %> <%= odt_button "Cancel", variant: :ghost, data: { action: "click->odt-modal#close" } %> <%= odt_button "Delete", color: :danger, data: { turbo_method: :delete } %> <% end %> <% end %> ``` | Parameter | Type | Default | Description | | :----------------------- | :-------- | :------- | :------------------------------------------- | | `id` | `String` | `nil` | HTML identifier for Stimulus/Turbo targeting | | `title` / `subtitle` | `String` | `nil` | Header titles | | `size` | `Symbol` | `:md` | `:sm`, `:md`, `:lg`, `:xl`, `:full` | | `radius` | `Symbol` | `:"2xl"` | `:none` to `:"4xl"`, `:full` | | `dismissible` | `Boolean` | `true` | Renders close button | | `open` | `Boolean` | `false` | Initial visible state | | `prevent_backdrop_close` | `Boolean` | `false` | Disables closing on overlay click | --- #### Alerts Inline banners for status notices, warning messages, and form callouts. ```erb <%= odt_alert "Your subscription will renew on May 1st.", variant: :info %> <%= odt_alert "Payment failed. Please update billing info.", variant: :danger, icon: "fa-solid fa-triangle-exclamation" %> ``` --- ### Actions & Indicators #### Buttons Versatile action buttons supporting brand colors, frosted glass surfaces, loading states, and icon decorations. ```erb <%# Standard Brand Action %> <%= odt_button "Save Changes", color: :primary, variant: :filled, radius: :full %> <%# Secondary & Frosted Glass %> <%= odt_button "Secondary", variant: :subtle, color: :secondary %> <%= odt_button "Frosted Glass", variant: :frosted, color: :primary %> <%# Link as Button %> <%= odt_button "View Project", href: project_path(@project), variant: :subtle %> <%# Loading & Icons %> <%= odt_button "Add Task", icon: "fa-solid fa-plus", color: :primary %> <%= odt_button "Saving...", loading: true %> <%= odt_button icon: "fa-solid fa-trash", icon_only: true, color: :danger, variant: :ghost %> ``` | Option | Type | Default | Description | | :-------------------- | :-------- | :--------- | :----------------------------------------------------------------------------------------------------------------- | | `variant` | `Symbol` | `:filled` | `:filled`, `:subtle`, `:ghost`, `:outline`, `:capsule`, `:frosted`, `:light` | | `color` | `Symbol` | `:primary` | `:primary`, `:secondary`, `:surface`, `:muted`, `:inverse`, `:success`, `:warning`, `:danger`, `:info`, `:neutral` | | `size` | `Symbol` | `:md` | `:xs`, `:sm`, `:md`, `:lg`, `:xl` | | `radius` | `Symbol` | `:"2xl"` | `:none`, `:xs`, `:sm`, `:md`, `:lg`, `:xl`, `:"2xl"`, `:"3xl"`, `:"4xl"`, `:full` | | `icon` / `right_icon` | `String` | `nil` | FontAwesome class or inline SVG string | | `icon_only` | `Boolean` | `false` | Square icon button formatting | | `loading` | `Boolean` | `false` | Replaces icon with spinning loader | | `disabled` | `Boolean` | `false` | HTML disabled state | | `full_width` | `Boolean` | `false` | Expands width to 100% | --- #### Badges Compact status indicators with optional indicator dots and icons. ```erb <%= odt_badge "Active", variant: :subtle, color: :success, dot: true %> <%= odt_badge "High Priority", variant: :filled, color: :danger, radius: :full %> <%= odt_badge "Calendar", icon: "fa-regular fa-calendar", variant: :outlined %> ``` --- ### Form Controls Accessible form components complete with labels, helper text, error validations, icons, and input adornments. ```erb <%= form_with model: @user, class: "space-y-4" do |f| %> <%# Text Input with Left Icon %> <%= odt_input( label: "Email Address", name: "user[email]", value: @user.email, type: "email", placeholder: "you@example.com", left_icon: "fa-solid fa-envelope", error: @user.errors[:email].first, required: true ) %> <%# Textarea %> <%= odt_textarea( label: "Bio", name: "user[bio]", value: @user.bio, rows: 4, helper: "Brief summary for your profile" ) %> <%# Select Dropdown %> <%= odt_select( label: "Country", name: "user[country]", options: [["Thailand", "TH"], ["United States", "US"], ["Japan", "JP"]], selected: @user.country, prompt: "Select country" ) %> <%# Checkbox %> <%= odt_checkbox( label: "Subscribe to newsletter", description: "Receive product updates once a week", name: "user[subscribed]", checked: true, color: :primary ) %> <%# Radio Group %> <%= odt_radio label: "Monthly Billing", name: "plan", value: "monthly", checked: true %> <%= odt_radio label: "Annual Billing (Save 20%)", name: "plan", value: "annual" %> <%# Custom Slot Form Group %> <%= odt_form_group label: "Custom Input", helper: "Unique value required" do %> <% end %> <% end %> ``` --- ### Layout & Typography #### Cards Versatile containers supporting header metadata, multiple surface styles, and hover transitions. ```erb <%= odt_card title: "Analytics Overview", subtitle: "Monthly performance", variant: :elevated, radius: :"3xl" do %> <p class="text-sm text-fg">Dashboard charts and statistics...</p> <% end %> <%= odt_card variant: :frosted, radius: :"2xl", hoverable: true do %> <span>Interactive frosted glass card</span> <% end %> ``` #### Typography (`odt_heading` & `odt_text`) Standardized typographic elements mapped to the design system scale. ```erb <%= odt_heading "Task Dashboard", as: :h1, size: :"4xl", weight: :bold, color: :strong %> <%= odt_heading "Section Title", as: :h3, size: :xl %> <%= odt_text "Manage all project deliverables in one workspace.", size: :sm, color: :muted %> <%= odt_text "A very long single-line description...", truncate: true %> ``` #### Avatars Renders image avatars or automatically computes two-letter initials from full names. ```erb <%# Generates initials avatar "JD" %> <%= odt_avatar name: "John Doe", size: :lg, color: :secondary %> <%# Image avatar with fallback %> <%= odt_avatar src: "https://avatar.vercel.sh/pixel", size: :md %> ``` --- ## Theming & Dark Mode The design system is fully dark mode ready. Apply `data-theme="dark"` or the `.dark` class to `` or any sub-container: ```html ... ``` All design tokens (backgrounds, surfaces, borders, text, and shadows) adapt automatically without custom CSS. --- ## License The gem is available as open source under the terms of the [MIT License](LICENSE).
Required Ruby Version
>= 3.0.0
Authors
Pixel
Versions
- 2.2.10 September 07, 2026 (47 KB)
- 2.2.9 September 07, 2026 (47 KB)
- 2.2.8 September 07, 2026 (46 KB)
- 2.2.7 September 04, 2026 (46 KB)
- 2.2.6 September 04, 2026 (46 KB)
- 2.2.1 September 02, 2026 (33.5 KB)