diff --git a/docs.json b/docs.json index e1c9af6..1c061b7 100644 --- a/docs.json +++ b/docs.json @@ -50,6 +50,7 @@ "v3.x/configuration/environment", "v3.x/configuration/customization", "v3.x/configuration/theme", + "v3.x/configuration/theme-hooks", "v3.x/configuration/beacon", "v3.x/configuration/api-keys" ] diff --git a/v3.x/configuration/theme-hooks.mdx b/v3.x/configuration/theme-hooks.mdx new file mode 100644 index 0000000..8597e9f --- /dev/null +++ b/v3.x/configuration/theme-hooks.mdx @@ -0,0 +1,170 @@ +--- +title: 'Theme hooks' +description: 'Theme Cachet with stable CSS selectors and render hooks.' +icon: 'code' +--- + +Cachet exposes stable HTML data attributes and PHP render hooks for status page themes. Use these public hooks instead +of CSS framework utilities, generated classes, or the current HTML nesting. Implementation classes may change between +releases; the documented data attributes are the theming contract. + +Add CSS in **Settings → Manage Customization → Custom CSS**. See [Customization](/v3.x/configuration/customization) +for CSS variables and the other customization fields. + +## CSS hooks + +Use `data-page` to scope an entire page, `data-component` to select a reusable component, and `data-slot` to select a +semantic part within that component. + +```css Custom CSS +/* Change only the main status page summary. */ +[data-page='status'] [data-component='status-summary'] { + border-radius: 1rem; +} + +/* Style component names without depending on heading tags or classes. */ +[data-component='component'] [data-slot='title'] { + letter-spacing: 0.01em; +} + +/* Use the nearest component to disambiguate a repeated slot name. */ +[data-component='schedule'] [data-slot='status'] { + justify-content: flex-start; +} +``` + + + Scope `data-slot` selectors through a `data-component` or `data-page`. Slot names such as `title`, `content`, and + `status` are intentionally reused by different components. + + +### Pages + +The `` element has one of these `data-page` values: + +| Value | Page | Page-level slots | +| --- | --- | --- | +| `status` | Main status page | `main` | +| `incident` | Incident detail | `main` | +| `schedule` | Scheduled maintenance detail | `main` | +| `subscribe` | Subscriber form and result states | `main` | +| `unsubscribe` | Unsubscribe confirmation | `main` | + +### Components and slots + +Components may be absent when their feature is disabled or they have no content. + +| `data-component` | Stable `data-slot` values | +| --- | --- | +| `about` | `title`, `description` | +| `affected-components` | `title`, `list` | +| `badge` | No named slots | +| `component-list` | `list` | +| `component-group` | `trigger`, `indicator`, `title`, `status`, `content`, `list` | +| `component` | `content`, `title`, `description`, `tags`, `status` | +| `footer` | `content`, `support`, `timezone` | +| `header` | `content`, `brand`, `navigation`, `subscribe`, `dashboard`, `logout` | +| `incident-timeline` | `header`, `title`, `filters`, `list`, `empty`, `navigation` | +| `incident-day` | `date`, `empty` | +| `incident` | `header`, `title`, `timestamp`, `status`, `affected-components`, `updates` | +| `incident-update` | `message` | +| `incident-update-status` | `indicator` | +| `logo`, `logomark` | No named slots | +| `metrics` | No named slots | +| `metric` | `content`, `header`, `title`, `description`, `suffix`, `periods`, `period`, `chart` | +| `page-navigation` | No named slots | +| `schedules` | `title`, `content`, `list` | +| `schedule` | `content`, `header`, `title`, `timestamp`, `status`, `message`, `affected-components`, `updates` | +| `schedule-update` | `message` | +| `status-overview` | `masthead`, `title` | +| `status-summary` | `content`, `status`, `indicator`, `title`, `last-updated` | +| `subscribe` | `content`, `state`, `form`, `navigation` | +| `timestamp` | `value`, `tooltip` | +| `unsubscribe` | `content`, `form`, `navigation` | + +Repeated records also expose their database key separately through `data-component-id`, `data-component-group-id`, +`data-incident-id`, `data-metric-id`, `data-schedule-id`, or `data-update-id`. Use those attributes only when a theme +must target one configured record. + +Incident-day components expose their ISO date through `data-date`. + +The subscribe component exposes `data-state="form"`, `subscribed`, `unsubscribed`, or `verified` on its `state` slot. + +## Render hooks + +Render hooks insert custom HTML without overriding Cachet views. Register hooks in the `boot` method of an application +service provider: + +```php app/Providers/AppServiceProvider.php + view('status.banner'), + ); + } +} +``` + +The callback may return a string, an `Htmlable` object, or a view. Cachet renders hook output as HTML, so escape any +untrusted data before returning it. + +### Available render hooks + +| Constant | Position | +| --- | --- | +| `STATUS_PAGE_BODY_BEFORE` | At the start of the status page `` | +| `STATUS_PAGE_BODY_AFTER` | At the end of the status page `` | +| `STATUS_PAGE_NAVIGATION_BEFORE` | Before the header | +| `STATUS_PAGE_NAVIGATION_AFTER` | After the header | +| `STATUS_PAGE_BANNER` | After the header and navigation hooks | +| `STATUS_PAGE_STATUS_SUMMARY_BEFORE` | Before each status summary | +| `STATUS_PAGE_STATUS_SUMMARY_AFTER` | After each status summary | +| `STATUS_PAGE_ABOUT_BEFORE` | Before the about component | +| `STATUS_PAGE_ABOUT_AFTER` | After the about component | +| `STATUS_PAGE_COMPONENT_GROUPS_BEFORE` | Before each component group | +| `STATUS_PAGE_COMPONENT_GROUPS_AFTER` | After each component group | +| `STATUS_PAGE_COMPONENTS_BEFORE` | Before each component | +| `STATUS_PAGE_COMPONENTS_AFTER` | After each component | +| `STATUS_PAGE_METRICS_BEFORE` | Before the metrics list | +| `STATUS_PAGE_METRICS_AFTER` | After the metrics list | +| `STATUS_PAGE_SCHEDULES_BEFORE` | Before each scheduled maintenance item | +| `STATUS_PAGE_SCHEDULES_AFTER` | After each scheduled maintenance item | +| `STATUS_PAGE_INCIDENT_TIMELINE_BEFORE` | Before the complete incident timeline | +| `STATUS_PAGE_INCIDENT_TIMELINE_AFTER` | After the complete incident timeline | +| `STATUS_PAGE_INCIDENTS_BEFORE` | Before each incident-day block | +| `STATUS_PAGE_INCIDENTS_AFTER` | After each incident-day block | +| `STATUS_PAGE_SUBSCRIBE_BEFORE` | Before subscribe content | +| `STATUS_PAGE_SUBSCRIBE_AFTER` | After subscribe content | +| `STATUS_PAGE_UNSUBSCRIBE_BEFORE` | Before unsubscribe confirmation content | +| `STATUS_PAGE_UNSUBSCRIBE_AFTER` | After unsubscribe confirmation content | +| `FOOTER` | At the end of the footer content | + +Item-level hooks can run more than once on a page. Component, component-group, and schedule hooks are adjacent to list +items; return a valid `
  • ` when inserting visible content there. + +Render hooks for optional sections run only when Cachet renders that section. For example, metrics hooks are unavailable +when metric graphs are disabled. Registering `FOOTER` creates the footer boundary even when Cachet's built-in support +and timezone content are disabled. + +## Compatibility + +Treat the documented attribute values and render-hook constants as public APIs. Avoid selectors based on: + +- Utility classes or generated framework classes +- Element depth, child position, or heading level +- Alpine attributes such as `x-data` +- Filament-specific markup + +These implementation details may change without a theming compatibility guarantee. diff --git a/v3.x/configuration/theme.mdx b/v3.x/configuration/theme.mdx index 4b25bf7..9e87282 100644 --- a/v3.x/configuration/theme.mdx +++ b/v3.x/configuration/theme.mdx @@ -7,6 +7,8 @@ icon: 'swatchbook' Cachet provides the ability to configure and theme the status page to match your brand. To customize your status page, navigate to the "Manage Theme" section found under "Settings" in your Cachet dashboard. +For custom CSS selectors and programmatic content insertion, see [Theme hooks](/v3.x/configuration/theme-hooks). + ## Banner image To override the default banner image shown at the top of your status page, upload a custom image by dragging and dropping the file into the "Banner Image" field. Alternatively, you can click to select a file.