Skip to main content

UI Pages

UI Pages are custom dashboard pages composed of widgets. Unlike standard control pages, UI Pages provide complete flexibility to design custom layouts with charts, gauges, cameras, weather, and any custom visualization.

Overview

UI Pages enable:

  • Custom Dashboards: Create purpose-built information displays
  • Widget Composition: Combine multiple widgets on a single page
  • Responsive Layouts: Widgets flow and resize based on screen size
  • Live Preview: See changes in real-time while editing
  • Page Templates: Copy and reuse successful page designs
Admin dashboard

The admin Dashboard is itself a UI page (admin_dashboard, seeded at boot). Its Customize button deep-links straight here (?page=admin_dashboard), so you reorder and configure the admin home dashboard the same way you edit any other UI page.

Page Management

The UI Pages interface is organized with selectors and action buttons at the top, followed by content areas.

Selectors

UI Page Selector

  • Dropdown of all UI pages
  • Select to view and edit page
  • Shows page title and name

Actions

Rename Page

  • Change page title and name
  • Updates all references

Copy Page

  • Duplicate entire page including all widgets
  • Creates independent copy
  • Useful for templates

Delete Page

  • Remove page (confirmation required)
  • Removes all page-widget assignments
  • Widgets themselves are not deleted

Save Page

  • Save title, enabled state, and override route changes
  • Saving is unified: both Save buttons (the page toolbar and the Widget Configuration card header) save the page settings — including Columns — and the widget currently loaded in Widget Configuration, in that order. There is no way to lose an unsaved layout change by saving a widget.

Preview Page

  • Opens live preview modal
  • Shows actual rendering with theme
  • Supports theme switching in preview
  • Interactive widgets (if live mode enabled)

Page Configuration

Page Title

  • Display name shown in navigation
  • Appears in page tabs/selector
  • User-friendly text

Page Enabled

  • Toggle to show/hide page
  • Disabled pages:
    • Don't appear in navigation
    • Cannot be accessed
    • Configuration preserved

Override Route

  • Optional Svelte route to override
  • Advanced: Replace built-in page with custom page
  • Example: Override /controls/lighting with custom lighting page
  • Leave blank for normal pages

Layout — Bento Grid

Every page is a bento grid: widgets fill equal-height grid cells in a fixed column count (default 3, configurable via the Columns field in Page Settings). In portrait, tablets render 2 columns while phones stack to a single full-width column — Column Span is ignored on phones, but Row Span still applies.

  • Per-widget Column Span and Row Span (in Widget Configuration, Layout group) let a widget cover multiple cells; containers support the same spans.
  • The grid cell defines each widget's size. Auto-size stays on by default and shrinks oversized content to fit the cell (turn it off per widget for natural-size content).
Migrated from the classic flow layout

Pages created before the bento-grid unification (the classic percent-width flow layout) are converted automatically at server startup: the column count is inferred from the page's widget widths and spans are written for anything larger than one cell. The migration also removes the retired classic keys (percent/portrait sizing, margins, page flow settings) from stored configs — the grid is the only layout, with no revert path. Check the startup log for migrated ui_page to bento grid lines; pages flagged with check: had widths that didn't snap cleanly and are worth eyeballing, then adjusting via spans.

Widgets Section

Manage widgets assigned to the selected page.

Available Widgets (Left)

Dropdown selector showing all UI widgets:

  • Shows widget name and type
  • Search/filter capabilities
  • Select widget to add to page

Current Page Widgets (Right)

Grid or list of widgets on this page:

  • Widget name
  • Widget type
  • Container assignment
  • Enabled state
  • Sort order

Widget Actions

Add Widget to Page

  1. Select widget from dropdown
  2. Choose container (if page has multiple containers)
  3. Click Add
  4. Widget appears on page

Remove Widget from Page

  1. Select widget in current page widgets list
  2. Click Remove
  3. Widget removed from page (widget itself not deleted)

