Thus far, we have created fairly basic pattern definitions with simple context data attached. To remind you, here’s our basic sample component:

1
2
3
4
5
6
7
8
{{-- /resources/views/components/button.blade.php --}}
<button
    {{ $attributes->class([
        "border rounded-md border-sky-400 shadow-md ..."
    ]) }}
>
    {{ $slot }}
</button>

And here’s a sample pattern definition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// tests/Patterns/Components/Button.php
namespace Tests\Patterns\Components;

use Illuminate\View\View;
use njpanderson\Braid\Base\ComponentPattern;
use njpanderson\Braid\Contracts\PatternContext;

class Button extends ComponentPattern {
    public function contextData(string $context): PatternContext|View
    {
        return $this->makeContext(
            slot: 'Button'
        );
    }
}

That’s all well and good, but what if our component is a little more complex? like the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{{-- /resources/views/components/card.blade.php --}}
@props(['title', 'image', 'link', 'link-text' ])

<div
    {{ $attributes->class([
        'w-full group mx-auto shadow-lg rounded border hover:border-blue-400 bg-slate-100 hover:bg-blue-100'
    ])}}
    {{ $attributes }}
>
    @if (isset($image))
        <picture class="w-full">
            <source srcset="{{ $image }}"/>
            <img class="w-full h-[150px] object-cover opacity-80 group-hover:opacity-100" src="{{ $image }}"/>
        </picture>
    @endif

    <h2 class="pt-4 px-4 font-semibold text-lg">
        <a href="#" class="group-hover:text-blue-600">{{ $title }}</a>
    </h2>

    <div class="px-4 pb-4">
        {!! $slot !!}

        @if (isset($link))
            <p class="mt-2 flex gap-x-2 items-center justify-end">
                <a href="{{ $link }}" class="hover:underline">{{ $linkText ?? $title }}</a>
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>
            </p>
        @endif
    </div>
</div>

How do we test this component?

Without a pattern library solution, you could perhaps make a “showcase” style template within your Laravel app, but it would get clunky fast, be situated inside other templates, and you still wouldn’t get useful reviewing features such as a status log or responsive layout switching1.

Siloing of patterns and context capability is where Braid really shines. ☀️

Using contexts

One of Braid’s useful features is to allow component developers to define various views, or contexts, of their components using a pattern definition. This means you can show your components off in various ways, all within the pattern library, without making material changes to the component itself.

Defining contexts

Each and every Braid pattern comes with a default context, appropriately named default. Braid doesn’t show you this one; it comes for free with the base pattern class and when you load a pattern inside Braid, the default context is shown first.

If you’d like to follow along with your own code, you can copy the above card component and place it at /resources/views/components/card.blade.php within your project. If this collides with an existing component, feel free to use a different name — just remember to either rename your pattern or define $viewName accordingly.

You can then use the following command to create an associated pattern at Tests\Patterns\Components\Card in artisan:

1
php artisan braid:make:pattern Components/Card --type=component

To define more contexts in your pattern definition, we can add to an array named $contexts:

1
2
3
4
5
6
7
8
9
10
    /**
     * Pattern contexts
     *
     * @var array
     */
    protected $contexts = [
        'no-image',
        'no-link',
        'long-description'
    ];

Context labels are normally generated from the name. You can customise the label by providing a key and value pair instead of just the value (e.g. $contexts = [ 'no-image' => 'No Image!', ...]). The key will be used to generate the URL and identifier, the value will be used verbatim for the label.

This will show up within Braid as a list of links beneath the main pattern:

Context options underneath a pattern

And clicking on each of them will… Do absolutely nothing. 🤨

You might even be treated to a template error if you’ve already defined the component from the example above. Don’t worry, this is to be expected! Read on…

Defining context data

Now we need to define the actual context data with each named context.

How you do this is up to you, but my usual approach is to use a switch, combined with the built in method named makeContext.

Let’s define a baseline context first. This will return the same data regardless of the context required, but it’s a good starting point:

1
2
3
4
5
6
7
8
9
10
11
    public function contextData(string $context): PatternContext|View
    {
        return $this->makeContext(
            attributes: [
                'image' => 'https://picsum.photos/600/150',
                'title' => 'About us',
                'link' => '/about-us'
            ],
            slot: '<p>' . fake()->sentence(rand(6, 15)) . '</p>'
        );
    }

There’s a few things going on here already:

  1. $this->makeContext is a built in method which returns an instance of njpanderson\Braid\Contracts\PatternContext. This (or a view) is a required return value from makeContext.
  2. makeContext supports three arguments2, two of which are being used here:
    1. attributes defines a simple array of attributes, which, in the context of a Laravel component, would be sent to the component as html-like attributes.
    2. slot defines the slot data for a component. Usually expressed on the component side with {{ $slot }}
    3. Scoped slots are also supported, but let’s not get into that here. See the Patterns reference page for more information.
  3. An instance of faker (fake()) is also being utilised to provide a random sentence. Due to the dynamic nature of PHP, we can write complex logic to provide context information as needed, without having to manually edit the patterns each time.

Adding the remaining contexts

Great start, but we’re only defining a single context at the moment, and our poor little $context argument is going completely unused. Let’s improve on that:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    public function contextData(string $context): PatternContext|View
    {
        switch ($context) {
        case 'no-image':
            return $this->makeContext(
                attributes: [
                    'title' => 'About us',
                    'link' => '/about-us'
                ],
                slot: '<p>' . fake()->sentence(rand(6, 15)) . '</p>'
            );

        case 'no-link':
            return $this->makeContext(
                attributes: [
                    'image' => 'https://picsum.photos/600/150',
                    'title' => 'About us'
                ],
                slot: '<p>' . fake()->sentence(rand(6, 15)) . '</p>'
            );

        case 'long-description':
            return $this->makeContext(
                attributes: [
                    'image' => 'https://picsum.photos/600/150',
                    'title' => 'About us'
                ],
                slot: '<p>' . fake()->sentence(rand(25, 40)) . '</p>'
            );

        default:
            return $this->makeContext(
                attributes: [
                    'image' => 'https://picsum.photos/600/150',
                    'title' => 'About us',
                    'link' => '/about-us'
                ],
                slot: '<p>' . fake()->sentence(rand(6, 15)) . '</p>'
            );
        }
    }

This example defines the data for all three contexts. no-image, no-link, and long-description, plus the default context as before.

Now, when viewing the component in those three different contexts, it will take on unique appearances:

Pattern context 1 Pattern context 2 Pattern context 3
Three pattern contexts.

And in all four cases, the component file itself remains the same.

Checking context data

If you’re implementing patterns from the pattern library, the knowing what attributes, slots etc the pattern needs can come in handy. You can see the context data in the pattern tools panel at the bottom of every pattern display:

In fact, this tab can show all kinds of data, such as booleans, arrays, nested data and even contains syntax highlighting for your slot payloads. Experiment with it and see what works!

For more information, see the Patterns reference page.

Up next: Using views

  1. Some developer tools can do this, but do (or should) your stakeholders, designers and account managers know how to use them? 

  2. Don’t recognise the argument syntax here? It’s part of PHP’s named arguments feature.Â