beartropy/ui
Beartropy UI is a small Laravel UI package providing ready-made components and helpers to speed up building admin and front-end screens. Drop in common layouts, forms, and UI utilities to get a consistent look fast with minimal setup.
<x-bt-toggle-theme />
<x-bt-toggle-theme mode="button" label="Dark Mode" size="lg" />
<x-bt-toggle-theme mode="square-button" icon-light="sun" icon-dark="moon" />
ToggleTheme extends BeartropyComponent (inherits HasPresets, HasErrorBag)toggle-theme.blade.php — inline <style> for animation, Alpine via x-data="btToggleTheme()"resources/js/modules/toggle-theme.js (bundled into beartropy-ui.js)
initTheme() — runs at bundle load (before Alpine), applies saved theme, exposes __setThemebtToggleTheme() — registered as Alpine.data(), handles toggle/rotation/event synclocalStorage.theme ('dark' | 'light') and bt_theme cookie (for server-side rendering)theme-change CustomEvent for cross-component syncMutationObserver on <html> to catch wire:navigate morphing the class attribute| Prop | Type | Default | Blade Attribute |
|---|---|---|---|
| size | string |
'md' |
size="lg" |
| mode | string |
'icon' |
mode="button" |
| class | string |
'' |
class="my-class" |
| inheritColor | bool |
false |
:inherit-color="true" |
| iconColorLight | ?string |
'text-orange-600' |
icon-color-light="text-yellow-500" |
| iconColorDark | ?string |
'text-blue-400' |
icon-color-dark="text-indigo-400" |
| borderColorLight | ?string |
'border-orange-300 dark:border-blue-600' |
border-color-light="..." |
| borderColorDark | ?string |
'border-orange-400 dark:border-blue-500' |
border-color-dark="..." |
| iconLight | ?string |
null |
icon-light="sun" |
| iconDark | ?string |
null |
icon-dark="moon" |
| label | ?string |
null |
label="Dark Mode" |
| labelPosition | string |
'right' |
label-position="left" |
| labelClass | ?string |
null |
label-class="text-xs" |
| ariaLabel | ?string |
null |
aria-label="Switch theme" |
| Slot | Description |
|---|---|
icon-light |
Custom SVG/HTML for light mode icon |
icon-dark |
Custom SVG/HTML for dark mode icon |
| Size | Icon | Button Padding | Square Button |
|---|---|---|---|
| xs | w-2 h-2 | p-1 | w-7 h-7 |
| sm | w-3 h-3 | p-1.5 | w-8 h-8 |
| md | w-4 h-4 | p-2 | w-10 h-10 |
| lg | w-5 h-5 | p-3 | w-12 h-12 |
| xl | w-6 h-6 | p-4 | w-14 h-14 |
| 2xl | w-8 h-8 | p-5 | w-16 h-16 |
icon-light / icon-dark) — fully custom contenticonLight / iconDark) — renders <x-beartropy-ui::icon><style> [@keyframes](https://github.com/keyframes) theme-spin, .theme-rotate </style>
div[x-data="btToggleTheme()"]
└── button[type="button"][[@click](https://github.com/click).stop="toggle()"][:aria-pressed][aria-label]
├── (icon-light slot | heroicon | sun SVG)[x-show="!dark"]
└── (icon-dark slot | heroicon | moon SVG)[x-show="dark"]
btToggleTheme()){
dark: boolean, // Current theme state (from localStorage + system pref)
rotating: boolean, // Animation flag (true during 500ms rotation)
init() {
// Listen for external theme-change events to sync state
},
toggle() {
// Flip dark, update <html> classList + colorScheme,
// persist to localStorage + bt_theme cookie,
// dispatch 'theme-change' event,
// trigger rotation via $nextTick (ensures new icon renders first)
}
}
resources/js/modules/toggle-theme.js)// initTheme() — called at bundle load, before Alpine
// - Applies saved theme before CSS loads (FOUC prevention)
// - Sets bt_theme cookie (enables server-side rendering via [@beartropyHtmlClass](https://github.com/beartropyHtmlClass))
// - Exposes window.__setTheme('dark' | 'light') for programmatic use
// - MutationObserver on <html> catches wire:navigate class morphing
window.__setTheme('dark'); // Programmatic theme setter
// Dispatches: CustomEvent('theme-change', { detail: { theme: 'dark' } })
{{-- Basic icon toggle --}}
<x-bt-toggle-theme />
{{-- Button with label --}}
<x-bt-toggle-theme mode="button" label="Dark Mode" size="lg" />
{{-- Square button --}}
<x-bt-toggle-theme mode="square-button" size="md" />
{{-- Custom colors --}}
<x-bt-toggle-theme
icon-color-light="text-yellow-500"
icon-color-dark="text-indigo-400"
/>
{{-- Custom heroicons --}}
<x-bt-toggle-theme icon-light="sun" icon-dark="moon" />
{{-- Custom SVG slots --}}
<x-bt-toggle-theme>
<x-slot:icon-light><span>☀️</span></x-slot:icon-light>
<x-slot:icon-dark><span>🌙</span></x-slot:icon-dark>
</x-bt-toggle-theme>
{{-- In a nav bar --}}
<x-bt-toggle-theme mode="button" label="Theme" size="sm" class="ml-auto" />
[@BeartropyAssets](https://github.com/BeartropyAssets) handles FOUC prevention automatically via four layers:
<style> — html.dark{color-scheme:dark} for native form control theming (CSS-only, no JS dependency)<script> — applies dark class + colorScheme synchronously before body rendersdata-navigate-once — prevents Livewire wire:navigate from removing/re-adding CSS and JS during head mergingMutationObserver — watches <html> class attribute; if wire:navigate morphs it and strips dark, the observer re-applies before the browser repaintsFor zero-FOUC server-side rendering, use [@beartropyHtmlClass](https://github.com/beartropyHtmlClass) on your <html> tag:
<html lang="en" class="[@beartropyHtmlClass](https://github.com/beartropyHtmlClass)">
This reads the bt_theme cookie (set automatically by the toggle) and renders the dark class server-side.
If you load assets manually (e.g., via [@vite](https://github.com/vite) without [@BeartropyAssets](https://github.com/BeartropyAssets)), add <x-bt-theme-head /> to your layout's <head> before stylesheets. See theme-head for details.
<button> (not bare SVG) — proper semantics + keyboard accessiblearia-label and :aria-pressed="dark"__('beartropy-ui::ui.toggle_theme') → "Toggle theme".theme-rotate class, synced with JS timeout<script> runs on every page load (even with multiple instances, safe to duplicate)window.__setTheme() allows external code (nav dropdowns, settings pages) to change themecolorScheme CSS property is set alongside dark class for native form element dark modebt_theme cookie enables server-side theme rendering via [@beartropyHtmlClass](https://github.com/beartropyHtmlClass)isset($__data['iconLight']) (camelCase) — slots are stored camelCase in $__dataHow can I help you explore Laravel packages today?