Appearance
Menus & Widgets
Introduction
In WordPress a menu location or a widget area exists only once the theme declares it in functions.php — register_nav_menus(), register_sidebar() — so a new place for a menu, or a new column of widgets, is a code change and a deploy. In kitloom both are managed in the admin: a location is added on the Menu Locations screen, a widget zone on Appearance → Widgets, and each is there the moment it is saved — named, assigned a menu or filled with widgets, read by the theme and by every JSON client. No code, no deploy, no theme to edit.
What the theme does is draw: it asks for a location's menu or a zone's widgets by id. A theme may still declare the places it needs, so a fresh site has them; it does not have to.
Menu locations
A location is a place the site shows a menu in — the header, the footer, a social strip. The Menu Locations screen (Filament Appearance → Menu Locations, Nova Appearance → Menus → Menu Locations on the Nova skeleton; the package alone puts it in a Menus section) has two tabs:
- Assigned Menus — one select per location, as WordPress's "Manage Locations": which menu is in it.
- Locations — the locations themselves. Add location asks for an id (lower case letters, digits,
-and_; it cannot be changed later) and a name. A location can be renamed and removed; removing one releases its menu, and the menu itself stays.
Where locations come from, in this order:
- The theme —
kitloom-navigation.locationsin config, and what the theme registers inbootTheme()with$this->app->make(MenuLocations::class)->register(['studio' => 'Studio header']), asregister_nav_menus(). These are shown, named by the theme and cannot be removed; the admin says Declared by the theme next to them. - The admin — the locations added on the Locations tab (the
kitloom_menu_locationsoption). A name given here also wins over the theme's. - The data — the keys
nav_menu_locationsalready holds. A site brought over from WordPress shows itsprimary,footer,menu-1before anybody declares them, named from the key until someone names them.
Every location, whatever its source, takes a menu on the Assigned Menus tab and in the menu form's Display location. The menu's items are edited on Menus.
Widget zones
A zone (WordPress's sidebar) is a column, a footer, a strip that holds widgets. Appearance → Widgets (Nova on the Nova skeleton; the package alone puts it under Settings) has a tab per zone, with its widgets in order and each one's form, and a last tab, Zones, where Add zone asks for an id and a name; a zone can be renamed and removed, and the widgets of a removed zone go to the inactive widgets, as in WordPress.
Where zones come from, in this order:
- The configuration —
kitloom-wp-schema.sidebars, a list of ids. - The theme — what it registers in
bootTheme()with$this->app->make(SidebarRegistry::class)->register(['journal-sidebar' => 'Sidebar']), asregister_sidebar(). Like the configured ones, these cannot be removed in the admin. - The admin — the zones added on the Zones tab (the
kitloom_sidebarsoption). - The data — the keys of
sidebars_widgets: a site brought over from WordPress shows its sidebars before anybody declares them.
The widgets themselves are the ones WordPress keeps (sidebars_widgets, widget_<type>): text and custom HTML, the standard widgets of the Widgets module, a theme's own types, and block widgets.
Showing them in a theme
The theme reads locations and zones by id through the page parts, so a location or zone added in the admin is drawn the moment the theme asks for it:
| Part | Key | Gives | |
|---|---|---|---|
| Which menu is in a location | Kitloom\Navigation\Parts\Locations | menuLocations | menu('header') — the menu's slug, or null when the location has none |
| The menus | Kitloom\Navigation\Parts\Menus | menus | get('main') — the menu's name and its items as a tree, or null; built when the theme first asks for that menu; in JSON and Vue props, the menus in the locations |
| A zone's widgets | Kitloom\Site\Page\Parts\Widgets | widgets | in('promo') — each widget's id, type, title and drawn content |
blade
@php($slug = $page->part(\Kitloom\Navigation\Parts\Locations::class)->menu('header'))
@php($items = $slug !== null ? ($page->part(\Kitloom\Navigation\Parts\Menus::class)->get($slug)['items'] ?? []) : [])
@foreach ($page->part(\Kitloom\Site\Page\Parts\Widgets::class)->in('promo') as $widget)
<section class="widget">{!! $widget['content'] !!}</section>
@endforeachjs
// A Vue theme: the same parts as props.
const menu = (location) => page.props.menus?.[page.props.menuLocations?.[location]]?.items ?? [];
const widgets = (zone) => page.props.widgets?.[zone] ?? [];A zone also goes into any text with the [sidebar id="promo"] shortcode, and one widget with [widget id="custom_html-4"] — see Shortcodes. In the page's JSON the same data is under menuLocations, menus and widgets; see Headless & APIs.
A site builder therefore adds a location or a zone in the admin and names it in the theme's template; a generated theme (Generating a Theme) comes with a header and a footer-links location, a social one with --with=social, and two zones, a sidebar and the footer, and draws a zone only while it has widgets.
AI agents
With AI agents installed, an agent whose user has edit_theme_options manages both as the admin does: menu_save puts a menu in its locations, and widgets_save sets a zone's widgets — and naming a zone the site does not have, with a label, adds it.
Next
- Writing a Theme — a theme's
bootTheme(), where it declares the places it needs. - Templates — every page part.
- Widgets — the standard widgets and blocks.