UI Components with DaisyUI
WPZylos scaffold comes with DaisyUI v5 + Tailwind CSS v4 pre-configured for building beautiful plugin UIs — both admin and frontend.
How It Works
Every WPZylos plugin gets a unique CSS prefix derived from the plugin name. This prevents style conflicts between plugins and with WordPress itself.
| Plugin Name | CSS Prefix | Example Class |
|---|---|---|
| First Class Dress Clothing | fcds- | fcds-btn, fcds-card |
| My Awesome Plugin | map- | map-btn, map-card |
| WooCommerce Bulk Editor | wbe- | wbe-btn, wbe-card |
Three-Layer CSS Isolation
Layer 1: DaisyUI Component Prefix
All DaisyUI component classes get your plugin's prefix:
@plugin "daisyui" {
prefix: "fcds-";
}
btn becomes fcds-btn, card becomes fcds-card, etc.
Layer 2: Tailwind Utility Prefix
Standard Tailwind utilities also get the prefix:
@import "tailwindcss" prefix(fcds-);
flex becomes fcds-flex, p-4 becomes fcds-p-4, etc.
Layer 3: Admin Root Scoping
Admin CSS variables are scoped to a container element, preventing WordPress dashboard conflicts:
@plugin "daisyui" {
prefix: "fcds-";
root: "#fcds-admin";
}
DaisyUI's CSS variables only apply inside #fcds-admin, not globally on :root.
File Structure
resources/
+-- css/
| +-- app.css # Frontend CSS (Tailwind + DaisyUI)
| +-- admin.css # Admin CSS (+ root scoping)
+-- js/
| +-- app.js # Frontend JS entry
| +-- admin.js # Admin JS entry
+-- views/
+-- admin/
| +-- settings.blade.php # Example admin page
+-- components/
+-- card.blade.php # Reusable card component
Build Pipeline
Development
npm install
npm run dev
Vite starts a dev server with hot module replacement.
Production
npm run build
Compiles CSS/JS to dist/ directory:
dist/
+-- css/
| +-- app.css
| +-- admin.css
+-- js/
+-- app.js
+-- admin.js
Usage in PHP Views
Admin Pages
Always wrap admin content in the root scope container:
<div id="fcds-admin" class="wrap">
<h1 class="text-2xl font-bold mb-6">Plugin Settings</h1>
<div class="fcds-card fcds-bg-base-100 fcds-shadow-xl">
<div class="fcds-card-body">
<h2 class="fcds-card-title">General Settings</h2>
<div class="fcds-form-control w-full max-w-md">
<label class="fcds-label">
<span class="fcds-label-text">API Key</span>
</label>
<input type="text" class="fcds-input fcds-input-bordered" />
</div>
<button class="fcds-btn fcds-btn-primary">Save</button>
</div>
</div>
</div>
Frontend Pages
No root scope needed for frontend — just use prefixed classes:
<div class="fcds-card fcds-bg-base-100 fcds-shadow-lg">
<div class="fcds-card-body">
<h3 class="fcds-card-title">Product Card</h3>
<p>Product description here.</p>
<div class="fcds-card-actions fcds-justify-end">
<button class="fcds-btn fcds-btn-primary">Add to Cart</button>
</div>
</div>
</div>
Available DaisyUI Components
All standard DaisyUI v5 components are available with your plugin's prefix:
| Component | Class | Description |
|---|---|---|
| Button | prefix-btn | Buttons with variants (primary, secondary, etc.) |
| Card | prefix-card | Content container with title, body, actions |
| Input | prefix-input | Text input fields |
| Textarea | prefix-textarea | Multi-line text input |
| Select | prefix-select | Dropdown select |
| Toggle | prefix-toggle | On/off switch |
| Checkbox | prefix-checkbox | Checkbox input |
| Tabs | prefix-tabs | Tab navigation |
| Modal | prefix-modal | Dialog/popup |
| Alert | prefix-alert | Alert messages |
| Badge | prefix-badge | Status badges |
| Table | prefix-table | Data tables |
| Navbar | prefix-navbar | Navigation bar |
| Drawer | prefix-drawer | Side panel |
| Menu | prefix-menu | Dropdown/context menu |
| Toast | prefix-toast | Notification toast |
| Loading | prefix-loading | Loading spinner |
| Progress | prefix-progress | Progress bar |
See the full list at daisyui.com/components.
Enqueuing Assets
The wpzylos-assets package handles asset enqueuing. In your AssetsServiceProvider:
public function boot(): void
{
$assets = $this->context->make(AssetManager::class);
// Admin styles and scripts
$assets->enqueueStyle('admin', 'dist/css/admin.css')
->onlyAdmin()
->onPage($this->context->slug());
$assets->enqueueScript('admin', 'dist/js/admin.js')
->onlyAdmin()
->onPage($this->context->slug());
// Frontend styles and scripts
$assets->enqueueStyle('app', 'dist/css/app.css')
->onlyFrontend();
$assets->enqueueScript('app', 'dist/js/app.js')
->onlyFrontend();
}
Theme Customization
Customize DaisyUI's theme colors in your CSS:
@plugin "daisyui" {
prefix: "fcds-";
}
@theme {
--color-primary: #4f46e5;
--color-secondary: #7c3aed;
--color-accent: #06b6d4;
--color-neutral: #1f2937;
--color-base-100: #ffffff;
--color-info: #3b82f6;
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #ef4444;
}
JavaScript Frameworks
WPZylos scaffold supports Vue.js 3 (default) and React 19 for building interactive admin and frontend UIs.
Vue.js (Default)
Vue is the default framework. The scaffold includes a ready-to-use Vue Options API component.
Admin App Example
<!-- resources/js/components/AdminApp.vue -->
<template>
<div>
<div class="fcds-card fcds-bg-base-100 fcds-shadow-xl">
<div class="fcds-card-body">
<h2 class="fcds-card-title">Settings</h2>
<input v-model="apiKey" class="fcds-input fcds-input-bordered" />
<button @click="save" class="fcds-btn fcds-btn-primary">Save</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AdminApp',
data() {
return { apiKey: '' };
},
mounted() {
if (window.myPluginData?.settings) {
this.apiKey = window.myPluginData.settings.apiKey || '';
}
},
methods: {
save() {
const formData = new FormData();
formData.append('action', 'myplugin_save_settings');
formData.append('nonce', window.myPluginData?.nonce || '');
formData.append('settings', JSON.stringify({ apiKey: this.apiKey }));
fetch(window.ajaxurl, { method: 'POST', body: formData });
},
},
};
</script>
Mounting Vue Apps
In resources/js/admin.js:
import '../css/admin.css';
import { createApp } from 'vue';
import AdminApp from './components/AdminApp.vue';
document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('my-plugin-admin-app');
if (el) {
createApp(AdminApp).mount(el);
}
});
In the PHP view:
<div id="fcds-admin" class="wrap">
<h1>Plugin Settings</h1>
<div id="my-plugin-admin-app"></div>
<script>
window.myPluginData = {
nonce: '<?php echo wp_create_nonce("myplugin_nonce"); ?>',
settings: <?php echo wp_json_encode($settings); ?>
};
</script>
</div>
React
React is included but commented out by default. To switch:
1. Update vite.config.js
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';
// import vue from '@vitejs/plugin-vue'; // Comment out Vue
import react from '@vitejs/plugin-react'; // Uncomment React
export default defineConfig({
plugins: [
tailwindcss(),
// vue(), // Comment out
react(), // Uncomment
],
});
2. Update resources/js/admin.js
import '../css/admin.css';
import React from 'react';
import { createRoot } from 'react-dom/client';
import AdminApp from './components/AdminApp.jsx';
document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('my-plugin-admin-app');
if (el) {
createRoot(el).render(<AdminApp />);
}
});
React Component Example
// resources/js/components/AdminApp.jsx
import { useState, useEffect } from 'react';
export default function AdminApp() {
const [apiKey, setApiKey] = useState('');
useEffect(() => {
if (window.myPluginData?.settings) {
setApiKey(window.myPluginData.settings.apiKey || '');
}
}, []);
const save = () => {
const formData = new FormData();
formData.append('action', 'myplugin_save_settings');
formData.append('nonce', window.myPluginData?.nonce || '');
formData.append('settings', JSON.stringify({ apiKey }));
fetch(window.ajaxurl, { method: 'POST', body: formData });
};
return (
<div className="fcds-card fcds-bg-base-100 fcds-shadow-xl">
<div className="fcds-card-body">
<h2 className="fcds-card-title">Settings</h2>
<input
value={apiKey}
onChange={e => setApiKey(e.target.value)}
className="fcds-input fcds-input-bordered"
/>
<button onClick={save} className="fcds-btn fcds-btn-primary">Save</button>
</div>
</div>
);
}
WordPress Data Passing
Pass data from PHP to Vue/React using wp_localize_script() or inline <script>:
// Method 1: wp_localize_script (recommended)
wp_localize_script('my-plugin-admin', 'myPluginData', [
'nonce' => wp_create_nonce('myplugin_nonce'),
'ajaxUrl' => admin_url('admin-ajax.php'),
'settings' => get_option('myplugin_settings', []),
]);
// Method 2: Using JsMount helper (from wpzylos-views)
$mount = $app->make(\WPZylos\Framework\Views\JsMount::class);
echo $mount->adminMount('my-plugin-admin-app', [
'nonce' => wp_create_nonce('myplugin_nonce'),
'settings' => get_option('myplugin_settings', []),
]);
Framework Comparison
| Feature | Vue.js 3 | React 19 |
|---|---|---|
| Default | Yes | No (opt-in) |
| Template syntax | HTML-like <template> | JSX |
| API style | Options API | Functional + Hooks |
| CSS class attr | class | className |
| Bundle size | ~33KB gzip | ~42KB gzip |
| Vite plugin | @vitejs/plugin-vue | @vitejs/plugin-react |