Reorder Widgets

  • Drag boxes in the Layout wireframe below the page widgets list: drop on the left or right half of another box to place the widget before or after it. The bento grid flows widgets in sort order, so reordering is how a widget moves around the page.
  • A container group drags as one unit; widgets inside a container drag to reorder within that container (drop zones follow the container's flow direction). Moving a widget into or out of a container is done with the Container select in Widget Configuration.
  • The ▲ ▼ buttons do the same thing one step at a time, and remain the path on touch screens.

Configure Widget

  • Widget-specific settings
  • Grid spans (Column Span / Row Span)
  • Container assignment
  • Enabled state
Global definition vs. page instance

Clicking a widget in Available Widgets edits the global widget definition; clicking one in Page Widgets (or the layout wireframe) edits that page's instance. A scope badge in the Widget Configuration header shows which one is loaded — Global — used on N pages (orange when the widget is placed anywhere) or Page instance. Saving a global definition that is in use asks for confirmation, since it updates the defaults on every page placing that widget; page instances that have been saved with their own config keep those values.

Widget config is edited through a labeled form rather than raw JSON. The Widget section at the top of the card holds the instance fields — Title, Name, Container assignment, and Enabled — with Save in the card header, and the form's setting groups are collapsible (click a group header to fold it). The card body scrolls internally when the form is long, so the layout wireframe beside it stays visible while editing. Even widgets that declare no settings of their own always show the common container fields — Column Span, Row Span, Show Container, Auto-size, CSS Class, Inline Style, Content Style, and Transition — so layout is editable for every widget. Config keys the schema doesn't declare (custom widget settings) still render as typed fields under an Other group, and a Raw JSON toggle remains for direct editing.

Inline Style vs. Content Style: Inline Style applies CSS to the widget's outer container, while Content Style applies CSS to the inner content area and overrides its default centering — e.g. justify-content: flex-start; align-items: flex-start to top/left-align a widget's content. Inline styles set here beat the scoped class rule, so any property you set wins. Both default to empty (no change).

Clearing a field vs. inheriting a default

For css_class, style, inner_style, and transition, blanking the field writes an explicit empty value so the cleared setting overrides the widget's merged-in default rather than falling back to it. Other fields fall back to their default when cleared.

Widget Containers

A widget container groups several widgets into one box that spans grid cells and is styled as a unit — resize the container's spans and the whole group follows. Widgets assigned to a container flow inside it per its Children settings. Containers are global: create one once and reuse it on any page (note the spans apply everywhere it's used). A widget with no container assignment simply occupies its own grid cells (the common case).

Creating Containers

Containers are created and edited on the Containers tab of the UI Pages editor:

  1. Click New and enter a title — the name is generated from it; both stay editable
  2. Configure layout in Container Config — a labeled form showing the options a container actually honors: Column/Row Span, CSS Class, and Inline Style. The Children group controls how assigned widgets flow inside the container — Direction, Gap, Align Items, Justify, Wrap, and Padding, the same flex controls (and icon buttons) as the Widget Designer's Section element. Containers saved before these options existed keep their original centered-row behavior until a value is changed. The form shares its catalog with the widget config form, and a live preview beside it shows the container at its configured spans on a simulated 3-column page with stand-in widget chips that follow the flow settings. Unknown keys are preserved and stay editable via the Raw JSON toggle.
  3. Click Save, then assign widgets to the container (below)

Assigning Widgets

Each page widget has a Container selector in its configuration. Pick a container to render the widget inside it; leave it empty to place the widget directly on the page. Widgets in the same container keep their own individual configs — the container only supplies the shared box and the flow of its children.

Layout Wireframe

Under the Page Widgets list, a live wireframe sketches the page layout as you work: one named box per spanned grid cell group, and containers drawn as outlined groups with their member widgets flowing inside per the container's Children settings.

  • Click a box to select that widget for editing (the selected widget is outlined).
  • The widget being edited follows your unsaved form values live — change a span, the Container assignment, or the Enabled toggle and the wireframe updates before you save.
  • Disabled widgets appear ghosted (dashed, faded) — the rendered page hides them entirely.
  • The Landscape / Tablet / Phone toggle previews each device class, and each button shows its column count — e.g. Landscape (3), Tablet (2), Phone (1). Landscape follows the page's Columns setting live (change the field and the count updates before saving), tablet portrait is always 2 columns (column spans still apply, clamped to the track count), and phone stacks a single column with every widget full-width. Row spans apply on every device. These breakpoints come from the runtime grid and are the same for all themes.

