Senren UI
Components / Card

Card

Stable

grouping related content

Pro plan

Everything in Hobby, plus advanced analytics.

$24 / month
  • · Unlimited projects
  • · Advanced analytics
  • · Priority support

Usage example #

Copy this ERB into a Rails view after installing the component. The snippet below is the same code used by the live preview above.

app/views/.../card_example.html.erb
<div class="w-full max-w-sm">
  <%= render Senren::CardComponent.new do |c| %>
    <% c.with_header do %>
      <%= render(Senren::TypographyComponent.new(variant: :h4)) { "Pro plan" } %>
      <%= render(Senren::TypographyComponent.new(variant: :muted)) { "Everything in Hobby, plus advanced analytics." } %>
    <% end %>
    <% c.with_body do %>
      <div class="flex items-baseline gap-1">
        <span class="font-display text-3xl font-semibold tracking-tight">$24</span>
        <span class="text-sm text-[hsl(var(--senren-muted-foreground))]">/ month</span>
      </div>
      <ul class="mt-3 space-y-1.5 text-sm text-[hsl(var(--senren-muted-foreground))]">
        <li>&middot; Unlimited projects</li>
        <li>&middot; Advanced analytics</li>
        <li>&middot; Priority support</li>
      </ul>
    <% end %>
    <% c.with_footer do %>
      <%= render(Senren::ButtonComponent.new(variant: :primary, class_name: "w-full")) { "Upgrade" } %>
    <% end %>
  <% end %>
</div>

Install this component #

Copy the official component into your app

Use this when you want the Senren-maintained implementation copied into app/components/senren.

Terminal
bin/rails senren:add card

Create a custom component with the same conventions

Use this when you need an app-specific static component that follows Senren's ViewComponent structure.

Terminal
bin/rails generate senren:component card --no-client

At a glance #

Category Layout
Class name Senren::CardComponent
Stimulus
Variants default, muted, outline
Depends on
Pairs with button, badge, separator

Source #

app/components/senren/card_component.html.erb
<%= tag.div(**root_attrs("rounded-(--senren-radius) border shadow-sm")) do %>
  <% if header? %>
    <div class="flex flex-col space-y-1.5 p-6 border-b border-[hsl(var(--senren-border))]"><%= header %></div>
  <% end %>
  <% if body? %>
    <div class="p-6"><%= body %></div>
  <% else %>
    <div class="p-6"><%= content %></div>
  <% end %>
  <% if footer? %>
    <div class="flex items-center p-6 pt-0 border-t border-[hsl(var(--senren-border))]"><%= footer %></div>
  <% end %>
<% end %>
app/components/senren/card_component.rb
# frozen_string_literal: true

module Senren
  class CardComponent < BaseComponent
    renders_one :header
    renders_one :body
    renders_one :footer

    VARIANTS = {
      default: 'bg-[hsl(var(--senren-card))] text-[hsl(var(--senren-card-foreground))] border-[hsl(var(--senren-border))]',
      muted: 'bg-[hsl(var(--senren-muted))] text-[hsl(var(--senren-muted-foreground))] border-transparent',
      outline: 'bg-transparent text-[hsl(var(--senren-foreground))] border-[hsl(var(--senren-border))]'
    }.freeze

    SIZES = { md: '' }.freeze
  end
end

AI agent rules #

Use for

  • +grouping related content
  • +dashboard widgets
  • +list items

Avoid

  • -nesting cards inside cards

Accessibility #

  • Use heading inside header slot; landmark roles only when meaningful.