fabianmichael/kirby-template-attributes

Better HTML attribute handling for your snippets and templates.

Maintainers

Package info

github.com/fabianmichael/kirby-template-attributes

Type:kirby-plugin

pkg:composer/fabianmichael/kirby-template-attributes

Fund package maintenance!

fabianmichael

Statistics

Installs: 6 655

Dependents: 1

Suggesters: 0

Stars: 24

Open Issues: 1

2.3.3 2026-03-14 06:03 UTC

This package is auto-updated.

Last update: 2026-03-14 06:04:45 UTC


README

Better attribute API for snippets and templates

This plugin brings Vue.js/Laravel-Blade-like attribute composition to the templates of your Kirby project. This is an exploration in search of better HTML attribute handling for nested snippets and components.

Note

This plugin is completely free and published under the MIT license. However, if you are using it in a commercial project and want to help me keep up with maintenance, please consider to ❤️ sponsor me for securing the continued development of the plugin.

Requirements

  • Kirby 4/5 (use version 1.x for Kirby 3 installations)
  • PHP 8.3 (because of Enumeration support)

Installation

The recommended is installation via composer.

composer require fabianmichael/kirby-template-attributes

Alternatively, you can also download the plugin and install it manually by copying it to the site/plugins/ folder of your website.

Usage

Basic usage

Use the attributes() helper for generating a string of attributes:

<button <?= attributes([
  'role' => 'button',
  'aria-expanded' => 'false',
]) ?>>[…]</button>

You can also use named arguments if you prefer a leaner syntax. Be aware, that this only works as long as you don’t have dashes in your attribute names:

<img <?= attributes(
  class: 'icon',
  width: 16,
  height: 16,
  src: $image->url(),
	alt: 'The funniest donkey ever!',
) ?>>

Or if all you have is an attributes string, you can also feed the that to the attributes() helper:

<?php

// get image dimensions as height="yyy" width="xxx" 
$src  = 'img.png';
$size = getimagesize($src)[3];

?>

<img <?= attributes($size)->merge([
	'src' => $src,
	'alt' => '',
]) ?>>

⚠️ If you need XML-compatible attributes, always call $attributes->toXml() instead of just echoing the Attributes object, because otherwise all attributes will be converted to lower-case.

In many cases, you need to set different classes. The classes() helper is a nice shortcut for improved readability:

<button <?= classes([
  'button',
  'button--red' => $color === 'red', // class will only appear in class attribute, if condition is true
]) ?>>[…]</button>

The classes() helper is pretty flexible and also accepts multiple paramaters, each of those can eithe be a string or array (but please ensure to write readible code anyways):

<button <?= classes('button', [
  'button--red' => $color === 'red',
], 'absolute', 'top-0 left-0') ?>>[…]</button>

Merging attributes

# site/snippets/button.php

<button <?= attributes([
  'class' => 'button',
  'role' => 'button',
  'aria-expanded' => 'false',
  'style' => '--foo: bar',
])->merge($attr ?? []) ?>>[…]</button>

# site/templates/default.php

<?php snippet('button', [
  'attr' => [
    'role' => 'unicorn', // attributes can be overridden
    'onclick' => 'alert("everyone likes alerts!!!")',
    'class' => 'absolute top-0 left-0
      md:left-4
      xl:left-8', // classes are automatically appended to the existing attribute value and surplus whitespace is trimmed
    'style' => '--bar: foo', // style attribute value is also appended to the original value
  ],
]) ?>

You can also use magic invoke method by calling the Attributes object like a function. This is basically syntactic sugar for passing-down attributes the snipept include chain:

<?php

// some parameter passed to the base button snippet for
// overriding the button type
$attr = attributes([
	'type' => 'submit' 
]);

?>

<button <?= $attr([
	'type' =>  'button'
]) ?>>[…]</button>

This is the same as:

<?php

$attr = attributes([
	'class' => 'submit'
]);

?>

<button <?= attributes([
	'type' =>  'button'
])->merge($attr) ?>>[…]</button>

Output is:

<button type="submit">[…]</button>

Before/After

You can set $before and $after, just like for Kirby’s Html::attr() helper by using the corresponding methods:

attributes(class: 'foo')->before(' ');
attributes(class: 'foo')->after(' ');

Examples

A button component exists as a snippet in site/snippets/button.php:

<button class="button"><?= html($text ?? 'Button text') ?></button>

A common situation would be the requirement to add attributes when calling the snippet('button') helper class, e.g. class, data-*, title, aria-* etc. Developers cannot handly every possible attribute for each component. The previous attributes() helper could help here:

<button <?= attributes($attr ?? []) ?> class="button"><?= html($text ?? 'Button text') ?></button>

This works better, but we still cannot extend the class attribute easily. Enter the new attributes() helper:

<button <?= attributes([
    'class' => 'button',
])->merge($attr ?? []) ?>><?= html($text ?? 'Button text') ?></button>

Even shorter:

<button <?= classes('button')->merge($attr ?? []) ?>><?= html($text ?? 'Button text') ?></button>

This becomes even cooler, because the classes can be assigned conditionally as an array:

<?php

$text ??= 'Button text';
$size ??= 'normal';
$theme ??= null;
$attr ??= [];

?>
<button <?= attributes([
    'role' => 'button',
    'style' => [
      'font-size: 2rem;' => ($size === 'large'),
    ],
])->class([
    'button',
    "button--{$size}",
    "button--{$theme}" => $theme, // will only be merged, if $theme is trueish
])->merge($attr) ?>><?= html($text) ?></button>

This is already cool and makes working with attributes for snippets much easier, e.g. is we use the button in site/snippets/menu.php:

<nav class="menu">
    […]
    <?php snippet('button', [
        'text' => 'Toggle Menu',
        'attr' => [
            'class' => 'menu__button',
            'aria-controls' => 'menu-popup',
            'aria-expanded' => false,
            'role' => 'teapot', // overrrides the default attribute
        ],
    ]) ?>
</nav>

License

MIT (but you are highly encouraged to ❤️ sponsor me, if this piece of software helps you to pay your bills).