The wireframe is schematic — for a full render with real widget content, use the Preview button in the page toolbar.

Page Preview

The preview feature allows real-time visualization:

Opening Preview

Click Preview Page button to:

  • Open modal with live page render
  • Apply selected theme
  • Show all widgets in their configured layout
  • See actual sizing and positioning

Preview Controls

Page Link

  • Shows the shareable deep-link for the selected page (<origin>/?page=<name>) — the same URL the app navigates to when opening this page
  • Click the link or the copy button to copy it to the clipboard
  • Paste it to bookmark the page or send it to another client

Theme Selector

  • Dropdown of available themes
  • Switch themes to see how page looks
  • Applies the theme's CSS and its Layout & Presentation flags (state-dot rectangle buttons, scene chips) — the preview renders as that theme's UIs will
  • Doesn't change actual UI theme, just preview

Interactive Mode

  • Toggle to enable widget interactivity
  • When enabled:
    • Widgets load real data
    • Controls are functional
    • Charts update live
  • When disabled:
    • Static preview only
    • Faster rendering

Close

  • Close preview modal
  • Returns to edit mode

Preview Use Cases

  1. Layout Testing: Verify widget arrangement
  2. Theme Compatibility: Test page with different themes
  3. Responsive Design: Resize browser to test different screen sizes
  4. Client Approval: Show clients the page before deploying
  5. Iteration: Make changes, preview, refine, repeat

Page Templates

Copying Pages

Duplicate successful page designs:

  1. Select page to copy

  2. Click Copy Page

  3. Enter new page name

  4. New page created with:

    • All widgets copied
    • Same layout
    • Same configuration
    • Independent from original
  5. Customize new page as needed

Use Cases:

  • Floor plan pages (copy layout, change zones)
  • Dashboard templates (copy structure, change data sources)
  • Client dashboards (copy template, customize branding)

Built-In Page Templates

GEM includes starter templates:

  • Dashboard - Overview with status widgets
  • Monitoring - Charts and graphs
  • Security - Cameras and alarm status
  • Energy - Power monitoring and usage

Copy and customize these templates rather than starting from scratch.

Assigning Pages to UIs

UI Assignment

Pages can be assigned to specific UIs:

  1. In page configuration, select Assign to UIs
  2. Choose which UIs should have this page
  3. Save

Options:

  • All UIs - Page appears in every UI
  • Specific UIs - Page appears only in selected UIs
  • No UIs - Page exists but isn't accessible (draft state)

Assigned pages appear in UI navigation:

  • Page tabs (if UI has page navigation)
  • Menu items
  • Home screen tiles

Override Routes

Advanced feature to replace built-in pages:

Use Case

Replace standard /controls/lighting with custom page:

  1. Create UI page with custom lighting interface
  2. Set Override Route: /controls/lighting
  3. Enable page
  4. When users navigate to Lighting control, they see your custom page instead

Considerations

  • Only override if custom page is truly better than built-in
  • Maintain expected functionality in override
  • Test thoroughly
  • Document customizations

Widget Layout

Responsive Grid

Widgets are arranged in a bento grid:

  • Desktop / landscape: the page's Columns setting (default 3), equal-height rows
  • Tablet portrait: 2 columns
  • Phone: single full-width column (Column Span ignored, Row Span still applies)

Widget Sizing

