# TallStackUI Component Documentation > TallStackUI is a TALL Stack (Tailwind CSS, Alpine.js, Laravel, Livewire) > component library providing 80+ Blade components for building modern web interfaces. > > **Stack:** PHP 8.1+, Laravel 10/11/12/13, Livewire 4, Tailwind CSS 4, Alpine.js 3 ## Component Index ### Display - [Alert](components/alert.md) - [Avatar](components/avatar.md) - [Avatar Group](components/avatar/group.md) - [Back to Top](components/back-to-top.md) - [Badge](components/badge.md) - [Banner](components/banner.md) - [Boolean](components/boolean.md) - [Breadcrumbs](components/breadcrumbs.md) - [Calendar](components/calendar.md) - [Card](components/card.md) - [Carousel](components/carousel.md) - [Chart](components/chart.md) - [Clipboard](components/clipboard.md) - [Editor](components/editor.md) - [Environment](components/environment.md) - [Errors](components/errors.md) - [Gallery](components/gallery.md) - [Icon](components/icon.md) - [Kbd](components/kbd.md) - [Key-Value](components/key-value.md) *(Livewire only)* - [Link](components/link.md) - [List](components/list/main.md) - [List Items](components/list/items.md) - [QrCode](components/qr-code.md) - [Stats](components/stats.md) - [Table](components/table.md) - [Timeline](components/timeline/main.md) - [Timeline Items](components/timeline/items.md) - [Tooltip](components/tooltip.md) ### Buttons - [Button](components/button/normal.md) - [Button Circle](components/button/circle.md) - [Button Group](components/button/group.md) ### Form - [Autocomplete](components/form/autocomplete.md) - [Checkbox](components/form/checkbox.md) - [Checkbox Group](components/form/checkbox/group.md) - [Color Picker](components/form/color.md) - [Currency](components/form/currency.md) - [Date Picker](components/form/date.md) - [Error](components/form/error.md) - [Hint](components/form/hint.md) - [Input](components/form/input.md) - [Input Select](components/form/input-select.md) - [Label](components/form/label.md) - [Number](components/form/number.md) - [Password](components/form/password.md) - [Pin](components/form/pin.md) - [Radio](components/form/radio.md) - [Radio Group](components/form/radio/group.md) - [Range](components/form/range.md) - [Select Native](components/form/select/native.md) - [Select Styled](components/form/select/styled.md) - [Swap](components/swap.md) - [Tag](components/form/tag.md) - [Textarea](components/form/textarea.md) - [Time Picker](components/form/time.md) - [Toggle](components/form/toggle.md) - [Upload](components/form/upload.md) *(Livewire only)* - [Upload Async](components/form/upload/async.md) ### Overlay & Interaction - [Command Palette](components/command-palette.md) - [Dialog](components/dialog.md) - [Dropdown](components/dropdown/main.md) - [Dropdown Items](components/dropdown/items.md) - [Dropdown Submenu](components/dropdown/submenu.md) - [Loading](components/loading.md) *(Livewire only)* - [Modal](components/modal.md) - [Slide](components/slide.md) - [Toast](components/toast.md) ### Navigation & Layout - [Accordion](components/accordion/main.md) - [Accordion Items](components/accordion/items.md) - [Dial](components/dial/main.md) - [Dial Items](components/dial/items.md) - [Layout](components/layout/main.md) - [Layout Header](components/layout/header.md) - [Sidebar](components/layout/sidebar/main.md) - [Sidebar Item](components/layout/sidebar/item.md) - [Sidebar Separator](components/layout/sidebar/separator.md) - [Step](components/step/main.md) - [Step Items](components/step/items.md) - [Tab](components/tab/main.md) - [Tab Items](components/tab/items.md) ### Progress & Feedback - [Progress Bar](components/progress/bar.md) - [Progress Circle](components/progress/circle.md) - [Rating](components/rating.md) - [Reaction](components/reaction.md) *(Livewire only)* - [Signature](components/signature.md) *(Livewire only)* - [Spinner](components/spinner.md) ### Theme - [Theme Switch](components/theme-switch.md) ### Internal - [Floating](components/floating.md) *(internal)* - [Wrapper Input](components/wrapper/input.md) *(internal)* - [Wrapper Radio](components/wrapper/radio.md) *(internal)* ## Binding to a nested property `wire:model` accepts a nested path, which is what a Livewire Form object and any nested array look like. Only the head of the path has to be a real property on the component: ```blade ``` ## Outside Livewire The library was built for Livewire, but the form components also work on a plain Blade page posting to a controller. Give the component a `name` instead of a `wire:model` and it renders a hidden input carrying the value, so the server receives it like any other field. `value` seeds the initial state. ```blade
@csrf ``` What arrives on the server depends on the component: a single value goes as is, and a multi-value selection is JSON encoded. Each component's page states its own shape, and `Currency` additionally offers three formats through `mutate` and `decimal`. Components that carry no value — Modal, Slide, Toast and friends — are unaffected either way. Livewire's script still has to be on the page, since that is where Alpine comes from. ## Readonly and disabled Every form component accepts both, and they mean the same thing except for one detail: `disabled` does not submit the value, `readonly` does. The visual is identical, and neither can be interacted with. ```blade ``` The browser only honours `readonly` on text inputs and textareas. On a checkbox, radio, range or select it does nothing at all, so the library emulates it: the control keeps its enabled state — which is what makes it submit — and the pointer and keyboard are swallowed instead. Buttons that live inside a component (the clear on `Date`, the tag remove on `Tag`, the palette on `Color`) are disabled outright, since a button carries no value. Supported by `Input`, `Textarea`, `Password`, `Number`, `Currency`, `Color`, `Date`, `Time`, `Tag`, `Pin`, `Range`, `Checkbox`, `Radio`, `Toggle`, `Upload`, `Upload/Async`, `Autocomplete`, `Select/Native`, `Select/Styled`, `Input/Select`, `Swap` and the `Checkbox`/`Radio` groups. `Input/Select` passes its own lock down to the component in the `left`/`right` slot, so the compound control locks as one. A lock declared on the slot's own component still wins: ```blade {{-- both halves locked --}} {{-- text locked, select still usable --}} ``` Components paint the state through a `locked` soft customization block, applied by the template rather than through Tailwind's `disabled:` variant — that variant cannot match an element that is only `readonly`. ## Global Configuration Top-level keys in `config/tallstackui.php`, applying across components rather than to a single one. Per-component options live under `components.` and are documented on each component's page. | Key | Type | Default | Description | |------------------------|--------|---------|----------------------------------------------------------------------------------------| | `prefix` | string | null | Prefixes every component, so `ts-` gives `` | | `invalidate_global` | bool | false | Suppresses validation errors on every form component (`TALLSTACKUI_INVALIDATE_GLOBAL`) | | `floating_scroll_lock` | bool | false | Locks the page scroll while any floating-based popup is open | `debug.environments` is a comma-separated list via `TALLSTACKUI_DEBUG_ENVIRONMENTS` (default `local,sandbox,staging`). `floating_scroll_lock` covers Dropdown and its Submenu, Autocomplete, Color, Date, Password, Select Styled, Time, Upload, Calendar and the List Items menu — every component built on [Floating](components/floating.md), where the reference counting and the interaction with modals are described. ### How the published file is merged `php artisan vendor:publish --tag=tallstackui.config` writes `config/tallstackui.php`, which is merged over the package defaults. Keys the file does not mention keep their default, so a file written against an older release does not lose options added since. Lists of scalars are the exception: they are taken as published rather than merged entry by entry, which is what lets a published list be shorter than the default. ```php // package default: [10, 25, 50, 100] 'quantity' => [15, 30], // the table offers exactly 15 and 30 ``` The same applies to `editor.toolbar`, `editor.sanitization.allowed_tags`, `editor.upload.mimes` and `debug.environments`: publishing a shorter list narrows what is allowed instead of adding to it. ## Skeleton Seven components accept a `skeleton` prop that renders a structural placeholder shaped like the component itself, for the first paint before any data exists: [Card](components/card.md#skeleton), [Stats](components/stats.md#skeleton), [Table](components/table.md#skeleton), [List](components/list/main.md#skeleton), [Step](components/step/main.md#skeleton), [Chart](components/chart.md#skeleton) and [QR Code](components/qr-code.md#skeleton). ```blade {{-- bare flag: 3 body lines --}} {{-- integer: 8 rows --}} ``` | Component | Unit | Default | |-----------|-------------------------|---------| | Card | body lines | 3 | | Table | rows | 5 | | List | items | 4 | | Step | step indicators | 3 | | Chart | data points (or slices) | 6 | | Stats | — (flag only) | n/a | | QR Code | — (flag only) | n/a | Everything else is derived from props the component already has. The prop is `bool|int|null` everywhere. Any integer below `1` throws. `Stats` throws on any integer, and `QR Code` ignores it — both draw a fixed shape with nothing to count. What is already known stays legible; only the unknown becomes a bar — the table keeps its real header labels, for instance. It belongs in the `placeholder()` of a `#[Lazy]` Livewire component, but works anywhere, including plain Blade with no Livewire at all. **It does not replace `loading`.** `loading` (Card and Table only) dims content already on screen during a Livewire round trip; `skeleton` stands in for content that does not exist yet. And it defers nothing: Blade evaluates slot content before the component renders, so deferral is Livewire's job. **Existing soft customizations carry over.** The skeleton view calls the same `classes()` as the normal one, so every structural block a component already has — wrappers, radius, padding, header and footer chrome — applies to the placeholder too, scopes included. Only the bars come from the `skeleton.*` blocks. Blocks the placeholder does not render simply have no target there. ```php TallStackUi::customize()->card()->block('wrapper.second', 'rounded-3xl bg-white'); ``` ```blade {{-- rounded-3xl, exactly like the real card --}} ``` ## Soft Customization All components support runtime customization of their Tailwind CSS classes: ```php // In AppServiceProvider::boot() TallStackUi::customize() ->alert() ->block('wrapper', 'your-tailwind-classes'); // Scoped customization TallStackUi::customize('alert', scope: 'hero')->block('wrapper', 'your-tailwind-classes'); // Then: // Extending a scope that already exists, including the ones the package ships TallStackUi::customize() ->extend(scope: 'card-shadowless') ->card() ->block('wrapper.second') ->append('ring-1 ring-gray-100'); ``` ### Customization Methods - `block(name, classes)` - Set classes for a block - `append(classes)` - Add classes to end - `prepend(classes)` - Add classes to beginning - `replace(from, to)` - Replace a substring, so `replace('gray-', 'zinc-')` swaps a palette - `remove(class)` - Remove whole classes, matched by token; accepts a list or a space-separated string - `scope(name)` - Define a scope, replacing it if the name is already taken - `extend(scope: name)` - Reuse a scope already defined for that component; throws when it does not exist Customizations of the same block stack: two chains, or two service providers, each add on top of what the other did rather than overwriting it. ### Scopes layer over the global customization A scope only overrides the blocks it names. Every other block keeps whatever the global customization did to it, so a scoped instance is the global look plus the scope's changes, not a reset: ```php TallStackUi::customize()->alert()->block('wrapper')->append('brand-shadow'); TallStackUi::customize('alert', scope: 'flat')->block('text.title')->append('text-xl'); ``` `` renders with both `brand-shadow` and `text-xl`. This applies to the scopes the package ships as well, such as ``. Block names containing a dot (`body.paddingless`, `wrapper.second`) are keys, not paths. A scope can set `body` and `body.paddingless` in the same call without one replacing the other. ## Global JavaScript API ```javascript // Modal control $tsui.open.modal('name') $tsui.close.modal('name') // Slide control $tsui.open.slide('name') $tsui.close.slide('name') // Select control $tsui.open.select('name') $tsui.close.select('name') // Command Palette $tsui.open.commandPalette() $tsui.close.commandPalette() // Focus element $tsui.focus('element-id') // Programmatic clipboard copy // Resolves to a boolean (success) and dispatches the `ts-ui:copy` // event on `window` with `{ detail: { text } }`. const copied = await $tsui.copy('text to copy') // Programmatic interactions $tsui.interaction('dialog').success('Title', 'Description').send() $tsui.interaction('toast').warning('Title').send() ```