Stat Card
Stabledashboard KPI tiles
MRR
$24.8k
+12%
this month
Users
8,412
+4%
this week
Churn
1.8%
-0.3%
this month
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/.../stat_card_example.html.erb
<div class="grid w-full max-w-xl gap-3 sm:grid-cols-3">
<% [
["MRR", "$24.8k", "+12%", "this month", :success],
["Users", "8,412", "+4%", "this week", :default],
["Churn", "1.8%", "-0.3%", "this month", :warning]
].each do |label, value, change, helper_text, variant| %>
<%= render Senren::StatCardComponent.new(label: label, value: value, change: change, helper_text: helper_text, variant: variant) %>
<% 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 stat_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 stat_card --no-client
Dependencies are resolved by senren:add:
card, typography.
At a glance #
Category
Saas
Class name
Senren::StatCardComponent
Stimulus
—
Variants
default, success, warning, destructive
Depends on
card, typography
Pairs with
badge
Source #
app/components/senren/stat_card_component.html.erb
<%= tag.div(**root_attrs("rounded-(--senren-radius) border bg-[hsl(var(--senren-card))] p-5 shadow-sm")) do %>
<% if content? && label.blank? && value.blank? %>
<%= content %>
<% else %>
<% if label.present? %>
<p class="text-xs font-semibold uppercase tracking-wide text-[hsl(var(--senren-muted-foreground))]"><%= label %></p>
<% end %>
<% if value.present? %>
<p class="mt-2 font-display text-3xl font-semibold tracking-tight text-[hsl(var(--senren-foreground))]"><%= value %></p>
<% end %>
<div class="mt-3 flex items-center justify-between gap-3 text-sm">
<% if change.present? %>
<span class="font-medium <%= accent_class %>"><%= change %></span>
<% end %>
<% if helper_text.present? %>
<span class="text-[hsl(var(--senren-muted-foreground))]"><%= helper_text %></span>
<% end %>
</div>
<% end %>
<% end %>
app/components/senren/stat_card_component.rb
# frozen_string_literal: true
module Senren
class StatCardComponent < BaseComponent
VARIANTS = {
default: 'border-[hsl(var(--senren-border))]',
success: 'border-[hsl(var(--senren-success)/0.4)]',
warning: 'border-[hsl(var(--senren-warning)/0.5)]',
destructive: 'border-[hsl(var(--senren-destructive)/0.42)]'
}.freeze
SIZES = { md: '' }.freeze
def initialize(label: nil, value: nil, change: nil, helper_text: nil, variant: :default, class_name: nil, **html)
super(variant: variant, size: :md, class_name: class_name, **html)
@label = label
@value = value
@change = change
@helper_text = helper_text
end
attr_reader :label, :value, :change, :helper_text
def accent_class
{
success: 'text-[hsl(var(--senren-success))]',
warning: 'text-[hsl(var(--senren-warning))]',
destructive: 'text-[hsl(var(--senren-destructive))]'
}.fetch(variant, 'text-[hsl(var(--senren-muted-foreground))]')
end
end
end
AI agent rules #
Use for
- +dashboard KPI tiles
Avoid
- -interactive widgets - use card with explicit controls
Accessibility #
- Avoid color-only meaning; include label and value text.