Composable, immutable PHP primitives for building UI components: alerts, breadcrumbs, dropdowns, navbars, toggles, and menu items.
Accessible by default, fluent API, framework-friendly data hooks, and rendering powered by html-core.
composer require ui-awesome/html-core-component:^0.4This package ships both abstract base classes (for subclassing) and ready-to-use concrete classes (Alert, Breadcrumb,
Dropdown, NavBar, Toggle, Item, Menu). The concrete classes can be used directly via ::tag() without any
subclassing.
The exposed abstractions are:
BaseAlert/Alertdismissible alerts with prefix/suffix containers and an optional toggle.BaseBreadcrumb/Breadcrumbaccessible breadcrumb navigation with active-path detection.BaseDropdown/Dropdowndropdown menus with toggles, dividers, and active-link wiring.BaseNavBar/NavBarnavigation bars with brand, menu, and collapsible toggle.BaseToggle/Togglebutton or link toggles with full data-attribute coverage (Bootstrap, Flowbite, Tailwind UI).
use UIAwesome\Html\Core\Component\Base\BaseBreadcrumb;
use UIAwesome\Html\Core\Component\Item;
final class Breadcrumb extends BaseBreadcrumb {}
echo Breadcrumb::tag()
->items(
Item::tag()->label('Home')->link('/'),
Item::tag()->label('Library')->link('/library'),
Item::tag()->label('Data')->active(true),
)
->currentPath('/library')
->render();use UIAwesome\Html\Core\Component\Base\BaseDropdown;
use UIAwesome\Html\Core\Component\Item;
final class Dropdown extends BaseDropdown {}
echo Dropdown::tag()
->items(
Item::tag()->label('Profile')->link('/profile'),
Item::tag()->label('Settings')->link('/settings'),
Item::tag()->divider(),
Item::tag()->label('Sign out')->link('/logout'),
)
->render();This example renders markup only. Connect the button to a menu target and provide the required client-side behavior.
use UIAwesome\Html\Core\Component\Base\{BaseNavBar, BaseToggle};
use UIAwesome\Html\Core\Component\{Item, Menu};
final class NavBar extends BaseNavBar {}
final class Toggle extends BaseToggle {}
echo NavBar::tag()
->brandText('My App')
->brandLink('/')
->brandToggle(Toggle::tag()->content('Menu'))
->menu(
Menu::tag()->items(
Item::tag()->label('Home')->link('/'),
Item::tag()->label('About')->link('/about'),
),
)
->render();NavBar::menuDefaultDefinitions() configures the menu passed to menu(), and Menu::dropdownDefaultDefinitions()
configures each nested dropdown, so a theme can supply a shared dropdown toggle and list classes.
A value the child sets itself wins over the default. A dropdown keeps its own toggle, toggleLabel, linkTag,
listItemTag, linkActiveClass, linkDisabledClass, and linkAriaCurrent. A menu keeps the same link and list-item
values plus its own listDropdownItemClass and dropdownDefaultDefinitions, which then replace the navbar definitions
as a whole. The hasOwn*() methods, such as hasOwnToggle() and hasOwnLinkTag(), report these choices; the remaining
defaults still apply.
toggleLabel() replaces only the content of the toggle that applies, whether the default or the dropdown's own, and
keeps its markup, classes, toggleContent() decoration, and icons. A non-empty label requires a BaseToggle; rendering
throws InvalidArgumentException when the toggle is raw markup or missing.
use UIAwesome\Html\Core\Component\{Dropdown, Item, Menu, NavBar, Toggle};
echo NavBar::tag()
->menuDefaultDefinitions(
[
'dropdownDefaultDefinitions' => [
[
'listClass' => 'dropdown-menu',
'toggle' => Toggle::tag()->class('dropdown-toggle')->content('Menu'),
],
],
],
)
->menu(
Menu::tag()->items(
Dropdown::tag()
->id('account')
->items(Item::tag()->label('Profile')->link('/profile'))
->toggleLabel('Account'),
Dropdown::tag()
->id('help')
->items(Item::tag()->label('Docs')->link('/docs'))
->toggle(Toggle::tag()->class('help-toggle')->content('Help')),
),
)
->render();The first dropdown relabels the default toggle; the second keeps its own toggle and still receives the list class:
Each item label can be wrapped in its own element (for styling or truncation) via labelTag/labelClass. Without
labelTag, the label renders as plain text.
use UIAwesome\Html\Core\Component\{Item, Menu};
use UIAwesome\Html\Interop\Inline;
echo Menu::tag()
->type('nav')
->linkClass('nav-link')
->linkActiveClass('is-active')
->items(
Item::tag()
->label('Request')
->link('/request')
->active()
->labelTag(Inline::SPAN)
->labelClass('nav-link-label'),
Item::tag()
->label('Logs')
->link('/logs')
->labelTag(Inline::SPAN)
->labelClass('nav-link-label'),
)
->render();Framework styling is supplied by a ThemeInterface implementation resolved through Config and applied with BaseTag::config(). The theme reads the ComponentContext to pick the recipe, so variants (danger, info, warning, ...) travel with the context instead of being hard-coded on the component.
use UIAwesome\Html\Core\Component\Alert;
use UIAwesome\Html\Core\Config\{Call, ComponentContext, Config, Cookbook, Recipe};
use UIAwesome\Html\Core\Theme\ThemeInterface;
final class AppTheme implements ThemeInterface
{
public function getName(): string
{
return 'app';
}
public function getRecipes(ComponentContext $context): iterable
{
if ($context->component !== 'alert' || $context->variant === null) {
return;
}
yield new Recipe(
'app.alert',
new Cookbook(new Call('class', "alert alert-{$context->variant}")),
);
}
}
$config = new Config(new AppTheme());
echo Alert::tag()
->config($config, new ComponentContext('alert', variant: 'danger'))
->content('Watch out!')
->render();config() applies recipes immediately. Call it before fluent setters that must remain local overrides.
For per-instance defaults that do not belong to a theme, pass them to the factory:
use UIAwesome\Html\Core\Component\Alert;
echo Alert::tag(['class' => 'alert alert-danger'])->content('Watch out!')->render();The component names, structural variants, and states this package defines are string-backed enums under
UIAwesome\Html\Core\Component\Values, so callers and themes reference a symbol that static analysis can check
instead of repeating string literals.
| Enum | Cases |
|---|---|
ComponentName |
ALERT, BREADCRUMB, DROPDOWN, ITEM, MENU, NAVBAR, TOGGLE |
ToggleVariant |
MENU (menu), MENU_DROPDOWN (menu-dropdown), SELECTOR_THEME (selector-theme) |
NavBarVariant |
ALIGN_RIGHT (align-right) |
DropdownVariant |
LANGUAGE (language) |
ComponentState |
DISMISSIBLE (dismissible) |
Pass the backing value to ComponentContext, and compare against it in a theme:
use UIAwesome\Html\Core\Component\Toggle;
use UIAwesome\Html\Core\Component\Values\{ComponentName, ToggleVariant};
use UIAwesome\Html\Core\Config\ComponentContext;
echo Toggle::tag()
->config($config, new ComponentContext(ComponentName::TOGGLE->value, variant: ToggleVariant::MENU->value))
->content('Menu')
->render();
// Inside ThemeInterface::getRecipes()
if ($context->component === ComponentName::TOGGLE->value && $context->variant === ToggleVariant::MENU->value) {
// ...
}Passing the enum cases directly to ComponentContext requires an ui-awesome/html-core version whose
ComponentContext accepts BackedEnum values. Alert color variants (danger, success, ...), sizes, and schemes
remain plain strings defined by each theme.
Note
The Bootstrap5 and Flowbite cookbooks were removed in 0.3.0. Flowbite is discontinued, and the Bootstrap 5
presets are being republished as standalone theme packages. See the Upgrade Guide for the migration.
For detailed testing, migration, and quality workflows.
