🎉 Welcome to the new major version: v3. Upgrade now!

Powerful suite of Blade components for TALL Stack apps.

Soft Customization

The soft customization.

The soft customization involves customizing components at runtime, either through a service provider like AppServiceProvider or object classes. The idea behind soft customization is to explore the building blocks of customization for each component. Even if you are starting with Laravel, with a little attention to the docs below, you will be able to fully customize the components using this concept.

Since the idea of soft customization is to apply customization through PHP object classes, the first thing you need to do is make sure that TailwindCSS tracks the classes that will be defined from your application's *.php files. To do this, you need to edit your app.css CSS file by inserting this content:

@source '../../app/Providers/*.php';

Now that you have prepared TailwindCSS to track your custom classes, let's start customizing your components. Let's take a look at an example:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// ...
 
TallStackUi::customize()
->form('input')
->block('input.base', 'w-full rounded-full');
 
// or...
 
TallStackUi::customize('form.input')
->block('input.base', 'w-full rounded-full');
}
}

In this example we are touching and replacing all the classes in the input.base block of the input component with the content: w-full rounded-full . This means that every input component displayed on the application pages will have these classes, instead of the original component classes.

At this point you may be wondering how to "discover" the blocks of each component. To do this, when browsing the documentation of each component individually you will notice a button called Customize: {Component Name} which, when clicked, will display a modal containing all the blocks - and their names, as well as the original classes that are defined by each block, for example:

Since soft customization was created to be easy to use, just like Pest, the soft customization offers a concept of fluency when using the and like a property or method. The idea behind this approach is to customize more than one component at the same time.

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// 1. Property
 
TallStackUi::customize()
->form('input')
->block('input.base', 'w-full rounded-full')
->and
->avatar()
->block('wrapper.sizes.sm', 'w-8 h-8 text-xs')
 
// Or, 2. Method
 
TallStackUi::customize()
->form('input')
->block('input.base', 'w-full rounded-full')
->and()
->avatar()
->block('wrapper.sizes.sm', 'w-8 h-8 text-xs')
}
}

Since a component has several blocks that organize the classes applied to the component, you can customize one block at a time or all of them at once:

use TallStackUi\Facades\TallStackUi;
use App\TallStackUi\InputCustomization;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize()
->form('input')
->block('input.base', new InputCustomization())
->block('icon.wrapper', fn (array $data) => 'px-4 py-2')
->block('icon.paddings.left', 'pl-10');
 
// Or ...
 
TallStackUi::customize()
->form('input')
->block([
'input.class' => new InputCustomization(),
'icon.wrapper' => fn (array $data) => 'px-4 py-2',
'icon.paddings.left' => 'pl-10',
]);
}
}

You may have noticed that in the example above we used the InputPersonalization class. This is a simple invokable object class, because soft customization also allows you to make your customization into object invokable classes. This approach is ideal if you are someone who prioritizes organization above all else. Let's take a look at an example:

Preparing:

use TallStackUi\Facades\TallStackUi;
use App\TallStackUi\InputCustomization;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize()
->form('input')
->block('input.base', new InputCustomization());
}
}

Customizing:

// You must track this namespace in the TailwindCSS config file!
namespace App\TallStackUi;
 
class InputCustomization
{
public function __invoke(array $data): string
{
return 'w-full rounded-full';
}
}

You may have noticed that the example above there is a variable called $data . This variable is an array containing all the component's properties, including the values passed when you used the component somewhere in your application.

Using the input like this:

<x-input label="Name" hint="Your full name" />

The $data will be something like:

[
"label" => "Name"
"hint" => "Your full name"
"icon" => null
"clearable" => null
"invalidate" => null
"position" => "left"
"prefix" => null
"suffix" => null
"componentName" => "input"
"attributes" => Illuminate\View\ComponentAttributeBag {...}
"blade" => Illuminate\View\InvokableComponentVariable {...}
"customization" => Illuminate\View\InvokableComponentVariable {...}
"ignoredParameterNames" => Illuminate\View\InvokableComponentVariable {...}
"classes" => TallStackUi\View\Components\Form\Input::classes(?Closure $callback = null): [...]
"slot" => Illuminate\View\ComponentSlot {...}
"__laravel_slots" => [...]
"livewire" => true
"property" => null
"error" => false
"id" => null
]
Although all the examples above are valid, they overwrite the original block classes by defining the second parameter of the block method, this is a way of doing a complete replacement of the original component classes by the blocks, an expected behavior when the soft customization was created. Luckily we have four special helpers to interact with the original classes by touching their content but preserving everything else. Let's take a look at an example:
use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize()
->form('input')
->block('input.base')
->replace('rounded-md', 'rounded-full');
 
// Or...
 
TallStackUi::customize()
->form('input')
->block('input.base')
->replace([
'rounded-md' => 'rounded-full',
'border-0' => 'border-1',
]);
}
}

Note that in the example above we omitted the second parameter of the block method, this way we can access four useful methods that allow us to touch the component's original classes in an easy way in order to make modifications while maintaining the rest of the original content.

