KISS Component System
Auflistung der fertiggestellten Basis-Komponenten des Contao-KISS-Frameworks.
Components
1. Colors
Light
Brand
Base
Semantic
Brand
Base
Semantic
2. Typography
Type System
- Font: FFMarkPro (
--font-sans) — Regular 400, Bold 700, plus a Bold Italic used for display headings - h1 / h2: italic + uppercase display (~104px / ~50px). h3–h6: regular case
- Helpers:
.h1–.h6apply heading sizes to any element;.toplinefor eyebrow text - Hero: wrap an h1 in
.h1-xxlfor the oversized hero headline (up to 190px) - Scale tokens:
--text-base…--text-5xland--text-8xl(see Type Scale)
Font Family
--font-sans: "FFMarkPro", system-ui, sans-serif
The quick brown fox jumps over the lazy dog
The quick brown fox
ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 & ? ! @ # %
Heading Hierarchy
Rendered with the .h1–.h6 helpers. Sizes are responsive (mobile → md breakpoint).
Headline
Headline
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
Eyebrow / Topline
Type Scale
The explicitly defined --text-* tokens for this brand.
| Class | Size | Sample |
|---|---|---|
text-base | 1.125rem · 18px | Sample text |
text-lg | 1.5rem · 24px | Sample text |
text-xl | 1.75rem · 28px | Sample text |
text-2xl | 2.125rem · 34px | Sample text |
text-3xl | 2.25rem · 36px | Sample text |
text-4xl | 2.5rem · 40px | Sample text |
text-5xl | 3.125rem · 50px | Sample text |
text-8xl | 6.5rem · 104px | Sample text |
Weights & Styles
FFMarkPro ships Regular (400), Bold (700) and a Bold Italic (display). Other weights are synthesised by the browser — avoid relying on them.
Body & Rich Text
How running copy renders inside .rte (the rich-text wrapper).
Topline
Body copy uses text-base (1.125rem). Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt. This is a link, some bold text and some italic text for contrast.
A blockquote is set larger (text-xl → md:text-3xl) to stand out from the surrounding copy.
- First list item
- Second list item
3. Button Component
Component Structure
.btn + [style] + [color] + [size] + [shape] + [shadow] + [state]
- Styles: (default/solid), btn-soft, btn-outline, btn-text
- Colors: btn-primary, btn-secondary, btn-accent, btn-info, btn-success, btn-warning, btn-error
- Sizes: btn-xs, btn-sm, (default), btn-lg, btn-xl
- Shapes: (default/rounded), btn-circle, btn-square, btn-wide, btn-block
- Shadows: btn-shadow-none, btn-shadow-sm, btn-shadow-md, btn-shadow-lg
- States: btn-active, disabled
Example: <button class="btn btn-primary btn-lg btn-shadow-md">Save</button>
Style & Color Variants
Styles modify how colors are displayed. Combine: .btn + .btn-[style] + .btn-[color]
| Style | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Solid(default) |
||||||||
Soft.btn-soft |
||||||||
Outline.btn-outline |
||||||||
Text.btn-text |
Size Variants
.btn-xs0.75rem
.btn-sm0.875rem
.btn1rem
.btn-lg1.125rem
.btn-xl1.25rem
Shape Variants
.btn-circle.btn-square.btn-wideCircle and square buttons scale with size variants (always 1:1 ratio):
.btn-blockShadow Variants
Shadows grow on hover, shrink on active.
.btn-shadow-none.btn-shadow-sm.btn-shadow-md.btn-shadow-lgButton States
.btn-activedisabled4. Badge Component
Component Structure
.badge + [style] + [color] + [size] + [shape]
- Styles: (default/solid), badge-soft, badge-outline, badge-dashed
- Colors: badge-primary, badge-secondary, badge-accent, badge-info, badge-success, badge-warning, badge-error
- Sizes: badge-xs, badge-sm, (default), badge-lg, badge-xl
- Shapes: (default/rounded), badge-pill, badge-square
Example: <span class="badge badge-success badge-soft badge-lg">Active</span>
Style & Color Variants
Styles modify how colors are displayed. Combine: .badge + .badge-[style] + .badge-[color]
| Style | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Solid(default) |
Solid | Solid | Solid | Solid | Solid | Solid | Solid | Solid |
Soft.badge-soft |
Soft | Soft | Soft | Soft | Soft | Soft | Soft | Soft |
Outline.badge-outline |
Outline | Outline | Outline | Outline | Outline | Outline | Outline | Outline |
Dashed.badge-dashed |
Dashed | Dashed | Dashed | Dashed | Dashed | Dashed | Dashed | Dashed |
Size Variants
.badge-xs0.625rem
.badge-sm0.75rem
.badge0.875rem
.badge-lg1rem
.badge-xl1.125rem
Shape Variants
.badge-pill.badge-square5. Link Component
Component Structure
.link + [color] + [size] + [underline-style] + [special]
- Colors: link-primary, link-secondary, link-accent, link-info, link-success, link-warning, link-error
- Sizes: link-xs, link-sm, (default), link-lg, link-xl
- Underline: (default/always), link-underline-hover, link-underline-animate, link-underline-none
- Special: link-external, link-disabled
Example: <a class="link link-primary link-underline-animate">Hover me</a>
Style & Color Variants
Links appear at 80% opacity by default, full intensity on hover. Combine: .link + .link-[style] + .link-[color]
| Style | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Default(default) |
Default | Default | Default | Default | Default | Default | Default | Default |
Hover.link-underline-hover |
Hover | Hover | Hover | Hover | Hover | Hover | Hover | Hover |
Animate.link-underline-animate |
Animate | Animate | Animate | Animate | Animate | Animate | Animate | Animate |
None.link-underline-none |
None | None | None | None | None | None | None | None |
Size Variants
.link-xs0.75rem
.link-sm0.875rem
.link1rem
.link-lg1.125rem
.link-xl1.25rem
Special Variants
.link-externalShows external icon
.link-disabledNon-interactive
6. Card Component
Component Structure
.card + [size] + [style] + [color] + [interactive]
- Sizes: card-xs, card-sm, (default), card-lg, card-xl
- Styles: (default/solid), card-soft, card-outline, card-glass
- Colors: card-primary, card-secondary, card-accent, card-info, card-success, card-warning, card-error
- Interactive: card-hover, card-grow, card-link
- Layout: card-side, image-full
- Sections: card-header, card-body, card-footer, card-title, card-actions
Card Structure
Body Only
Simplest card with just card-body.
With Header
Card-header adds a top section with border.
Full Structure
Header, body, and footer sections.
Size Variants
XS
.card-xs
Small
.card-sm
Default
.card
Large
.card-lg
Extra Large
.card-xl
Color & Style Variants
Styles modify how colors are displayed. Combine: .card-[style] .card-[color]
| Style | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Solid(default) |
Solid
|
Solid
|
Solid
|
Solid
|
Solid
|
Solid
|
Solid
|
Solid
|
Soft.card-soft |
Soft
|
Soft
|
Soft
|
Soft
|
Soft
|
Soft
|
Soft
|
Soft
|
Outline.card-outline |
Outline
|
Outline
|
Outline
|
Outline
|
Outline
|
Outline
|
Outline
|
Outline
|
Glass Style .card-glass
Interactive Variants
Add hover/focus effects. Can combine with any style/color.
Hover Shadow
Adds shadow on hover.
.card-hover
Grow
Scales up on hover.
.card-grow
Link Card
Clickable with pointer cursor. Combine with hover/grow for effects.
.card-link
Combined Example
Soft + Primary + Hover
.card-soft.card-primary.card-hover
Outline + Success + Grow
.card-outline.card-success.card-grow
Error + Link + Hover
.card-error.card-link.card-hover
Layout Variants
Side Layout .card-side
Horizontal Card
Image on side with card-side class.
With Soft Style
Side layout + soft primary.
Image Full Overlay .image-full
Image Overlay
Content overlays the background image.
Card with Actions
Actions in Body
Buttons at the bottom of card-body.
Actions in Header
Toolbar-style layout with actions in header.
7. Alert Component
Component Structure
.alert + [style] + [color]
- Styles: (default/solid), alert-soft, alert-outline, alert-dashed
- Colors: alert-primary, alert-secondary, alert-accent, alert-info, alert-success, alert-warning, alert-error
- Sections: alert-icon, alert-content, alert-title, alert-description
Example: <div class="alert alert-success alert-soft">...</div>
Alert Structure
Alerts support different content layouts using alert-icon, alert-title, and alert-description.
.alert > .alert-content
.alert > .alert-icon + .alert-content > .alert-description
.alert-content > .alert-title + .alert-description
.alert > .alert-icon + .alert-content > (.alert-title + .alert-description)
Color & Style Variants
Styles modify how colors are displayed. Combine: .alert + .alert-[style] + .alert-[color]
| Color | Solid | Soft | Outline | Dashed |
|---|---|---|---|---|
Default.alert |
Solid |
Soft |
Outline |
Dashed |
Primary.alert-primary |
Solid |
Soft |
Outline |
Dashed |
Secondary.alert-secondary |
Solid |
Soft |
Outline |
Dashed |
Accent.alert-accent |
Solid |
Soft |
Outline |
Dashed |
Info.alert-info |
Solid |
Soft |
Outline |
Dashed |
Success.alert-success |
Solid |
Soft |
Outline |
Dashed |
Warning.alert-warning |
Solid |
Soft |
Outline |
Dashed |
Error.alert-error |
Solid |
Soft |
Outline |
Dashed |
8. Pagination Component
Component Structure
.pagination + [size] + [shape]
- Sizes: pagination-xs, pagination-sm, (default), pagination-lg, pagination-xl
- Shapes: (default/rounded), pagination-circle, pagination-square
Note: Pagination items inherit button styles through CSS. Inactive = default button, Active (strong) = primary button.
Default Pagination
Size Variants
Shape Variants
With Icons
Disabled State
9. Breadcrumb Component
JS Configuration (Stimulus)
data-controller="breadcrumb"
- Targets: data-breadcrumb-target="list|item"
- Max Items: data-breadcrumb-max-items-value="5" (collapse when MORE than N marked items)
How it works: Only items with data-breadcrumb-target="item" are counted and collapsible.
When collapsed, ALL marked items are hidden and shown in a dropdown. Items without the attribute stay always visible.
CSS Classes
.breadcrumb + [size] + [color]
- Sizes: breadcrumb-xs, breadcrumb-sm, (default), breadcrumb-lg, breadcrumb-xl
- Colors: breadcrumb-primary, breadcrumb-secondary, breadcrumb-accent, etc. (changes hover color)
- Separators: .breadcrumb-separator (use SVG icons or text characters)
Contao: .mod_breadcrumb inherits all styles. Add variant classes via CSS ID field.
Dropdown: Size and color variants apply to the generated dropdown menu as well.
Default (Responsive Collapse)
This example has 4 marked items ≤ default max of 5, so it's expanded on desktop.
On screens <640px, the CSS variable --breadcrumb-force-collapse: 1 triggers collapse.
Note: The collapse state is determined on page load. It does NOT auto-collapse when resizing the browser window.
Color Variants (Collapsed)
All examples use max-items="3" to force collapse (4 marked items > 3). Hover over links and ellipsis to see color.
.breadcrumb-primary (default).breadcrumb-secondary.breadcrumb-accent.breadcrumb-info.breadcrumb-success.breadcrumb-warning.breadcrumb-errorSize Variants (Collapsed)
All examples use max-items="3" to force collapse. Click the ellipsis to see dropdown sizing.
.breadcrumb-xs.breadcrumb-sm.mod_breadcrumb (default).breadcrumb-lg.breadcrumb-xl10. Popover & Dropdown Component
JS Configuration (Stimulus)
data-controller="popover"
- Targets: data-popover-target="trigger|content|arrow"
- Placement: data-popover-placement-value="top|bottom|left|right" (default: bottom)
- Alignment: data-popover-alignment-value="start|center|end" (default: center)
- Trigger: data-popover-trigger-value="click|hover" (default: click)
- Flip: data-popover-flip-value="true|false" (default: true)
- Actions: click->popover#toggle, click->popover#close
CSS Classes
.popover-content + [size] + [preset]
- Sizes: popover-xs, popover-sm, (default), popover-lg, popover-xl
- Presets: popover-dropdown, popover-tooltip
- Arrow: .popover-arrow (optional, auto-positioned)
- Menu: .dropdown-menu, .dropdown-item, .dropdown-separator, .dropdown-label
- Item states: .dropdown-item.active, [aria-disabled="true"]
Placement Variants
Set placement with data-popover-placement-value. Click trigger requires data-action="click->popover#toggle"
placement="top"placement="bottom"placement="left"placement="right"Trigger Variants
Set trigger with data-popover-trigger-value. Hover works automatically, click needs action.
trigger="click" (default)trigger="hover"Alignment Variants
Set alignment with data-popover-alignment-value. Aligns popover along placement axis.
alignment="start"alignment="center" (default)alignment="end"Size Variants
Add size class to .popover-content
.popover-xs.popover-sm.popover-lg.popover-xlSize Variants with Hover (Tooltip Style)
Same sizes but with data-popover-trigger-value="hover" and .popover-tooltip
.popover-xs.popover-sm.popover-lg.popover-xlPreset Variants
Presets apply predefined styling. Add to .popover-content
.popover-dropdown.popover-tooltipDropdown Menu
Use .popover-dropdown with .dropdown-menu, .dropdown-item, .dropdown-separator, .dropdown-label
John Doe
john@example.com
Interactive Content
Popovers can contain forms and buttons. Use data-action="click->popover#close" for close buttons.
Dimensions
Set the dimensions for the layer.
Delete this item?
This action cannot be undone.
11. Input Component
Component Structure
.form-input + [style] + [color] + [size]
- Styles: (default/outline), input-soft
- Colors: input-primary, input-secondary, input-accent, input-info, input-success, input-warning, input-error
- Sizes: input-xs, input-sm, (default), input-lg, input-xl
- States: is-valid, is-invalid, disabled, readonly
- Labels: label-text, helper-text, error-text, success-text
Example: <input class="form-input input-primary" placeholder="Enter text">
Input Structure
Inputs support labels, helper text, and error messages.
.label-text + .form-input + .helper-text
.label-text.label-required
.form-input.is-valid + .success-text
.form-input.is-invalid + .error-text
Color & Style Variants
Colors apply to focus state. Combine: .form-input + .input-[style] + .input-[color]
| Color | Outline (default) | Soft |
|---|---|---|
| Default | ||
Primary.input-primary |
||
Secondary.input-secondary |
||
Accent.input-accent |
||
Info.input-info |
||
Success.input-success |
||
Warning.input-warning |
||
Error.input-error |
Size Variants
.input-xs
.input-sm
.form-input
.input-lg
.input-xl
Floating Label
Label floats up when input is focused or has value.
.input-floating > .form-input + .input-floating-label
When using a placeholder with floating label, placeholder shows only when input is focused.
placeholder="you@example.com"
-
Background matching: The floating label needs a solid background to "sit on" the input border.
By default, it uses
bg-base-100. Add a matchingbg-*class to.input-floatingwhen on different backgrounds. -
Supported backgrounds: Only full color classes work:
bg-base-{100|200|300},bg-{primary|secondary|accent|neutral|info|success|warning|error}. Opacity variants likebg-primary/10are not supported. -
Soft style not recommended: When using
.input-soft, the input has a tinted background while the label has a solid background, creating visible contrast. Use outline style inputs with floating labels. - Background consistency: For the best appearance, ensure the background color inside the input (transparent for outline style) matches the background color of the container.
Input with Icons
Use .input-group wrapper with .has-icon-left or .has-icon-right.
.input-group.has-icon-left
.input-group.has-icon-right
.has-icon-left.has-icon-right
Input States
disabled attribute
readonly attribute
12. Input Number
Number input with increment/decrement buttons. Layout is determined by HTML order. Requires input-number Stimulus controller.
Default (Number Left, Buttons Right)
HTML order: input → button − → button +
<div class="input-number">
<input type="number" class="form-input" ...>
<button class="input-number-btn">−</button>
<button class="input-number-btn">+</button>
</div>
Center (− Number +)
HTML order: button − → input → button + with .input-number-center
<div class="input-number input-number-center">
<button class="input-number-btn">−</button>
<input type="number" class="form-input" ...>
<button class="input-number-btn">+</button>
</div>
Layout & Style Variants
Combine layout (.input-number / .input-number-center), style (.input-soft), and color classes. Buttons inherit colors automatically.
| Layout | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Default.input-number |
|
|
|
|
|
|
|
|
Center.input-number-center |
|
|
|
|
|
|
|
|
Soft.input-soft |
|
|
|
|
|
|
|
|
Soft Center.input-soft |
|
|
|
|
|
|
|
|
.input-[color] → buttons inherit automatically
Size Variants
Buttons scale automatically based on the input size class.
.input-xs
.input-sm
.form-input (default)
.input-lg
.input-xl
Min, Max & Step
The controller respects min, max, and step attributes. Buttons are disabled when limits are reached.
Disabled State
disabled attribute on input and buttons
13. Textarea Component
Component Structure
.form-textarea + [style] + [color] + [size]
- Styles: (default/outline), textarea-soft
- Colors: textarea-primary, textarea-secondary, textarea-accent, textarea-info, textarea-success, textarea-warning, textarea-error
- Sizes: textarea-xs, textarea-sm, (default), textarea-lg, textarea-xl
- Resize: (default/vertical), textarea-resize-none, textarea-resize-x, textarea-resize-both
- States: is-valid, is-invalid, disabled, readonly
Example: <textarea class="form-textarea textarea-primary" placeholder="Enter message"></textarea>
Textarea Structure
Textareas follow the same patterns as inputs.
.label-text + .form-textarea + .helper-text
.form-textarea.is-invalid + .error-text
Color & Style Variants
Colors apply to focus state. Combine: .form-textarea + .textarea-[style] + .textarea-[color]
Size Variants
.textarea-xs
.textarea-sm
.form-textarea
.textarea-lg
.textarea-xl
Floating Label
.textarea-floating > .form-textarea + .textarea-floating-label
Resize Options
default (resize-y)
.resize-none
.resize-x
.resize-both
14. Select Component
Component Structure
.form-select + [style] + [color] + [size]
- Styles: (default/outline), select-soft
- Colors: select-primary, select-secondary, select-accent, select-info, select-success, select-warning, select-error
- Sizes: select-xs, select-sm, (default), select-lg, select-xl
- States: is-valid, is-invalid, disabled
Example: <select class="form-select select-primary">...</select>
Select Structure
Selects follow the same patterns as inputs.
.label-text + .form-select + .helper-text
.select.is-invalid + .error-text
Color & Style Variants
Colors apply to focus state. Combine: .form-select + .select-[style] + .select-[color]
| Style | Default | Primary | Secondary | Accent | Info | Success | Warning | Error |
|---|---|---|---|---|---|---|---|---|
Outline(default) |
||||||||
Soft.select-soft |
Size Variants
.select-xs
.select-sm
.select
.select-lg
.select-xl
Floating Label
Unlike inputs, select floating labels remain floated at all times.
.select-floating > .form-select + .select-floating-label
With Leading Icon
Selects only support leading icons (trailing is the dropdown arrow).
.select-group.has-icon-left
Option Groups
<optgroup label="...">
Validation States
.select.is-valid
.select.is-invalid
Disabled State
disabled
15. Checkbox Component
Component Structure
.form-checkbox + [color] + [size]
- Colors: checkbox-primary, checkbox-secondary, checkbox-accent, checkbox-info, checkbox-success, checkbox-warning, checkbox-error
- Sizes: checkbox-xs, checkbox-sm, (default), checkbox-lg, checkbox-xl
- States: disabled, indeterminate (via JS)
Example: <input type="checkbox" class="form-checkbox checkbox-primary">
Default Checkbox
<input type="checkbox" class="form-checkbox">
Colors
.checkbox-primary, .checkbox-secondary, etc.
Sizes
.checkbox-xs, .checkbox-sm, .checkbox-lg, .checkbox-xl
Disabled State
disabled
Indeterminate State
The indeterminate state is set via JavaScript. It shows a horizontal line instead of a checkmark.
element.indeterminate = true;
16. Radio Component
Component Structure
.form-radio + [color] + [size]
- Colors: radio-primary, radio-secondary, radio-accent, radio-info, radio-success, radio-warning, radio-error
- Sizes: radio-xs, radio-sm, (default), radio-lg, radio-xl
- States: disabled
Example: <input type="radio" name="group" class="form-radio radio-primary">
Default Radio
<input type="radio" name="group" class="form-radio">
Colors
.radio-primary, .radio-secondary, etc.
Sizes
.radio-xs, .radio-sm, .radio-lg, .radio-xl
Disabled State
disabled
17. Switch Component
Component Structure
.form-switch + [color] + [size] + [variant]
- Colors: switch-primary, switch-secondary, switch-accent, switch-info, switch-success, switch-warning, switch-error
- Sizes: switch-xs, switch-sm, (default), switch-lg, switch-xl
- Variants: switch-outline
- States: disabled
Example: <input type="checkbox" class="form-switch switch-primary">
Default Switch
.form-switch
Colors
.switch-primary, .switch-secondary, .switch-accent, etc.
Sizes
.switch-xs, .switch-sm, .switch-lg, .switch-xl
Outline Variant
.switch-outline
Disabled State
disabled
18. Range Component
Component Structure
.form-range + [color] + [size]
- Colors: range-primary, range-secondary, range-accent, range-info, range-success, range-warning, range-error
- Sizes: range-xs, range-sm, (default), range-lg, range-xl
- States: disabled
Example: <input type="range" class="form-range range-primary" min="0" max="100" value="50">
Default Range
<input type="range" class="form-range">
Colors
.range-primary, .range-secondary, .range-accent, etc.
Sizes
.range-xs
.range-sm
.range
.range-lg
.range-xl
Min, Max & Step
min="0" max="10" step="25"
Disabled State
disabled
With JavaScript (Stimulus Controller)
The range component can be enhanced with the range Stimulus controller for:
- Fill effect: Track fills with color as you drag (add
.range-fill) - Value display: Live value updates in a target element
How to use
- 1. Add
data-controller="range"to a wrapper element - 2. Add
data-range-target="input"to the range input - 3. Add
data-action="input->range#update"to trigger updates - 4. (Optional) Add
.range-fillclass for fill effect - 5. (Optional) Add
data-range-target="value"to an element to display the value - 6. (Optional) Add
data-range-suffix-value="%"to add a suffix
Fill Variant
Add .range-fill to show a colored fill up to the thumb position.
.form-range.range-fill + data-controller="range" + data-range-target="input" + data-action="input->range#update"
Value Display
Add data-range-target="value" to any element to display the current value. Use data-range-suffix-value to add a suffix like "%". The fill effect (.range-fill) is optional.
data-range-target="value" — fill is optional (add .range-fill if desired)
Full Example
<div data-controller="range" data-range-suffix-value=" GB">
<span data-range-target="value">50</span>
<input type="range" class="form-range range-fill range-primary"
data-range-target="input"
data-action="input->range#update"
min="10" max="100" step="10" value="50">
</div>
19. Theme Controller
Animated Sun/Moon Toggle
The easiest way to add a theme toggle. Just include the Twig template - no additional setup required. It automatically connects to the theme controller.
Default (24px)
Medium (32px)
Large (48px)
Usage
{# Default size (24px) #}
{{ include('@Contao/kiss_component/theme/_toggle.html.twig') }}
{# Custom size #}
{{ include('@Contao/kiss_component/theme/_toggle.html.twig', { size: 32 }) }}
{# With additional classes #}
{{ include('@Contao/kiss_component/theme/_toggle.html.twig', { class: 'my-class' }) }}
🎨 Animation Features
- Sun rays: Scale down and fade out when transitioning to moon
- Moon mask: Slides in to create crescent shape
- Rotation: -45° tilt in dark mode
- Hover: Playful wiggle animation
- Easing: Spring-like cubic-bezier for organic motion
Theme Controller (Stimulus)
The underlying Stimulus controller that powers all theme switching. Use this to create custom toggle implementations.
Controller Reference
data-controller="theme"
- Modes:
light,dark,system(follows OS preference) - Methods:
toggle()switches light↔dark,set()sets specific mode - Targets:
toggle,radio,select,lightIcon,darkIcon - Events: Dispatches
theme:changewith detail{ mode, theme, isDark } - Storage: Persists to
localStorageautomatically - Sync: Multiple controllers on the same page stay in sync
Toggle Variants (Light ↔ Dark)
Simple two-way toggles for switching between light and dark mode.
Switch Toggle
<label data-controller="theme">
<input type="checkbox" class="switch"
data-theme-target="toggle"
data-action="change->theme#toggle">
</label>
Checkbox Toggle
Same pattern, just use .checkbox instead
Button with Text
<div data-controller="theme">
<button class="btn" data-action="click->theme#toggle">
<span data-theme-target="lightIcon">Light Mode</span>
<span style="display: none;" data-theme-target="darkIcon">Dark Mode</span>
</button>
</div>
Mode Selectors (Light / Dark / System)
Three-way selectors that include "System" mode which follows the OS preference. All selectors use data-action="change->theme#set" to call the set() method which reads the value from the input.
Dropdown Select
Native select element. The controller auto-syncs the selected option on page load.
<div data-controller="theme">
<select class="select select-primary"
data-theme-target="select"
data-action="change->theme#set">
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">System</option>
</select>
</div>
Radio Buttons
Standard visible radios. Use .radio class for styling and any color variant.
Each radio input needs data-theme-target="radio" and data-action="change->theme#set". The controller automatically checks the correct radio on page load based on the stored preference.
Segmented Control
Hidden radios with custom styling. Uses .sr-only to hide inputs and peer-checked: to style the visible span when selected.
The .sr-only class hides the radio visually while keeping it accessible. The peer class on the input allows sibling elements to react to its state using peer-checked: variants. This pattern works with any Tailwind utility.
Pill Buttons
Hidden radios styled as buttons. Uses .btn classes with peer-checked: to apply active state styling.
Same technique as the segmented control but using .btn classes instead. The !bg-primary with important modifier ensures the background color applies correctly when combined with other button styles.
Events
Listen for theme changes anywhere in your application:
document.addEventListener('theme:change', (event) => {
console.log('Theme changed:', event.detail);
// { mode: 'dark', theme: 'dark', isDark: true }
});
KISS Component System - Phase 1
All components ready for production use