Class Map System

April 13, 2026 · View on GitHub

The Class Map is the core abstraction of this plugin. Every helper looks up its CSS classes from a flat key → class-string map, so the same helper code can emit DaisyUI, KTUI, or any custom Tailwind component library's markup just by swapping the map.

Resolution order

When the first helper method runs, the class map is resolved in this order:

  1. Load config/class_maps/daisyui.php as the base
  2. If Configure::read('TailwindUi.classMap') is a string, load that preset from config/class_maps/{name}.php and merge over the base
  3. If Configure::read('TailwindUi.classMap') is an array, merge those key/values over the current map (partial override)
  4. If Configure::read('TailwindUi.classMapOverrides') is an array, merge those on top (so you can combine a preset with extra overrides)

The resolved map is cached on the helper instance for the request.

Usage

Use a preset

// config/bootstrap.php
use Cake\Core\Configure;

Configure::write('TailwindUi.classMap', 'ktui');

Override individual keys

Configure::write('TailwindUi.classMap', [
    'form.input' => 'input input-bordered w-full text-base',
    'btn.primary' => 'btn-primary shadow-lg',
]);

Preset + overrides

Configure::write('TailwindUi.classMap', 'ktui');
Configure::write('TailwindUi.classMapOverrides', [
    'form.input' => 'kt-input kt-input-lg',
]);

Keys

The full list of keys is in config/class_maps/daisyui.php. Summary:

Forms

KeyPurpose
form.input`<input type="text
form.select<select> class
form.textarea<textarea> class
form.input.xs / .sm / .md / .lg / .xlInput size variants (['size' => 'lg'])
form.select.xs.xlSelect size variants
form.textarea.xs.xlTextarea size variants
form.checkbox<input type="checkbox"> class
form.radio<input type="radio"> class
form.switch<input type="checkbox"> class when 'switch' => true
form.fileFile input class
form.rangeRange input class
form.labelDefault field label class (used on single checkboxes and KTUI forms)
form.fieldsetOuter fieldset class in fieldset layout mode
form.fieldsetLegendLegend class in fieldset layout mode (empty string in KTUI)
form.helperLabeldaisyUI helper-label class (used for help text paragraphs)
form.validatordaisyUI 5 validator class added to inputs with errors
form.labelHorizontalLabel class in horizontal layout
form.helpTextHelp text block class
form.errorValidation error block class
form.inputErrorClass added to input when field has error
form.selectErrorClass added to select when field has error
form.textareaErrorClass added to textarea when field has error
form.containerContainer class for each control in vertical (non-fieldset) layout
form.containerHorizontalContainer class for each control in horizontal layout
form.checkboxLabelWrapperWrapper class for the single-checkbox label row
form.checkboxLabelInlineClass on the <label> that wraps a single checkbox + its text
form.inputGroupContainerWrapper class when prepend/append is used
form.inputGroupTextAddon (prepend/append) class

Buttons

KeyPurpose
btnBase <button>/submit class
btn.primary btn.secondary btn.neutral btn.accentColor variants
btn.success btn.danger btn.warning btn.infoSemantic color variants
btn.outline btn.soft btn.dash btn.ghost btn.linkStyle modifiers
btn.xs btn.sm btn.md btn.lg btn.xlSizes

Colors and modifiers stack freely: ['class' => 'soft primary'] emits btn btn-soft btn-primary. Sizes and modifiers don't suppress the default color — ['class' => 'ghost'] still gets btn-primary applied.

Custom keys added via TailwindUi.classMapOverrides are recognized automatically — but as modifiers, so the default color still applies on top (['class' => 'brand']btn btn-brand btn-primary). To make a custom key act as a standalone color, promote it via Configure:

Configure::write('TailwindUi.colorVariants', ['brand']);

Alerts / flash

KeyPurpose
alertBase alert class
alert.success alert.error alert.warning alert.info alert.defaultType variants

Badges

KeyPurpose
badgeBase badge class
badge.primary badge.secondary badge.neutral badge.accentColor variants
badge.success badge.danger badge.warning badge.infoSemantic color variants
badge.outline badge.soft badge.dash badge.ghostStyle modifiers
badge.xs badge.sm badge.md badge.lg badge.xlSizes

Same stacking rules as buttons: colors + modifiers compose, the default (secondary) only kicks in when no color keyword is present.

Pagination

KeyPurpose
paginationContainer class wrapping the page links
pagination.itemEach <a> class
pagination.activeAdditional class on the current page link
pagination.disabledAdditional class on disabled (first/prev on page 1)
KeyPurpose
breadcrumbsContainer class
breadcrumbs.itemEach <li> class
breadcrumbs.activeAdditional class on the last crumb

Icons

KeyPurpose
icon.tagHTML tag (svg, i, …)
icon.namespaceIcon set name (for the classes generated)
icon.prefixClass prefix (e.g. ki-filled for KTUI)
icon.sizeSize class (e.g. size-5)

In default SVG mode, the helper ships a small built-in path map for the icons used by the plugin/docs. For app-specific icons, pass a content option to HtmlHelper::icon() with the SVG path markup you want rendered.

Cards, tables

card, card.header, card.body, card.footer, card.title, table.

Preset file format

Presets may return either the legacy flat class-map array:

return [
    'form.input' => 'my-input',
    'btn' => 'my-btn',
];

…or the extended nested shape, which also allows overriding FormHelper's container templates:

return [
    'classMap' => [
        'form.input' => 'my-input',
        'btn' => 'my-btn',
    ],
    'templates' => [
        'inputContainer' => '<fieldset class="{{fieldsetClass}}">{{content}}{{help}}</fieldset>',
        'inputContainerError' => '<fieldset class="{{fieldsetClass}}">{{content}}{{error}}{{help}}</fieldset>',
        'label' => '<legend{{attrs}}>{{text}}</legend>',
        'inputHelp' => '<p class="{{helperClass}}">{{text}}</p>',
    ],
];

Both shapes are auto-detected. Use the nested shape when your framework's form idiom requires different wrapper markup (e.g. daisyUI 5 uses <fieldset> and <legend>, KTUI uses plain <div> and <label>).

Template overrides are ignored in horizontal alignment mode — horizontal layout always uses the plugin's built-in div-based templates because <legend> doesn't compose with a two-column flex layout.

Available form template keys

KeyUsed for
inputContainer / inputContainerErrorwrapper around text/select/textarea/file/date
radioContainer / radioContainerErrorwrapper around radio groups
multicheckboxContainer / multicheckboxContainerErrorwrapper around multicheckbox groups
labelthe label element rendered before the input (becomes <legend> in fieldset mode)
inputHelpthe help-text fragment rendered inside the container

Available placeholders inside container templates: {{content}}, {{help}}, {{error}}, {{containerClass}}, {{fieldsetClass}}, {{groupId}}, {{errorClass}}.

Adding a custom preset

Create config/class_maps/mypreset.php in the plugin directory (or in your app if you use a loader). Any keys you don't set fall back to the DaisyUI defaults. Activate it:

Configure::write('TailwindUi.classMap', 'mypreset');