All the four methods:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize()
->form('input')
->block('input.base')
// Replace: replace parts of the original content.
// Accepts:
// - single: from/to replace,
// - array for multiples replaces in the first parameter
->replace('rounded-md', 'rounded-full');
 
 
TallStackUi::customize()
->form('input')
->block('input.base')
// Remove:
// - single removal
// - an array for multiple removals
->remove('w-full');
 
TallStackUi::customize()
->form('input')
->block('input.base')
// Append: appends classes as string
->append('px-4');
 
TallStackUi::customize()
->form('input')
->block('input.base')
// Prepend: prepend classes as string
->prepend('py-4');
}
}

Now that these methods have been introduced, let's imagine that you want to transform all your inputs into a fully round style to follow the look of your application, so all the work (🥵) you need to do is:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize()
->form('input')
->block('input.base')
->replace('rounded-md', 'rounded-full');
}
}

While soft customization is powerful and easy to use, there is a catch: all soft customization are applied to all components, and you cannot assign specific customization to a component only once. However, just like in VueJS, where we have scoped CSS - CSS applied only to the component that defined the scope, soft customization offers the same concept of scoped customization - customization that will only be applied to the components that have the scope defined. Let's take a look at an example:

This is a normal Alert component

This is a fully round Alert component

Notice how one alert is normal while the other is fully rounded? This was only possible thanks to scoped soft customization, which instead of turning all alerts into rounded alerts, turned only the one that was defined with the circle scope. Now let's see how to achieve the same result as in the example above:

First, let's do the same soft customization via service provider:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
TallStackUi::customize('alert')
->scope('circle')
->block('wrapper')
->replace('rounded-lg', 'rounded-full');
 
// Or ...
 
TallStackUi::customize(component: 'alert', scope: 'circle')
->block('wrapper')
->replace('rounded-lg', 'rounded-full');
}
}

The difference is that we must instruct that customization to be applied to a scope - defined by a unique name, and as you can see above, there are two ways to define the scope name. Just choose one of them and use it as you wish.

You have three different ways to define scopes:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// 1
TallStackUi::customize('alert')
->scope('circle')
->block('wrapper')
->replace('rounded-lg', 'rounded-full');
 
// Or
TallStackUi::customize(component: 'alert', scope: 'circle')
->block('wrapper')
->replace('rounded-lg', 'rounded-full');
 
// Or
TallStackUi::customize()
->scope('circle')
->alert()
->block('wrapper')
->replace('rounded-lg', 'rounded-full');
}
}

Lastly and most importantly, we must apply the use of the scope to the components that are alert and that we want to receive the effects of the defined customization:

<x-alert text="This is a fully round Alert component" scope="circle" />

You can not set more than one scope in the same component.

Some TallStackUI components use other TallStackUI components internally. For example, the Color Picker renders an Input, and the Date Picker renders a Floating panel. With internal scoped customization, you can target and customize these internal instances without publishing Blade templates. The second parameter of the customize method accepts the scope name of the internal component:

use TallStackUi\Facades\TallStackUi;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Customize the input used inside the color picker
TallStackUi::customize(component: 'input', scope: 'form.color.input')
->block('input.base', 'rounded-full');
 
// Customize the floating used inside the date picker
TallStackUi::customize(component: 'floating', scope: 'form.date.floating')
->block('wrapper', 'shadow-2xl');
 
// Customize the badge used inside sidebar items
TallStackUi::customize(component: 'badge', scope: 'sidebar.item.badge')
->block('wrapper.class', 'border-0');
}
}

Below is the full reference of available scopes organized by parent component.

Wrapper Components

These scopes affect all form components that use the shared wrapper infrastructure.

Parent Child Scope
wrapper.input label wrapper.input.label
wrapper.input hint wrapper.input.hint
wrapper.input error wrapper.input.error
wrapper.radio error wrapper.radio.error

Form Components

Component Child Scope
Color input form.color.input
Color floating form.color.floating
Password input form.password.input
Password floating form.password.floating
Currency input form.currency.input
Date input form.date.input
Date floating form.date.floating
Time input form.time.input
Time floating form.time.floating
Time button form.time.button
Upload input form.upload.input
Upload label form.upload.label
Upload floating form.upload.floating
Upload error form.upload.error
Select Styled label form.select-styled.label
Select Styled input form.select-styled.input
Select Styled floating form.select-styled.floating
Select Styled hint form.select-styled.hint
Select Styled error form.select-styled.error
Select Native label form.select-native.label
Select Native hint form.select-native.hint
Select Native error form.select-native.error
Pin label form.pin.label
Pin hint form.pin.hint
Pin error form.pin.error

UI Components

Component Child Scope
Table select.styled table.select-styled
Table input table.input
Table checkbox table.checkbox
Dialog button (cancel) dialog.button
Dropdown floating dropdown.floating
Dropdown Submenu floating dropdown.submenu.floating
Clipboard label clipboard.label
Clipboard hint clipboard.hint
Sidebar Item badge sidebar.item.badge

Code highlighting provided by Torchlight