Each widget occupies grid cells:

  • Column Span: how many columns wide (clamped to the page's column count)
  • Row Span: how many rows tall

Example Layouts (3-column page):

Full Width Banner:

Column Span: 3
Row Span: 1

Featured Widget Beside Tiles:

Widget 1: Column Span 2, Row Span 2
Widgets 2-5: Column Span 1, Row Span 1
(Widget 1 dominates the top-left, tiles fill around it)

Even Tile Grid:

All widgets: Column Span 1, Row Span 1
(Three per row, uniform cells — the default)

Common Page Types

Status Dashboard

Monitor system at a glance:

Widgets:

  • System status widget (online/offline devices)
  • Security status widget (armed/disarmed, sensors)
  • Climate widget (all temperatures)
  • Energy widget (current usage)
  • Weather widget

Layout (2-column page):

[ System Status ] [ Security ]
[ Climate ] [ Energy ] [ Weather ] (3-column page: span 1 each)

Camera Dashboard

Security camera monitoring:

Widgets:

  • Multiple camera feed widgets
  • Motion detection status
  • Recording status
  • PTZ controls

Layout:

[ Camera 1 - 50% ] [ Camera 2 - 50% ]
[ Camera 3 - 50% ] [ Camera 4 - 50% ]
Build a camera wall in one ask

The built-in Camera widget accepts a comma-delimited list of camera zone ids and renders them as a responsive grid — one widget placement covers the whole wall. The AI Assistant's create_camera_wall skill builds the entire composition (page + camera widget + placement + UI links) from a single request like "build me a Cameras page with every camera on the site".

Energy Monitoring

Power and energy analytics:

Widgets:

  • Real-time power chart
  • Daily usage chart
  • Cost calculator
  • Top consumers list
  • Solar production (if applicable)

Floor Plan Dashboard

Interactive floor plan:

Widgets:

  • Floor plan widget (custom)
  • Zone status overlays
  • Quick controls
  • Room temperature display

Best Practices

  1. Page Purpose: Each page should have a clear purpose

    • Don't combine unrelated widgets
    • Keep pages focused
  2. Widget Count: Limit widgets per page

    • Desktop: 4-8 widgets
    • Tablet: 3-5 widgets
    • Mobile: 2-3 widgets
  3. Layout Balance: Distribute screen space appropriately

    • Important widgets get more space
    • Support widgets use less space
    • Avoid one widget dominating
  4. Performance: Consider widget update frequency

    • Too many live-updating widgets can slow page
    • Stagger update intervals
    • Use static widgets where appropriate
  5. Responsive Design: Test on multiple screen sizes

    • Preview with different window sizes
    • Ensure widgets remain readable when stacked
    • Test on actual target devices
  6. Color Coordination: Widgets should complement theme

    • Use theme colors in custom widgets
    • Avoid clashing colors
    • Consider dark/light theme variants
  7. Loading States: Widgets should handle loading gracefully

    • Show loading indicators
    • Don't block entire page
    • Fail gracefully if data unavailable

Troubleshooting

Page Not Appearing in UI

Check:

  1. Page Enabled: Page must be enabled
  2. UI Assignment: Page must be assigned to UI
  3. User Permissions: User role allows access
  4. Navigation: Verify page appears in UI's navigation

Widget Not Showing on Page

Check:

  1. Widget Enabled: Widget must be enabled
  2. Page Assignment: Widget must be added to page
  3. Container: Widget assigned to correct container
  4. Size: Widget has reasonable size (not 0%)
  5. Widget Errors: Check widget itself works

Preview Not Loading

Check:

  1. Page Selected: Select a page first
  2. Widgets: Page has widgets assigned
  3. Theme: Theme is valid and loaded
  4. Browser: Try different browser
  5. Cache: Clear browser cache

Copy Failed

Check:

  1. Name Unique: New page name must be unique
  2. Permissions: User has permission to create pages
  3. Widgets Exist: All widgets on source page still exist

Override Not Working

Check:

  1. Route Format: Must match existing route exactly
  2. Page Enabled: Override page must be enabled
  3. Page Content: Page has appropriate widgets/content
  4. Cache: Clear browser cache and reload

Advanced Topics

Dynamic Page Loading

Pages can load different widgets based on:

  • User role
  • Time of day
  • System mode
  • Screen size

Requires custom page implementation or JavaScript in theme.

API Integration

Display external data in widgets:

  • Weather data from API
  • Stock prices
  • News feeds
  • Calendar events
  • Smart home integrations

Configure in individual widgets.

Custom Page Components

For maximum flexibility, create custom page components:

  1. Develop Svelte component in src/routes/
  2. Add as override route
  3. Full control over layout and behavior