Search by

wocody / wp-boat-ui

wocody

Shared Tailwind CSS v4 admin interface for Wocody WordPress plugins: layout, sidebar navigation, settings sections and form components.

Package info

github.com/wocody/wp-boat-ui

pkg:composer/wocody/wp-boat-ui

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-10-28 13:52 UTC

This package is auto-updated.

Last update: 2026-10-28 14:11:52 UTC


README

Shared WordPress admin interface for every Wocody plugin (Boat Backup, SEO, Cache, …). It gives each plugin the same screen layout — app bar with the brand mark and quick search, sidebar navigation, settings cards, right-hand column — and the same form controls, built with Tailwind CSS v4 and rendered by PHP.

Visual identity: Wocody orange #FC7900 on warm neutrals (ink), the system font stack at 14px, 10px control and 16px card radii, and Phosphor Icons (regular weight).

  • Server-rendered PHP components, escaped by default. No React, no build step in the plugins.
  • One prebuilt stylesheet (dist/boat-ui.css) and one dependency-free script (dist/boat-ui.js).
  • Scoped to .bui-root: WordPress admin styles outside the screen are untouched, and wp-admin CSS does not leak in.
  • Safe when several plugins bundle different copies: the newest version is elected and loaded once.
  • PHP 7.4+, WordPress 6.0+. English and pt_BR strings included.

Boat UI showcase

Installation

composer require wocody/wp-boat-ui

Load the bootstrap from the plugin's main file. Composer's autoloader does it too, but requiring the file directly keeps the plugin free of vendor/autoload.php at runtime:

require_once __DIR__ . '/vendor/wocody/wp-boat-ui/bootstrap.php';

Ship these runtime paths in the plugin ZIP: bootstrap.php, src/, dist/, languages/, LICENSE, NOTICE.

Quick start

use Wocody\BoatUI\Page;
use Wocody\BoatUI\UI;

add_action( 'admin_menu', function () {
	add_menu_page( 'Boat SEO', 'Boat SEO', 'manage_options', 'boat-seo', 'boat_seo_render', 'dashicons-search' );
} );

add_action( 'admin_enqueue_scripts', function ( $hook ) {
	if ( 'toplevel_page_boat-seo' === $hook ) {
		UI::enqueue(); // Stylesheet, script and the `bui-admin-page` body class.
	}
} );

function boat_seo_render() {
	Page::render( array(
		'brand'      => array( 'name' => 'Boat', 'product' => 'SEO', 'icon' => 'magnifying-glass', 'href' => '#general' ),
		'actions'    => UI::badge( 'v1.0.0', 'plain', array( 'dot' => true ) ), // End of the app bar.
		'router'     => true, // One [data-bui-view] visible at a time, chosen by the URL hash.
		'navigation' => array(
			array( 'label' => 'General', 'items' => array( // Group heading + links.
				array( 'label' => 'Site features', 'icon' => 'squares-four', 'view' => 'general', 'active' => true ),
				array( 'label' => 'Site basics', 'icon' => 'sliders-horizontal', 'view' => 'basics' ),
			) ),
			array( 'label' => 'Tools', 'icon' => 'wrench', 'href' => admin_url( 'admin.php?page=boat-seo-tools' ) ),
		),
		'content'    => array(
			'<form method="post" action="options.php" data-bui-view="general">'
				. UI::paper( array(
					'as'           => 'main',
					'title'        => 'Site features',
					'icon'         => 'squares-four',
					'description'  => 'Tell us which features you want to use.',
					'dirty_footer' => true, // The save bar appears only after a change.
					'content'      => UI::section( array(
						'title'   => 'Site info',
						'content' => array(
							UI::text_field( array( 'name' => 'boat_seo[name]', 'label' => 'Website name' ) ),
							UI::toggle_field( array( 'name' => 'boat_seo[tracking]', 'label' => 'Usage tracking', 'checked' => true ) ),
						),
					) ),
					'footer'       => UI::button( 'Save changes', array( 'type' => 'submit' ) ),
				) )
			. '</form>',
		),
		'aside'      => UI::aside_box( array( 'title' => 'Need help?', 'icon' => 'lifebuoy', 'content' => '<p>Read the docs.</p>' ) ),
	) );
}

Text arguments are escaped. Arguments documented as trusted HTML (content, footer, description, *_html, …) accept a markup string, a callable that echoes or returns markup, or a list of those; escape user data before passing it.

Components

All methods are static on Wocody\BoatUI\UI and return a string. Every component accepts id, class and attrs.

