Esc

Components

All components Every component, with search. Accordion Sections that expand and collapse, one at a time or several. Alert A highlighted message, optionally dismissible. Avatar A person's picture, with initials when there is none. Avatar group Overlapping avatars with a "+N" count for the rest. Badge A small status label. Banner A dismissible strip across the top of a page. Breadcrumb The path to the current page, collapsing long trails. Button Buttons in every variant and size, with icons and a loading state. Button to A button that submits a one-off form, like Rails' button_to. Card A bordered container with header, content and footer. Checkbox A checkbox with label, standalone or bound to a form builder. Collapsible A section that opens and closes under its trigger, like a branch of a nav tree. Confirm dialog A styled dialog that answers data-turbo-confirm, in place of the browser's confirm box. Data grid A sortable, filterable, server-paged table on AG Grid. Date picker A typed date field with a calendar, on Cally. Dialog A modal dialog on the native <dialog> element. Divider A horizontal or vertical rule, optionally labelled. Empty state What a page shows when it has nothing to list yet, with a way to add the first thing. File field A drag-and-drop file picker with a label, hint and error message, standalone or with a form builder. Form field A label, hint and error message wired to any form control. Icon An SVG icon from your chosen icon library, drawn inline on the server. Image An image with consistent sizing and corner radius. Input A text field with label, hint and error message, standalone or with a form builder. Input group A text field with addons before or after it. Label A form label with a required marker. Link A link styled as text or as a button. Menu A dropdown menu with keyboard navigation. Navigation menu Site navigation with links and hover or click panels of rich links. Pagination Page links for a long list, from a Pagy object. Password input A password field with a show/hide toggle. Popover Floating content anchored to a trigger. Radio group A set of radio buttons. Native select The browser's own select, styled closed; best for phones' system pickers. Select A styled dropdown with keyboard navigation and type-ahead, posting like a native select. Session tabs Browser-style tabs the user opens, renames, duplicates, reorders and closes. Sheet A panel that slides in from an edge of the screen. Sidebar A collapsible application sidebar with groups and menus. Skeleton A pulsing placeholder shown in a piece of content's shape while it loads. Spinner A loading indicator. Stepper The steps of a multi-step form, each one done, current or still to come. Switch An on/off toggle. Table A styled table with header, rows and caption. Table of contents An "On this page" list that follows the reader's scroll. Tabs Tabbed panels with keyboard navigation. Text Body text in set sizes, weights and tones. Textarea A multi-line text field with label, hint and error, that can grow with its text. Theme toggle A light/dark/system theme switcher. Toast A stacked, auto-dismissing message, pushed from the client or a Turbo Stream. Toggle group A row of buttons where one or several are chosen, posting like radios or checkboxes. Tooltip A hint shown on hover and focus. Typography Headings, lead, muted, code and blockquote text styles.

Icons

Components draw their icons from one library you choose: Lucide, Tabler or Phosphor. Each icon is an SVG file in your app, drawn into the page on the server, with no JavaScript.

Choose a library

Pick it when you install. Lucide is the default.

bin/rails generate velvetkit:install --icons=phosphor

The choice is saved in config/velvetkit.yml, and every component you add after that uses it:

config/velvetkit.yml
namespace: UI
icons: phosphor
  • search
  • x
  • check
  • chevron-down
  • circle-alert
  • info
  • eye-off
  • grip-vertical
  • loader
  • settings
  • mail
  • calendar
app/components/ui/alert.rb
class UI::Alert < UI::Base
  DEFAULT_ICONS = {
    default:     "info",
    destructive: "circle-alert",
    warning:     "circle-alert",
    success:     "circle-check",
    info:        "info"
  }.freeze

Browse Lucide · lucide-static@0.545.0 · ISC licence

How it works

The components are written with Lucide names. When velvetkit:add copies one into an app that uses another library, it renames every icon in the component, its tests and its previews, so the Alert above asks for warning-circle under Phosphor. It also copies the SVG of each icon the component draws into app/assets/icons.

After that, nothing looks anything up. UI::Icon.new(name: "warning-circle") reads app/assets/icons/warning-circle.svg and puts the SVG into the page, sized by the component and coloured with the text around it.

Add your own icons

Use any icon from your library by its name in that library. Add its SVG first:

bin/rails generate velvetkit:icons rocket user

Run it with no names to add every icon your own components, views and helpers draw that has no SVG yet. Icons the kit uses come from the gem; others are downloaded from the npm CDN, at the version the kit was built with.

app/views/home/index.html.erb
<%= render UI::Icon.new(name: "rocket") %>
<%= render UI::Icon.new(name: "user", size: :sm, label: "Account") %>

An icon with no SVG draws a dashed square, and the log names the missing file. In production it draws nothing. Any SVG can go in app/assets/icons, from another library or your designer, as long as it has a viewBox and uses currentColor.

Switch libraries later

Change icons: in config/velvetkit.yml, then add your components again so they're renamed and get the new SVGs. This overwrites them, so commit first and review the diff for your own changes.

bin/rails generate velvetkit:add dialog menu alert --force

Icons in your own views keep the names you gave them. Rename those to the new library's names, then run velvetkit:icons to fetch their SVGs.

Upgrading from 0.2

Before 0.3, icons were drawn in the browser by the lucide package. Components you added then keep working that way until you add them again.

  1. Add your components again with --force, including icon. They now draw SVGs, and copy the ones they need.
  2. Run bin/rails generate velvetkit:icons with no names, to add an SVG for every icon your own views draw.
  3. Delete app/javascript/velvetkit_icons.ts and its import, and remove lucide from package.json.