Button
Stableprimary action
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/.../button_example.html.erb
<div class="flex flex-wrap items-center justify-center gap-2">
<%= render(Senren::ButtonComponent.new(variant: :primary)) { "Primary" } %>
<%= render(Senren::ButtonComponent.new(variant: :secondary)) { "Secondary" } %>
<%= render(Senren::ButtonComponent.new(variant: :destructive)) { "Destructive" } %>
<%= render(Senren::ButtonComponent.new(variant: :ghost)) { "Ghost" } %>
<%= render(Senren::ButtonComponent.new(variant: :link)) { "Link" } %>
</div>
<div class="mt-3 flex flex-wrap items-center justify-center gap-2">
<%= render(Senren::ButtonComponent.new(variant: :primary, size: :sm)) { "Small" } %>
<%= render(Senren::ButtonComponent.new(variant: :primary, size: :md)) { "Medium" } %>
<%= render(Senren::ButtonComponent.new(variant: :primary, size: :lg)) { "Large" } %>
</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 button
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 button --no-client
At a glance #
Category
Actions
Class name
Senren::ButtonComponent
Stimulus
—
Variants
default, primary, secondary, destructive, ghost, link
Depends on
—
Pairs with
form, dialog, dropdown_menu, alert_dialog
Source #
app/components/senren/button_component.html.erb
<% base = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-(--senren-radius) font-medium transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[hsl(var(--senren-ring))] disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed" %>
<% if as == :a %>
<%= tag.a content, **root_attrs(base, href: href) %>
<% else %>
<%= tag.button content, **root_attrs(base, type: type) %>
<% end %>
app/components/senren/button_component.rb
# frozen_string_literal: true
module Senren
class ButtonComponent < BaseComponent
VARIANTS = {
default: 'bg-[hsl(var(--senren-secondary))] text-[hsl(var(--senren-secondary-foreground))] hover:opacity-90',
primary: 'bg-[hsl(var(--senren-primary))] text-[hsl(var(--senren-primary-foreground))] hover:opacity-90',
secondary: 'bg-[hsl(var(--senren-secondary))] text-[hsl(var(--senren-secondary-foreground))] hover:opacity-90',
destructive: 'bg-[hsl(var(--senren-destructive))] text-[hsl(var(--senren-destructive-foreground))] hover:opacity-90',
ghost: 'bg-transparent text-[hsl(var(--senren-foreground))] hover:bg-[hsl(var(--senren-accent))]',
link: 'bg-transparent text-[hsl(var(--senren-primary))] underline-offset-4 hover:underline'
}.freeze
SIZES = {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base'
}.freeze
def initialize(variant: :default, size: :md, type: 'button', as: :button, href: nil, class_name: nil, **html)
super(variant: variant, size: size, class_name: class_name, **html)
@type = type
@as = href ? :a : as
@href = href
end
attr_reader :type, :as, :href
end
end
AI agent rules #
Use for
- +primary action
- +secondary action
- +form submit
- +destructive action
Avoid
- -using destructive variant for non-destructive actions
Accessibility #
- Icon-only buttons must include aria-label.
- Use button for actions and anchor for navigation.