Invoke when touching an app's layout, favicons, web manifest, theme color, logo, page title, or the shell (sidebar, navbar, auth and portal layouts) in a Nyumbani app. Skip for component-level styling.
php artisan boost:add-skill nyumbani/ui
Save this content to: .claude/skills/nyumbani-branding/SKILL.md
# Nyumbani Branding and Layouts
The package owns the look and the shell. An app extends it and only supplies content and its sidebar.
## Layouts
- `nyumbani::layouts.app`: the base document. Includes favicons, the manifest, the theme-color meta, `@vite`, toasts (`nyumbani::partials.alerts`) and the `styles` and `js` stacks.
- `nyumbani::layouts.portal`: signed-in shell with sidebar and navbar. Sections: `title`, `content`, `portal.content` (optional, see below), `banner`, `sidebar`, and the `actions` stack.
- `nyumbani::layouts.auth`: authentication pages only (sign-in, forgot password, reset password), which the UI package already ships. Sections: `heading`, `description`, `content`. Not for other public pages.
- `nyumbani::layouts.error`: error pages, see the nyumbani-error-pages skill.
Avoid repeating the sidebar include on every page. Create one app layout and extend it from every page:
```blade
@extends('nyumbani::layouts.portal')
@section('sidebar', 'partials.sidebar')
```
The `sidebar` section takes the name of a view and the portal renders it; markup written inline in the section works too. A portal page with no sidebar throws a `LogicException`, and so does a name that is not a view.
## Favicons and manifest
- Never add favicon files or a favicons partial to an app. The shared set, the dark Nyumbani logo on an amber tile at each size, lives in `resources/images/favicons/` of the package and is served by the `nyumbani.favicon` route. The layout links them inline.
- The manifest comes from `route('nyumbani.manifest')` (`WebManifestController`). It uses the app name and `ui.branding.theme_color`.
- Change the theme color with `UI_THEME_COLOR` or the published config, never in the layout.
- To replace an icon for one app, publish the package images, do not edit `vendor`.
## Theme
Import the shared theme once in the app's css, in place of `@import 'tailwindcss'`. The theme imports Tailwind and loads the `@tailwindcss/forms` plugin, which the package bundles in `resources/dist/forms.js`, so an app has no `@plugin` line and installs only the `tailwindcss` npm package. `nyumbani:install` swaps an existing Tailwind import and forms plugin line for the theme import.
```css
@import '../../vendor/nyumbani/ui/resources/css/theme.css';
```
Use the tokens `primary`, `primary-dark`, `primary-darker` and `secondary*`. Do not hard-code hex colors for brand surfaces.
## Rules
- Use `@use` for any class in a view and never `@php`.
- Page titles come from `@section('title', ...)`. The layout appends the app name.
## Extending the layouts
There are four, and the Layouts page of the UI docs shows the code that extends each one:
- `nyumbani::layouts.portal`: signed-in pages. Sections `title`, `content`, `sidebar`, and optionally `portal.content`, `brand`, `navbar`, `user-menu`, `banner`; `@push('actions')` for page buttons. Define the sidebar once: the app's `layouts/app.blade.php` extends the portal and sets `@section('sidebar', 'partials.sidebar')`, the partial holds the `nav-link` and `nav-group` items, and every page extends `layouts.app` (never the portal) and fills only its own sections.
- `nyumbani::layouts.auth`: authentication pages only (sign-in, forgot password, reset password), which the UI package already ships; use `nyumbani::layouts.app` for any other public page. Sections `heading`, `description`, `content`.
- `nyumbani::layouts.error`: error pages. Sections `code`, `heading`, `detail`, `icon`, `actions`.
- `nyumbani::layouts.app`: the base document the others extend. Section `title`, `body`, `body-class`; stacks `styles` and `js`.
## Wrapping every page (portal.content)
The portal draws `portal.content` when the app layout defines it, and the page's `content` otherwise. Define it in `layouts/app.blade.php` to put every page on a card, and let a page opt out with another section the layout checks for:
```blade
@extends('nyumbani::layouts.portal')
@section('sidebar', 'partials.sidebar')
@section('portal.content')
@hasSection('content.plain')
@yield('content.plain')
@else
<div class="bg-white p-2 shadow-xs">
@yield('content')
</div>
@endif
@endsection
```
A page then fills `content` to sit on the card, or `content.plain` to sit straight on the portal. Without `portal.content`, pages fill `content` and the portal draws it as it is.
The package owns the look and the shell. An app extends it and only supplies content and its sidebar.
nyumbani::layouts.app: the base document. Includes favicons, the manifest, the theme-color meta, @vite, toasts (nyumbani::partials.alerts) and the styles and js stacks.nyumbani::layouts.portal: signed-in shell with sidebar and navbar. Sections: title, content, portal.content (optional, see below), banner, sidebar, and the actions stack.nyumbani::layouts.auth: authentication pages only (sign-in, forgot password, reset password), which the UI package already ships. Sections: heading, description, content. Not for other public pages.nyumbani::layouts.error: error pages, see the nyumbani-error-pages skill.Avoid repeating the sidebar include on every page. Create one app layout and extend it from every page:
@extends('nyumbani::layouts.portal')
@section('sidebar', 'partials.sidebar')
The sidebar section takes the name of a view and the portal renders it; markup written inline in the section works too. A portal page with no sidebar throws a LogicException, and so does a name that is not a view.
resources/images/favicons/ of the package and is served by the nyumbani.favicon route. The layout links them inline.route('nyumbani.manifest') (WebManifestController). It uses the app name and ui.branding.theme_color.UI_THEME_COLOR or the published config, never in the layout.vendor.Import the shared theme once in the app's css, in place of @import 'tailwindcss'. The theme imports Tailwind and loads the @tailwindcss/forms plugin, which the package bundles in resources/dist/forms.js, so an app has no @plugin line and installs only the tailwindcss npm package. nyumbani:install swaps an existing Tailwind import and forms plugin line for the theme import.
@import '../../vendor/nyumbani/ui/resources/css/theme.css';
Use the tokens primary, primary-dark, primary-darker and secondary*. Do not hard-code hex colors for brand surfaces.
@use for any class in a view and never @php.@section('title', ...). The layout appends the app name.There are four, and the Layouts page of the UI docs shows the code that extends each one:
nyumbani::layouts.portal: signed-in pages. Sections title, content, sidebar, and optionally portal.content, brand, navbar, user-menu, banner; @push('actions') for page buttons. Define the sidebar once: the app's layouts/app.blade.php extends the portal and sets @section('sidebar', 'partials.sidebar'), the partial holds the nav-link and nav-group items, and every page extends layouts.app (never the portal) and fills only its own sections.nyumbani::layouts.auth: authentication pages only (sign-in, forgot password, reset password), which the UI package already ships; use nyumbani::layouts.app for any other public page. Sections heading, description, content.nyumbani::layouts.error: error pages. Sections code, heading, detail, icon, actions.nyumbani::layouts.app: the base document the others extend. Section title, body, body-class; stacks styles and js.The portal draws portal.content when the app layout defines it, and the page's content otherwise. Define it in layouts/app.blade.php to put every page on a card, and let a page opt out with another section the layout checks for:
@extends('nyumbani::layouts.portal')
@section('sidebar', 'partials.sidebar')
@section('portal.content')
@hasSection('content.plain')
@yield('content.plain')
@else
<div class="bg-white p-2 shadow-xs">
@yield('content')
</div>
@endif
@endsection
A page then fills content to sit on the card, or content.plain to sit straight on the portal. Without portal.content, pages fill content and the portal draws it as it is.
How can I help you explore Laravel packages today?