Components
A container that organizes stuff neatly. Cards are great for showing off different items or sections, keeping things tidy and easy to understand for users.
Send a message to your teammates.
<div class="rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70 md:w-[350px]">
<h3 class="text-xl font-extrabold text-slate-900 dark:text-slate-100 tracking-tight">Create a message</h3>
<p class="prose prose-slate dark:prose-invert mt-2 mb-4">Send a message to your teammates.</p>
<button class="btn btn-primary">Start writing</button>
</div>
.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border(class="border-slate-300/70 dark:border-slate-600/70 md:w-[350px]")
%h3.text-xl.font-extrabold.text-slate-900.dark:text-slate-100.tracking-tight Create a message
%p.prose.prose-slate.dark:prose-invert.mt-2.mb-4 Send a message to your teammates.
%button.btn.btn-primary Start writing
Lorem ipsum dolor sit amet, consectetur adipisicin.
Engineering
Remote
Design
Remote
Design
Remote
<div class="rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70">
<div class="-mx-6 border-b border-slate-200 dark:border-slate-700 px-6 pb-4 mb-4">
<h3 class="text-2xl font-extrabold text-slate-900 dark:text-slate-100 tracking-tight">Job postings</h3>
<p class="prose prose-slate dark:prose-invert mt-2 mb-4">Lorem ipsum dolor sit amet, consectetur adipisicin.</p>
</div>
<ul class="divide-y divide-slate-300/70 dark:divide-slate-600/70 last-child:pb-0 pointer-events-none select-none">
<li class="flex justify-between items-start py-3 -mx-6 px-6">
<div>
<%= link_to "Ruby developer", "#", class: "btn-link" %>
<div class="flex items-center gap-3 text-slate-500 text-sm py-1 dark:text-slate-400">
<div class="flex items-center gap-2">
<%= icon "users", class: "size-4 text-slate-400" %>
<p>Engineering</p>
</div>
<div class="flex items-center gap-2">
<%= icon "map", class: "size-4 text-slate-400" %>
<p>Remote</p>
</div>
</div>
</div>
<div>
<span class="rounded bg-green-100 text-green-800 font-medium px-2 py-1 text-xs dark:text-green-300 dark:bg-green-500/20">Full-time</span>
</div>
</li>
<li class="flex justify-between items-start py-3 -mx-6 px-6">
<div>
<%= link_to "Front-end developer", "#", class: "btn-link" %>
<div class="flex items-center gap-3 text-slate-500 text-sm py-1 dark:text-slate-400">
<div class="flex items-center gap-2">
<%= icon "users", class: "size-4 text-slate-400" %>
<p>Design</p>
</div>
<div class="flex items-center gap-2">
<%= icon "map", class: "size-4 text-slate-400" %>
<p>Remote</p>
</div>
</div>
</div>
<div>
<span class="rounded bg-green-100 text-green-800 font-medium px-2 py-1 text-xs dark:text-green-300 dark:bg-green-500/20">Full-time</span>
</div>
</li>
<li class="flex justify-between items-start py-3 -mx-6 px-6">
<div>
<%= link_to "UX Designer", "#", class: "btn-link" %>
<div class="flex items-center gap-3 text-slate-500 text-sm py-1 dark:text-slate-400">
<div class="flex items-center gap-2">
<%= icon "users", class: "size-4 text-slate-400" %>
<p>Design</p>
</div>
<div class="flex items-center gap-2">
<%= icon "map", class: "size-4 text-slate-400" %>
<p>Remote</p>
</div>
</div>
</div>
<div>
<span class="rounded bg-green-100 text-green-800 font-medium px-2 py-1 text-xs dark:text-green-300 dark:bg-green-500/20">Full-time</span>
</div>
</li>
</ul>
</div>
.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border(class="border-slate-300/70 dark:border-slate-600/70")
.-mx-6.border-b.border-slate-200.dark:border-slate-700.px-6.pb-4.mb-4
%h3.text-2xl.font-extrabold.text-slate-900.dark:text-slate-100.tracking-tight Job postings
%p.prose.prose-slate.dark:prose-invert.mt-2.mb-4 Lorem ipsum dolor sit amet, consectetur adipisicin.
%ul.divide-y.last-child:pb-0.pointer-events-none.select-none(class="divide-slate-300/70 dark:divide-slate-600/70")
%li.flex.justify-between.items-start.py-3.-mx-6.px-6
%div
= link_to "Ruby developer", "#", class: "btn-link"
.flex.items-center.gap-3.text-slate-500.text-sm.py-1.dark:text-slate-400
.flex.items-center.gap-2
= icon "users", class: "size-4 text-slate-400"
%p Engineering
.flex.items-center.gap-2
= icon "map", class: "size-4 text-slate-400"
%p Remote
%div
%span.rounded.bg-green-100.text-green-800.font-medium.px-2.py-1.text-xs.dark:text-green-300(class="dark:bg-green-500/20") Full-time
%li.flex.justify-between.items-start.py-3.-mx-6.px-6
%div
= link_to "Front-end developer", "#", class: "btn-link"
.flex.items-center.gap-3.text-slate-500.text-sm.py-1.dark:text-slate-400
.flex.items-center.gap-2
= icon "users", class: "size-4 text-slate-400"
%p Design
.flex.items-center.gap-2
= icon "map", class: "size-4 text-slate-400"
%p Remote
%div
%span.rounded.bg-green-100.text-green-800.font-medium.px-2.py-1.text-xs.dark:text-green-300(class="dark:bg-green-500/20") Full-time
%li.flex.justify-between.items-start.py-3.-mx-6.px-6
%div
= link_to "UX Designer", "#", class: "btn-link"
.flex.items-center.gap-3.text-slate-500.text-sm.py-1.dark:text-slate-400
.flex.items-center.gap-2
= icon "users", class: "size-4 text-slate-400"
%p Design
.flex.items-center.gap-2
= icon "map", class: "size-4 text-slate-400"
%p Remote
%div
%span.rounded.bg-green-100.text-green-800.font-medium.px-2.py-1.text-xs.dark:text-green-300(class="dark:bg-green-500/20") Full-time
Lorem ipsum dolor sit amet consectetur...
Lorem ipsum dolor sit amet consectetur...
<div class="rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70">
<div class="-mx-6 border-b border-slate-200 dark:border-slate-700 px-6 pb-4 mb-4 flex items-center justify-between flex-wrap">
<h1 class="text-2xl font-extrabold text-slate-900 dark:text-slate-100 tracking-tight">Posts</h1>
<%= link_to "New post", "#", class: "btn btn-primary" %>
</div>
<ul class="divide-slate-300/70 dark:divide-slate-600/70 last-child:pb-0 pointer-events-none select-none">
<li class="py-3 -mx-6 px-6">
<%= link_to "Post title 1", "#", class: "btn-link" %>
<p class="text-slate-700 dark:text-slate-400">Lorem ipsum dolor sit amet consectetur...</p>
</li>
<li class="pt-3 -mx-6 px-6">
<%= link_to "Post title 2", "#", class: "btn-link" %>
<p class="text-slate-700 dark:text-slate-400">Lorem ipsum dolor sit amet consectetur...</p>
</li>
</ul>
</div>
.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border(class="border-slate-300/70 dark:border-slate-600/70")
.-mx-6.border-b.border-slate-200.dark:border-slate-700.px-6.pb-4.mb-4.flex.items-center.justify-between.flex-wrap
%h1.text-2xl.font-extrabold.text-slate-900.dark:text-slate-100.tracking-tight Posts
= link_to "New post", "#", class: "btn btn-primary"
%ul.last-child:pb-0.pointer-events-none.select-none(class="divide-slate-300/70 dark:divide-slate-600/70")
%li.py-3.-mx-6.px-6
= link_to "Post title 1", "#", class: "btn-link"
%p.text-slate-700.dark:text-slate-400 Lorem ipsum dolor sit amet consectetur...
%li.pt-3.-mx-6.px-6
= link_to "Post title 2", "#", class: "btn-link"
%p.text-slate-700.dark:text-slate-400 Lorem ipsum dolor sit amet consectetur...
John Doe
john.doe@example.com
+1-555-555-5555
<div class="rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70 py-4 px-6 flex items-center space-x-4 w-auto dark:shadow-slate-900/50">
<%= image_tag demo_avatar_url(id: rand(1..10).to_s, variant: [:men, :women].sample), class: "object-cover size-14 rounded-full flex-shrink-0" %>
<div class="flex-1 flex flex-col items-start space-y-px text-slate-900">
<p class="font-bold dark:text-white mb-1">John Doe</p>
<div class="flex flex-wrap items-center md:gap-4 gap-0">
<div class="flex items-center gap-2 pb-0.5">
<%= icon "envelope", class: "size-4 text-slate-500 dark:text-primary-400" %>
<p class="text-sm dark:text-slate-300">john.doe@example.com</p>
</div>
<div class="flex items-center gap-2">
<%= icon "phone", class: "size-4 text-slate-500 dark:text-primary-400" %>
<p class="text-sm dark:text-slate-300">+1-555-555-5555</p>
</div>
</div>
</div>
</div>
.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border.py-4.px-6.flex.items-center.space-x-4.w-auto(class="border-slate-300/70 dark:border-slate-600/70 dark:shadow-slate-900/50")
= image_tag demo_avatar_url(id: rand(1..10).to_s, variant: [:men, :women].sample), class: "object-cover size-14 rounded-full flex-shrink-0"
.flex-1.flex.flex-col.items-start.space-y-px.text-slate-900
%p.font-bold.dark:text-white.mb-1 John Doe
.flex.flex-wrap.items-center.md:gap-4.gap-0
.flex.items-center.gap-2(class="pb-0.5")
= icon "envelope", class: "size-4 text-slate-500 dark:text-primary-400"
%p.text-sm.dark:text-slate-300 john.doe@example.com
.flex.items-center.gap-2
= icon "phone", class: "size-4 text-slate-500 dark:text-primary-400"
%p.text-sm.dark:text-slate-300 +1-555-555-5555
<div class="py-4 px-6 flex flex-wrap items-center w-auto dark:shadow-slate-900/50 rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70 gap-4">
<img class="object-cover size-14 rounded-full flex-shrink-0" src="https://randomuser.me/api/portraits/women/32.jpg" />
<div class="flex-1 flex flex-col items-start space-y-px text-slate-900">
<p class="font-bold dark:text-white">Jane Doe</p>
<p class="text-sm dark:text-slate-300">Software Architect at <a class="btn-link" href="#">Amazon</a></p>
</div>
<div class="grid gap-3 grid-cols-2 lg:pt-0 pt-3 w-full lg:w-auto">
<%= link_to "#", class: "btn btn-white flex items-center gap-2 group" do %>
<%= icon "phone", class: "size-4 text-slate-500 dark:text-slate-400 group-hover:text-primary-600 dark:group-hover:text-primary-400" %>
<span>Phone</span>
<% end %>
<!-- * The mail_to helper doesn't play too nicely with blocks so we have to rig it up a bit -->
<% mail_content = capture do %>
<%= icon "envelope", class: "size-4 text-slate-500 dark:text-slate-400 group-hover:text-primary-600 dark:group-hover:text-primary-400" %>
<span>Email</span>
<% end %>
<%= mail_to "railsui@example.com", mail_content, class: "btn btn-white flex items-center gap-2 group" %>
</div>
</div>
.py-4.px-6.flex.flex-wrap.items-center.w-auto.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border.gap-4(class="dark:shadow-slate-900/50 border-slate-300/70 dark:border-slate-600/70")
%img.object-cover.size-14.rounded-full.flex-shrink-0(src="https://randomuser.me/api/portraits/women/32.jpg")/
.flex-1.flex.flex-col.items-start.space-y-px.text-slate-900
%p.font-bold.dark:text-white Jane Doe
%p.text-sm.dark:text-slate-300
Software Architect at
%a.btn-link(href="#") Amazon
.grid.gap-3.grid-cols-2.lg:pt-0.pt-3.w-full.lg:w-auto
= link_to "#", class: "btn btn-white flex items-center gap-2 group" do
= icon "phone", class: "size-4 text-slate-500 dark:text-slate-400 group-hover:text-primary-600 dark:group-hover:text-primary-400"
%span Phone
/ * The mail_to helper doesn't play too nicely with blocks so we have to rig it up a bit
- mail_content = capture do
= icon "envelope", class: "size-4 text-slate-500 dark:text-slate-400 group-hover:text-primary-600 dark:group-hover:text-primary-400"
%span Email
= mail_to "railsui@example.com", mail_content, class: "btn btn-white flex items-center gap-2 group"
Just wanted to share my thoughts on the new product launch. The attention to detail and user experience is fantastic. The interface is intuitive and the performance improvements are noticeable. Great work by the entire team on this milestone achievement!
<div class="rounded-md bg-white p-6 dark:bg-slate-800 dark:text-white border border-slate-300/70 dark:border-slate-600/70 dark:shadow-slate-900/50">
<div class="flex flex-wrap items-center gap-4">
<img class="object-cover size-14 rounded-full flex-shrink-0" src="https://randomuser.me/api/portraits/men/49.jpg" />
<div class="flex-1 flex flex-col items-start space-y-px text-slate-900 dark:text-white">
<p class="font-bold">Jared Doe</p>
<p class="dark:text-slate-400 text-sm"><%= Time.zone.now.strftime("%B %e at %l:%M %p") %></p>
</div>
<div data-controller="dropdown" class="relative">
<button type="button" data-action="railsui-dropdown#toggle click@window->railsui-dropdown#hide" class="relative group border-transparent border transition ease-in-out focus:ring-4 w-8 h-8 flex items-center justify-center rounded-lg hover:border-slate-200 focus:ring-slate-50 hover:bg-white hover:shadow-sm focus:border-slate-200 dark:focus:border-slate-700 dark:hover:bg-slate-700/80 dark:hover:border-slate-700 dark:focus:ring-slate-400/50">
<%= icon "ellipsis-vertical", class: "size-6 text-slate-500 group-hover:text-primary-500 dark:text-slate-200 dark:group-hover:text-primary-300" %>
</button>
<ul class="hidden transition transform origin-to-right absolute right-0 top-9 bg-white rounded-lg shadow-xl shadow-slate-900/10 border border-slate-200 min-w-[200px] z-50 py-2 dark:bg-slate-700 dark:shadow-slate-900/50 dark:border-slate-500/60 text-sm font-medium text-slate-600 dark:text-slate-200"
data-railsui-dropdown-target="menu"
data-transition-enter-from="opacity-0 scale-95"
data-transition-enter-to="opacity-100 scale-100"
data-transition-leave-from="opacity-100 scale-100"
data-transition-leave-to="opacity-0 scale-95">
<li>
<%= link_to "Follow", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300" %>
</li>
<li>
<%= link_to "Save for later", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300" %>
</li>
<li>
<%= link_to "Mute", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300" %>
</li>
</ul>
</div>
<div class="prose dark:prose-invert mt-4">
<p>Just wanted to share my thoughts on the new product launch. The attention to detail and user experience is fantastic. The interface is intuitive and the performance improvements are noticeable. Great work by the entire team on this milestone achievement!</p>
</div>
</div>
</div>
.rounded-md.bg-white.p-6.dark:bg-slate-800.dark:text-white.border(class="border-slate-300/70 dark:border-slate-600/70 dark:shadow-slate-900/50")
.flex.flex-wrap.items-center.gap-4
%img.object-cover.size-14.rounded-full.flex-shrink-0(src="https://randomuser.me/api/portraits/men/49.jpg")/
.flex-1.flex.flex-col.items-start.space-y-px.text-slate-900.dark:text-white
%p.font-bold Jared Doe
%p.dark:text-slate-400.text-sm= Time.zone.now.strftime("%B %e at %l:%M %p")
.relative(data-controller="dropdown")
%button.relative.group.border-transparent.border.transition.ease-in-out.focus:ring-4.w-8.h-8.flex.items-center.justify-center.rounded-lg.hover:border-slate-200.focus:ring-slate-50.hover:bg-white.hover:shadow-sm.focus:border-slate-200.dark:focus:border-slate-700.dark:hover:border-slate-700(class="dark:hover:bg-slate-700/80 dark:focus:ring-slate-400/50" data-action="dropdown#toggle click@window->dropdown#hide" type="button")
= icon "ellipsis-vertical", class: "size-6 text-slate-500 group-hover:text-primary-500 dark:text-slate-200 dark:group-hover:text-primary-300"
%ul.hidden.transition.transform.origin-to-right.absolute.right-0.top-9.bg-white.rounded-lg.shadow-xl.border.border-slate-200.z-50.py-2.dark:bg-slate-700.text-sm.font-medium.text-slate-600.dark:text-slate-200(class="shadow-slate-900/10 min-w-[200px] dark:shadow-slate-900/50 dark:border-slate-500/60" data-dropdown-target="menu" data-transition-enter-from="opacity-0 scale-95" data-transition-enter-to="opacity-100 scale-100" data-transition-leave-from="opacity-100 scale-100" data-transition-leave-to="opacity-0 scale-95")
%li
= link_to "Follow", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300"
%li
= link_to "Save for later", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300"
%li
= link_to "Mute", "#", class: "px-4 py-[.4rem] hover:text-primary-600 block dark:hover:text-primary-300"
.prose.dark:prose-invert.mt-4
%p Just wanted to share my thoughts on the new product launch. The attention to detail and user experience is fantastic. The interface is intuitive and the performance improvements are noticeable. Great work by the entire team on this milestone achievement!
// app/javascript/controllers/dropdown_controller.js
import { Controller } from '@hotwired/stimulus'
import { useTransition } from 'stimulus-use'
export default class extends Controller {
static targets = ['menu', 'trigger']
connect() {
useTransition(this, {
element: this.menuTarget,
})
}
toggle() {
this.toggleTransition()
}
hide(event) {
if (
!this.element.contains(event.target) &&
!this.menuTarget.classList.contains('hidden')
) {
this.leave()
}
}
}