# VelvetKit

## Theme toggle (`UI::ThemeToggle`)

A light/dark/system theme switcher.

Add it: `bin/rails generate velvetkit:add theme_toggle`

Also adds: `icon`, `spinner`, `button`, `menu`

Stimulus: `ui--theme`

### Arguments

```ruby
UI::ThemeToggle.new(label: nil, class: nil, **html_attrs)
```

### Notes

UI::ThemeToggle — System / Light / Dark picker.

The preference lives in localStorage under `ui-theme` and is applied by the
`ui--theme` Stimulus controller, which sets `.dark` on <html>. The initial
class is set pre-paint by layouts/_theme_script to avoid a flash.

Usage:
  <%= render(UI::ThemeToggle.new) %>

### Example: In A Header

```erb
<%# Mirrors the real app header: sidebar trigger left, theme toggle right. %>
<div class="rounded-lg border overflow-hidden">
  <header class="flex border-b h-14 shrink-0 items-center gap-2">
    <div class="flex items-center gap-2 px-2">
      <%= render(UI::Sidebar::Trigger.new(sidebar_id: "preview-sidebar")) %>
    </div>
    <div class="ml-auto flex items-center gap-2 px-2">
      <%= render(UI::ThemeToggle.new) %>
    </div>
  </header>
  <div class="p-6">
    <%= render(UI::Text.new) { "Switch the theme with the control above — the whole page follows." } %>
  </div>
</div>
```

### Example: Palette

```erb
<%# Every semantic token side by side. The fastest way to review both themes:
    flip the toggle and check nothing goes muddy or illegible. %>
<% pairs = [
     ["background", "foreground"], ["card", "card-foreground"],
     ["popover", "popover-foreground"], ["primary", "primary-foreground"],
     ["secondary", "secondary-foreground"], ["muted", "muted-foreground"],
     ["accent", "accent-foreground"], ["sidebar", "sidebar-foreground"],
     ["sidebar-accent", "sidebar-accent-foreground"]
   ] %>
<div class="flex flex-col gap-6">
  <div class="flex items-center gap-3">
    <%= render(UI::ThemeToggle.new) %>
    <%= render(UI::Typography::Muted.new) { "Flip the theme, then scan for anything unreadable." } %>
  </div>

  <div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
    <% pairs.each do |bg, fg| %>
      <div class="rounded-md border p-4" style="background: var(--<%= bg %>); color: var(--<%= fg %>);">
        <div class="text-sm font-medium">--<%= bg %></div>
        <div class="text-xs opacity-80">--<%= fg %></div>
      </div>
    <% end %>
  </div>

  <div class="flex flex-wrap items-center gap-3">
    <%= render(UI::Button.new) { "Primary" } %>
    <%= render(UI::Button.new(variant: :secondary)) { "Secondary" } %>
    <%= render(UI::Button.new(variant: :outline)) { "Outline" } %>
    <%= render(UI::Button.new(variant: :ghost)) { "Ghost" } %>
    <%= render(UI::Button.new(variant: :destructive)) { "Destructive" } %>
  </div>

  <div class="flex flex-wrap items-center gap-3">
    <% %i[default secondary destructive outline success warning].each do |v| %>
      <%= render(UI::Badge.new(variant: v)) { v.to_s.capitalize } %>
    <% end %>
  </div>

  <div class="flex flex-col gap-3 max-w-md">
    <% %i[default destructive warning success info].each do |v| %>
      <%= render(UI::Alert.new(variant: v)) do |alert| %>
        <% alert.with_title { v.to_s.capitalize } %>
        <% alert.with_description { "How the #{v} alert reads in this theme." } %>
      <% end %>
    <% end %>
  </div>
</div>
```
