Core
Status: complete (v0.6.0).
Essential primitives for real pages: app chrome, forms, links, and field messaging. Render-only — no validation, routing, or JavaScript.
Scaffold (landmarks)
├─ header → AppBar
├─ aside → Sidebar (nav)
├─ body → MainContent (<main>)
└─ footer → Footer
Form (<form>)
└─ FieldGroup
├─ Label
├─ Input | PasswordInput | Textarea | Select | Checkbox | Radio | SwitchControl
├─ HelperText
└─ ErrorText
Link — first-class <a>
Divider — orientation + spacingAll widgets:
ThemeInterface / design tokensWidgetRegistryWidgetInterface + render pipelineScaffold::make()
->appBar(AppBar::make()->title('Dashboard'))
->sidebar(Sidebar::make(Link::make('Home')->href('/')->active()))
->body(MainContent::make(Text::make('Hello')->as('h1')))
->footer(Footer::make('© 2026 SERIXA'));Scaffold owns landmarks (<header>, <aside>, <footer>). AppBar / Sidebar / Footer supply inner content so landmarks are not doubled. MainContent always renders <main>.
Form::make()
->method('POST')
->action('/login')
->child(
Column::make([
Label::make('Email')->for('email'),
Input::make()->id('email')->name('email')->type('email'),
PasswordInput::make()->name('password'),
Checkbox::make()->name('remember')->label('Remember me'),
Button::make('Login')->primary()->type('submit'),
]),
);PHP reserves the keyword switch, so the widget class is SwitchControl. Registry name remains switch.
SwitchControl::make()->name('dark')->label('Dark mode')->checked();| Pattern | Approach |
|---|---|
| Labels | Label::for($id) + control id() |
| Help / errors | HelperText / ErrorText + describedBy('help-id error-id') |
| Invalid UI | ->invalid() sets aria-invalid (no validator) |
| Switch | role="switch" + aria-checked |
| Errors | ErrorText uses role="alert" |
| Active link | Link::active() → aria-current="page" |
Validation engines, form submission handling, routing, modals, toasts, tables, charts, reactivity, Avatar, DataTable, Pagination.