Soft Personalization
The soft personalization.
The soft personalization involves personalizing components at runtime, either through a service provider like AppServiceProvider or object classes. The idea behind soft personalization is to explore the building blocks of personalization for each component. Even if you are starting with Laravel, with a little attention to the docs below, you will be able to fully personalize the components using this concept.
Since the idea of soft personalization is to apply personalization 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 tailwind.config.js
file by inserting this content:
content: [ // If you are personalizing into service providers... './app/Providers/*.php', // If you are using object invokable classes... // Don't worry! You'll understand it below! './app/NameSpaceGoesHere/**/*.php', ],
Now that you havve 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::personalize() ->form('input') ->block('input.base', 'w-full rounded-full'); // or... TallStackUi::personalize('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
Personalize: 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.
Since soft personalization was created to be easy to use, just like Pest,
the soft personalization offers a concept of fluency when using the and
like a property or method.
The idea behind this approach is to personalize more than one component at the same time.
use TallStackUi\Facades\TallStackUi; class AppServiceProvider extends ServiceProvider{ public function boot(): void { // 1. Property TallStackUi::personalize() ->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::personalize() ->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 personalize one block at a time or all of them at once:
use TallStackUi\Facades\TallStackUi;use App\TallStackUi\InputPersonalization; class AppServiceProvider extends ServiceProvider{ public function boot(): void { TallStackUi::personalize() ->form('input') ->block('input.base', new InputPersonalization()) ->block('icon.wrapper', fn (array $data) => 'px-4 py-2') ->block('icon.paddings.left', 'pl-10'); // Or ... TallStackUi::personalize() ->form('input') ->block([ 'input.class' => new InputPersonalization(), '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 personalization also allows you to make your personalization 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\InputPersonalization; class AppServiceProvider extends ServiceProvider{ public function boot(): void { TallStackUi::personalize() ->form('input') ->block('input.base', new InputPersonalization()); }}
Personalizing:
// You must track this namespace in the TailwindCSS config file!namespace App\TallStackUi; class InputPersonalization{ 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 {...} "personalization" => 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]
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 personalization was created. Luckily we have an easy way 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::personalize() ->form('input') ->block('input.base') ->replace('rounded-md', 'rounded-full'); // Or... TallStackUi::personalize() ->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::personalize() ->form('input') ->block('input.base') // Replace: replace parts of the original content. // Accepts: single replace or an array for multiples replaces ->replace('rounded-md', 'rounded-full') // Remove: single removal or an array for multiple removals ->remove('w-full') // Append: appends classes as string ->append('px-4') // 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::personalize() ->form('input') ->block('input.base') ->replace('rounded-md', 'rounded-full'); }}
While soft personalization is powerful and easy to use, there is a catch: all soft personalization are applied to all components, and you cannot assign specific personalization 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 personalization offers the same concept of scoped personalization - personalization 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 personalization, 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 personalization via service provider:
use TallStackUi\Facades\TallStackUi; class AppServiceProvider extends ServiceProvider{ public function boot(): void { TallStackUi::personalize('alert') ->scope('circle') ->block('wrapper') ->replace('rounded-lg', 'rounded-full'); // Or ... TallStackUi::personalize('alert', scope: 'circle') ->block('wrapper') ->replace('rounded-lg', 'rounded-full'); }}
The difference is that we must instruct that personalization 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.
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 personalization:
<x-alert text="This is a fully round Alert component" scope="circle" />