This guide is the first page to read before building or migrating an Evolution manager module with EvoUI. It turns the current consumer drift into rules that humans and agents can follow without opening a donor module and copying random markup.
Answer these questions before editing a consumer module:
| Question | Use | Do not use |
|---|---|---|
| Is this a full manager screen? | x-evo::layout plus local EvoUI assets |
legacy manager shell, Bootstrap, CDN UI assets |
| Is this a top-level module section? | x-evo::module-tab-shell or documented no-tabs UX |
copied tab markup and copied dirty-state modal |
| Is this repeated data? | evo-ui.module-table with provider config |
hand-built tables with local sorting/filter CSS |
| Is this a settings or model form? | evo-ui.form, x-evo::form, x-evo::settings-row |
ad hoc label grids and custom save buttons |
| Is this a dashboard summary? | x-evo::dashboard and x-evo::dashboard-card |
module-local card grids |
| Is this reorderable UI? | x-evo::reorder-rail, data-evo-dnd, option-list primitives |
custom drag handles, custom payloads, local DnD JS |
| Does add create an inline row/tab/card? | data-evo-inline-create, data-evo-inline-focus, evo-ui:inline-create.created |
local scrollIntoView, local focus scripts, always-visible duplicate add buttons |
| Is this resource-tab content inside Evolution edit page? | Embedded Resource Contract | full x-evo::layout or full asset partial |
| Is this task/progress/log UI? | operational primitive task in EvoUI first | widget-local progress bars and inline scripts |
If the right primitive does not exist, create a visible EvoUI backlog task before shipping a consumer-local workaround. Temporary bridge code must be scoped, named, tested and linked to the follow-up task.
Full manager modules use the EvoUI shell:
<x-evo::layout :title="__('vendor::global.title')">
<livewire:vendor.module-panel :active-tab="$activeTab" />
</x-evo::layout>dDocs is the documented exception: it uses a tree/sidebar and document viewer UX without upper module tabs. Do not force top tabs into documentation readers.
Embedded resource screens are another exception. sLang resource language tabs and sSeo resource SEO fields live inside an existing Evolution edit document. They must expose the embedded markers from the Embedded Resource Contract and must not render a second full manager shell.
Common commands must look identical across modules:
<x-evo::button
type="submit"
icon="check"
tone="primary"
variant="filled"
:label="__('evo::global.action_save')"
/>Rules:
- save:
check,primary,filled,evo::global.action_save; - add:
plus, usuallysuccess; - edit:
edit, usuallyprimary; - duplicate/copy:
copyorcopy-plus, usuallyinfo; - delete:
trash,danger; - dense row commands use
.evo-ui-row-actionsand.evo-ui-row-action.
Do not invent another green save button, another row action size, or another toolbar gap in a consumer module.
Use x-evo::module-tab-shell when switching sections can leave dirty forms:
<x-evo::module-tab-shell :tabs="$tabs" model="activeTab">
@if($activeTab === 'items')
<livewire:evo-ui.module-table preset="vendor.items" />
@elseif($activeTab === 'settings')
<livewire:evo-ui.form preset="vendor.settings" />
@endif
</x-evo::module-tab-shell>The shell owns active tab markup, unsaved-change prompts and the
window.EvoUI.form.waitForClean bridge. Consumers own only the tab config and
the business surface rendered in each tab.
Use evo-ui.module-table for list/table CRUD:
<livewire:evo-ui.module-table preset="vendor.redirects" />The provider/config owns columns, filters, sorting, pagination, typed cells, modal metadata, inline edit hooks, row actions, delete guards and reorder hooks. Do not create a custom table just to change spacing, header actions, pagination, or double-click behavior. Add the missing table primitive to EvoUI instead.
Use declarative forms for settings and model editors:
return [
'key' => 'vendor.settings',
'model' => 'config',
'fields' => [
['name' => 'enabled', 'type' => 'checkbox', 'label' => 'vendor::global.enabled'],
['name' => 'title', 'type' => 'text', 'label' => 'vendor::global.title'],
],
];Label alignment, help text, dirty state, save buttons, in-button saved feedback and field rendering are EvoUI responsibilities. Consumer modules own persistence, validation rules and domain semantics.
Use shared dashboard cards for summaries:
<x-evo::dashboard>
<x-evo::dashboard-card
icon="sitemap"
:title="$siteTitle"
span="6"
:stats="[['value' => $count, 'label' => __('vendor::global.ready')]]"
/>
<x-slot:body>
<livewire:evo-ui.module-table preset="vendor.recent-activity" />
</x-slot:body>
</x-evo::dashboard>The card grid, half-width spans and table spacing belong to EvoUI. Modules only provide data and labels.
Use the shared DnD contract for nested rows, settings builders and tables:
<div
class="evo-ui-dnd"
data-evo-dnd
data-evo-dnd-group-method="sortGroupByUid"
data-evo-dnd-item-method="sortItemByUid"
>
<section class="evo-ui-dnd-group-row" data-evo-dnd-group data-evo-dnd-uid="{{ $group['_uid'] }}">
<x-evo::reorder-rail />
<div class="evo-ui-dnd-list" data-evo-dnd-list data-evo-dnd-group-uid="{{ $group['_uid'] }}">
<div class="evo-ui-dnd-row" data-evo-dnd-item data-evo-dnd-uid="{{ $item['_uid'] }}">
<x-evo::reorder-rail />
</div>
</div>
</section>
</div>After an inline create action, the new row/tab/card should scroll into view and focus the primary input. Use the shared inline-create primitive:
<div class="evo-ui-inline-create" data-evo-inline-create="settings-config">
<div data-evo-inline-created="{{ $uid }}" data-evo-inline-create-id="{{ $uid }}">
<input data-evo-inline-focus wire:model.live="field.label">
</div>
<div class="evo-ui-inline-create-bottom" data-evo-inline-create-bottom hidden>
<x-evo::button icon="plus" tone="success" wire:click="addField" />
</div>
</div>window.dispatchEvent(new CustomEvent('evo-ui:inline-create.created', {
detail: { root: 'settings-config', id: uid }
}));data-evo-inline-create-bottom appears only when the page or configured root
overflows. Do not copy local scrollIntoView, focus() or duplicate-bottom-add
scripts into modules.
Use x-evo::modal, table form modals and delete confirmation modals. Modal
header, body spacing, footer alignment, close buttons and destructive action
placement are shared. Consumers provide title, fields, save/delete methods and
domain validation.
Do not place cards inside cards or add extra borders just because a modal looks empty. Prefer compact EvoUI modal body spacing and shared panel primitives.
sTask and dGramm need operational UI, not one-off widgets. Treat these as EvoUI primitive candidates:
- task runner panel;
- progress and status cells;
- log/code viewer;
- upload/drop-zone;
- command list;
- onboarding cards;
- dashboard cards;
- adapter boundary for third-party worker widgets.
Task execution, permissions, worker discovery, command allowlists and log contents remain consumer-owned. Progress bars, log chrome, runner layout and empty/loading/error states belong in EvoUI when reused.
Do not put conditional Blade directives inside complex opening tags:
{{-- Bad: can render raw text in older/embedded Blade compilation paths. --}}
<button @if($active) aria-current="page" @endif>
{{ $page }}
</button>Use whole-element branches or an attribute bag:
@if($active)
<button aria-current="page" class="is-active">{{ $page }}</button>
@else
<button>{{ $page }}</button>
@endif@php
$rowAttributes = new \Illuminate\View\ComponentAttributeBag([
'class' => $rowClass,
'wire:key' => $rowKey,
]);
if ($opensModal) {
$rowAttributes = $rowAttributes->merge(['data-evo-modal-dblclick' => $rowId]);
}
@endphp
<tr {{ $rowAttributes }}>This rule applies to pagination, table rows, list cards, onboarding actions and any markup that mixes Livewire, Alpine and conditional attributes.
Before closing a task, search the consumer diff for:
<style>blocks in manager views;style="..."used for shared layout;- Bootstrap, jQuery, Roboto, CDN UI assets or legacy manager bundles;
- module-local
.btn, tab, modal, card, table, DnD or progress CSS; - copied dirty-state or unsaved modal logic;
- inline conditional Blade attributes in opening tags;
- duplicated save/add/delete visual patterns;
- generic UI code living in a consumer instead of EvoUI.
If any item is present, either replace it with an EvoUI primitive or create a visible backlog task that explains why a temporary scoped bridge exists.