Badge
Displays a badge or a component that looks like a badge.
Usage
Default
Badge
Badge { "Badge" }
Secondary
Secondary
Badge(variant: :secondary) { "Secondary" }
Destructive
Destructive
Badge(variant: :destructive) { "Destructive" }
Outline
Outline
Badge(variant: :outline) { "Outline" }
Ghost
Ghost
Badge(variant: :ghost) { "Ghost" }
With icon
Mark the icon with data-icon="inline-start" or "inline-end" so the badge tightens the padding on that side. Icons are sized to 12px.
VerifiedBookmark
div(class: "flex flex-wrap gap-2 justify-center") do
Badge(variant: :secondary) do
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-start"}) do |s|
s.path(d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z")
s.path(d: "m9 12 2 2 4-4")
end
plain "Verified"
end
Badge(variant: :outline) do
plain "Bookmark"
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-end"}) do |s|
s.path(d: "m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z")
end
end
end
As link
Pass href: to render an <a>. Links get the variant's hover color and a focus ring.
Badge(href: "#") do
plain "Open Link"
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-end"}) do |s|
s.path(d: "M7 7h10v10")
s.path(d: "M7 17 17 7")
end
end
Custom colors
Badge ships the shadcn variants. For any other color, pass class: with a light and a dark shade of the same scale, so the text stays readable in both themes.
BlueGreenSkyPurpleRed
div(class: "flex flex-wrap gap-2 justify-center") do
Badge(class: "bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300") { "Blue" }
Badge(class: "bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300") { "Green" }
Badge(class: "bg-sky-50 text-sky-700 dark:bg-sky-950 dark:text-sky-300") { "Sky" }
Badge(class: "bg-purple-50 text-purple-700 dark:bg-purple-950 dark:text-purple-300") { "Purple" }
Badge(class: "bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-300") { "Red" }
end
Sizes
SmallMediumLarge
div(class: "flex flex-wrap gap-2 justify-center items-center") do
Badge(size: :sm) { "Small" }
Badge(size: :md) { "Medium" }
Badge(size: :lg) { "Large" }
end
Installation
Using RubyUI CLI
Run the install command
rails g ruby_ui:component Badge
Manual installation
1
Add RubyUI::Badge to app/components/ruby_ui/badge/badge.rb
# frozen_string_literal: true
module RubyUI
# A small label. Renders a <span>, or an <a> when given href:, which turns on
# the variants' hover styles and the focus ring, like shadcn's
# <Badge render={<a />}>. An icon marked data-icon="inline-start" or
# "inline-end" gets tighter padding on its side.
class Badge < Base
BASE = [
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap",
"rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all",
"has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
"[&>svg]:pointer-events-none [&>svg]:size-3!",
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40"
].freeze
# :md is shadcn's only size; :sm and :lg are RubyUI extras.
SIZES = {
sm: "h-4 px-1.5",
md: "",
lg: "h-6 px-2.5 text-sm"
}.freeze
# Variants mirror shadcn; pass class: for any other color.
VARIANTS = {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
destructive: "bg-destructive/10 text-destructive dark:bg-destructive/20 [a]:hover:bg-destructive/20 " \
"focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
link: "text-primary underline-offset-4 hover:underline"
}.freeze
# Older name of :default, kept so variant: :primary keeps working.
ALIASES = {primary: :default}.freeze
def initialize(variant: :default, size: :md, href: nil, **attrs)
@variant = variant.to_s.to_sym
@variant = ALIASES.fetch(@variant, @variant)
@variant = :default unless VARIANTS.key?(@variant) # nil or unknown: default look
@size = SIZES.key?(size.to_s.to_sym) ? size.to_s.to_sym : :md
@href = href
super(**attrs)
end
def view_template(&)
if @href
a(href: @href, **attrs, &)
else
span(**attrs, &)
end
end
private
def default_attrs
{
data: {slot: "badge", variant: @variant},
class: [BASE, SIZES.fetch(@size), VARIANTS.fetch(@variant)]
}
end
end
end
2
Add RubyUI::BadgeDocs to app/components/ruby_ui/badge/badge_docs.rb
# frozen_string_literal: true
class Views::Docs::Badge < Views::Base
def view_template
component = "Badge"
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Badge", description: "Displays a badge or a component that looks like a badge.")
Heading(level: 2) { "Usage" }
render Docs::VisualCodeExample.new(title: "Default", context: self) do
<<~RUBY
Badge { "Badge" }
RUBY
end
render Docs::VisualCodeExample.new(title: "Secondary", context: self) do
<<~RUBY
Badge(variant: :secondary) { "Secondary" }
RUBY
end
render Docs::VisualCodeExample.new(title: "Destructive", context: self) do
<<~RUBY
Badge(variant: :destructive) { "Destructive" }
RUBY
end
render Docs::VisualCodeExample.new(title: "Outline", context: self) do
<<~RUBY
Badge(variant: :outline) { "Outline" }
RUBY
end
render Docs::VisualCodeExample.new(title: "Ghost", context: self) do
<<~RUBY
Badge(variant: :ghost) { "Ghost" }
RUBY
end
render Docs::VisualCodeExample.new(title: "Link", context: self) do
<<~RUBY
Badge(variant: :link, href: "#") { "Link" }
RUBY
end
render Docs::VisualCodeExample.new(title: "With Icon", description: "Mark the icon with data-icon=\"inline-start\" or \"inline-end\" so the badge tightens the padding on that side. Icons are sized to 12px.", context: self) do
<<~RUBY
div(class: "flex flex-wrap gap-2 justify-center") do
Badge(variant: :secondary) do
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-start"}) do |s|
s.path(d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z")
s.path(d: "m9 12 2 2 4-4")
end
plain "Verified"
end
Badge(variant: :outline) do
plain "Bookmark"
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-end"}) do |s|
s.path(d: "m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z")
end
end
end
RUBY
end
render Docs::VisualCodeExample.new(title: "As Link", description: "Pass href: to render an <a>. Links get the variant's hover color and a focus ring.", context: self) do
<<~RUBY
Badge(href: "#") do
plain "Open Link"
svg(xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewbox: "0 0 24 24", fill: "none",
stroke: "currentColor", stroke_width: "2", stroke_linecap: "round", stroke_linejoin: "round",
aria_hidden: "true", data: {icon: "inline-end"}) do |s|
s.path(d: "M7 7h10v10")
s.path(d: "M7 17 17 7")
end
end
RUBY
end
render Docs::VisualCodeExample.new(title: "Custom Colors", description: "Badge ships the shadcn variants. For any other color, pass class: with a light and a dark shade of the same scale, so the text stays readable in both themes.", context: self) do
<<~RUBY
div(class: "flex flex-wrap gap-2 justify-center") do
Badge(class: "bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300") { "Blue" }
Badge(class: "bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-300") { "Green" }
Badge(class: "bg-sky-50 text-sky-700 dark:bg-sky-950 dark:text-sky-300") { "Sky" }
Badge(class: "bg-purple-50 text-purple-700 dark:bg-purple-950 dark:text-purple-300") { "Purple" }
Badge(class: "bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-300") { "Red" }
end
RUBY
end
render Docs::VisualCodeExample.new(title: "Sizes", context: self) do
<<~RUBY
div(class: "flex flex-wrap gap-2 justify-center items-center") do
Badge(size: :sm) { "Small" }
Badge(size: :md) { "Medium" }
Badge(size: :lg) { "Large" }
end
RUBY
end
render Components::ComponentSetup::Tabs.new(component_name: component)
# components
render Docs::ComponentsTable.new(component_files(component))
end
end
end
Components
| Component | Built using | Source |
|---|---|---|
Badge | Phlex | |
BadgeDocs | Phlex |