Area Methods
Layout Page::render() / Page::html(), paper() (with header icon), section(), divider(), card(), aside_box() (default or dark highlight), check_list()
Text title(), link(), code(), badge() (plain, primary, info, success, warning, error, upsell; dot), icon()
Actions button() (primary, secondary, tertiary, error, upsell; small … extra-large; href, icon, loading), button_group(), spinner()
Forms text_field(), textarea_field(), select_field(), checkbox(), checkbox_group(), radio_group() (incl. inline-block tiles), toggle(), toggle_field(), file_input(), disclosure()
Feedback alert(), banner(), progress(), progress_field(), tooltip(), skeleton()
Data table() (stacks on phones, keeps header associations), feature_group(), feature_list()
Overlays modal() (native <dialog>), toasts via JavaScript

Toggles are native checkboxes with role="switch", so they post like any other field. Validation is passed as 'validation' => array( 'variant' => 'error', 'message' => '…' ). Icons are Phosphor Icons in the regular weight; pass the name shown on the site (gear-six, cloud-arrow-up, …). Icon::names() lists all 1,512.

JavaScript API

dist/boat-ui.js wires the components automatically. It also exposes window.BoatUI:

BoatUI.toast({ title: 'Settings saved', description: 'Your changes are live.', variant: 'success' }); // success|error|warning|info
BoatUI.modal.open('my-modal'); BoatUI.modal.close('my-modal');
BoatUI.views.show('basics', { updateHash: true }); // when `router` is on; fires `bui:view`
BoatUI.refresh(container); // after inserting server-rendered components dynamically

Declarative hooks: data-bui-modal-open="id", data-bui-modal-close, data-bui-dismiss, data-bui-confirm="Are you sure?", and on forms data-bui-warn-unsaved. Dispatch bui:saved on a form after an AJAX save to hide its dirty footer.

Theming

The brand scale is orange, built around primary-500: #FC7900. White text on that orange fails WCAG contrast (2.7:1), so filled orange surfaces use dark text (primary-contrast: #12110F, 7.1:1) and orange text on white uses primary-700 (#A84E00, 5.6:1). Neutrals are the warm ink scale (ink-50 page background … ink-950 text). Status colours come from Tailwind (red, amber, green, blue).

Each plugin can re-theme its screen:

UI::enqueue( array( 'theme' => array(
	'primary-500'      => '#0a7cbd',
	'primary-600'      => '#086aa1',
	'primary-700'      => '#075a88',
	'primary-contrast' => '#ffffff',
) ) );

Tokens are CSS custom properties named --bui-color-{palette}-{shade}; radii are --bui-radius-control and --bui-radius-card.

Tailwind utilities

Utilities use the Tailwind v4 prefix syntax and are !important so they win over wp-admin styles:

<div class="bui:flex bui:items-center bui:gap-4 bui:text-ink-600 bui:md:grid-cols-2">…</div>

The prebuilt CSS contains the utilities used by the components plus the common set in resources/css/safelist.txt. Note the Tailwind v4 names: shadow-sm is v3's shadow, rounded-sm is v3's rounded. To use any other utility, build a plugin stylesheet that imports the package source and scans the plugin's templates, then enqueue it instead of the default one:

/* my-plugin/assets/admin.css */
@import "../vendor/wocody/wp-boat-ui/resources/css/boat-ui.css";
@source "../includes";
wp_deregister_style( UI::HANDLE );
wp_register_style( UI::HANDLE, plugins_url( 'assets/admin.build.css', __FILE__ ), array(), '1.0.0' );
UI::enqueue();

Upgrading from 1.x

2.0 replaces the visual identity. Breaking changes: icons are Phosphor names (e.g. cog → gear-six, chevron-down → caret-down); components use the ink neutrals instead of slate, and slate/gray/indigo now carry the Tailwind v4 default values; sidebar groups are headings instead of accordions (group icon/open are ignored, put icons on the links); sidebar classes are bui-nav__* and the active class is bui-is-active; mobile_title is gone (the app bar shows the brand).

Several plugins, one copy

Each bundled copy registers itself in bootstrap.php; the highest version is chosen the first time a Wocody\BoatUI class is used, and only that copy's classes and assets load. Keep packages within the same major version across plugins. Wocody_Boat_UI_Loader::versions() lists every registered copy for diagnostics.

Showcase

Add define( 'WOCODY_BOAT_UI_DEMO', true ); to wp-config.php on a development site and open Tools → Boat UI for every component, including sample "Site features" and "Site basics" settings screens.

Development

npm install
npm run build        # dist/boat-ui.css (Tailwind CLI, minified) and dist/boat-ui.js
npm run watch        # rebuild CSS on change
npm run icons        # regenerate src/icons.php from Phosphor Icons
composer install
composer test        # dependency-free PHP smoke tests
composer standards   # WordPress Coding Standards + PHP 7.4 compatibility

dist/ is committed so that plugins never need Node. Releases follow SemVer: bump the version in bootstrap.php, rebuild, update CHANGELOG.md, then tag vX.Y.Z and push; Packagist picks the tag up.

License

GPL-2.0-or-later. Bundles Phosphor Icons and compiled Tailwind CSS (both MIT); see NOTICE.