Typography Scale
Typography is the driving visual force of the mirror. We use Inter. Display headings use very thin weights to minimize light dispersion, while smaller labels use bold weights for legibility behind the reflective glass.
08:32
<h1 class="display-xl">08:32</h1>
<div class="display-lg">-14°C</div>
<div class="headline-md">Stockholm</div>
<div class="body-base">Main content paragraph or secondary temperature description.</div>
<div class="body-sm">Details, metadata labels, or smaller paragraph blocks.</div>
Name Day
<h2 class="module-header">Name Day</h2>
Color Palette
The palette is strictly functional, optimized for light transmission through two-way glass. Every color has a defined purpose.
Color Utility Classes
Colors have specific purposes to help prioritize readability against ambient lighting.
<span class="text-primary">Primary Light</span>
<span class="text-secondary">Standard gray label</span>
<span class="text-dimmed">Dimmed Charcoal</span>
<span class="text-error">Critical Error alert</span>
Layout & Grid Regions
The mirror viewport layout is constrained to a 3x3 peripheral grid. To preserve the functionality of the mirror reflection, content must remain at the edges, keeping the center empty unless displaying temporary modal alerts.
<!-- Module position configuration in config.json -->
{
"modules": {
"mirrordash-clock": {
"position": "top_left",
"enabled": true
}
}
}
<!-- Horizontal Row with Icon alignment -->
<div class="flex-row">
<i data-lucide="sun" class="text-primary"></i>
<span class="body-base text-primary">22°C</span>
</div>
<!-- Space-Between row (left label, right value) -->
<div class="flex-row-between">
<span class="text-secondary">Status</span>
<span class="text-primary">Online</span>
</div>
<!-- Vertical Stack -->
<div class="flex-column">
<span class="body-base text-primary">Title</span>
<span class="body-sm text-secondary">Subtitle description...</span>
</div>
Iconography
The system uses Lucide Icons as its standard, vector-based line-art iconography. All icons inherit their parent element's text color and use a thin outline (default 1.5px stroke weight).
<!-- Monochromatic line-art icons -->
<i data-lucide="sun"></i>
<i data-lucide="settings" class="text-secondary"></i>
<i data-lucide="alert-triangle" class="text-error glow"></i>
Containers & Depth
In a physical mirror, drop shadows are useless. Instead, we use semi-opaque dark backdrops to isolate content and subtle atmospheric glow filters to emphasize elements.
Demo Module
<div data-module="my-custom-module">
<h2 class="module-header">Demo Module</h2>
<div class="body-sm text-primary">Content goes here.</div>
</div>
<div class="glow display-xl">GLOWING TEXT</div>
<!-- Add class to body or container during critical alerts -->
<body class="backdrop-blur-active">
<div data-module="weather">…</div> <!-- blurred -->
<div class="alert-modal">…</div> <!-- stays sharp -->
</body>
UI Components
Pre-designed structural blocks for building clean, consistent mirror layouts.
<div class="widget-header">
<h2 class="module-header">Weather Forecast</h2>
</div>
<div style="display: flex; align-items: baseline;">
<span class="display-xl">08:32</span>
<span class="headline-md text-dimmed">:45</span>
</div>
<div class="notification-card" data-module="notification">
<div class="body-sm text-primary">System Update Available</div>
<div class="body-sm text-secondary">Details here.</div>
</div>
<div class="data-list">
<div class="data-list__item">
<span class="data-list__label">SSE index</span>
<span class="data-list__value data-list__value--up">2,410.50 (+1.2%)</span>
</div>
<div class="data-list__item">
<span class="data-list__label">OMX Stockholm</span>
<span class="data-list__value data-list__value--down">960.20 (-0.8%)</span>
</div>
</div>
<span class="status-indicator status-indicator--online"></span> Online
<span class="status-indicator status-indicator--warning"></span> Warning
<hr class="separator-hr">
<div class="carousel-group-container">
<div class="carousel-slide carousel-active" data-module="weather">
<h2 class="module-header">Weather</h2>
<div class="body-sm text-primary">Sunny, 22°C</div>
</div>
<div class="carousel-slide" data-module="news">
<h2 class="module-header">News Feed</h2>
<div class="body-sm text-secondary">Headlines...</div>
</div>
</div>
Extended Layout Elements
Comprehensive, layout-ready widgets and telemetry patterns to simplify building common dashboard modules.
<div class="forecast-grid">
<div class="forecast-grid__day">
<span class="forecast-grid__name">Mon</span>
<i data-lucide="sun" class="forecast-grid__icon"></i>
<span class="forecast-grid__temp">22°<span class="forecast-grid__temp-low">14°</span></span>
</div>
<div class="forecast-grid__day">
<span class="forecast-grid__name">Tue</span>
<i data-lucide="cloud-sun" class="forecast-grid__icon"></i>
<span class="forecast-grid__temp">20°<span class="forecast-grid__temp-low">12°</span></span>
</div>
</div>
<div class="agenda-list">
<div class="agenda-list__item">
<div class="agenda-list__date">
<span class="agenda-list__date-month">Jun</span>
<span class="agenda-list__date-day">02</span>
</div>
<div class="agenda-list__details">
<span class="agenda-list__title">Design Sync Meeting</span>
<span class="agenda-list__subtitle">09:00 - 10:30 | Room 404</span>
</div>
</div>
</div>
<div class="gauge-bar">
<div class="gauge-bar__label-row">
<span>System Storage</span>
<span>65%</span>
</div>
<div class="gauge-bar__track">
<div class="gauge-bar__fill" style="width: 65%;"></div>
</div>
</div>
<div class="stats-grid">
<div class="stats-grid__item">
<span class="stats-grid__value">0.12</span>
<span class="stats-grid__label">Load</span>
</div>
<div class="stats-grid__item">
<span class="stats-grid__value">42°</span>
<span class="stats-grid__label">Temp</span>
</div>
<div class="stats-grid__item">
<span class="stats-grid__value">1.2G</span>
<span class="stats-grid__label">RAM</span>
</div>
</div>
<div class="alert-callout">
<div>
<div class="alert-callout__title">Warning</div>
<div class="alert-callout__desc">The back door has been left open for 10 minutes.</div>
</div>
</div>
Configuration Form Controls
MirrorDash auto-generates admin settings forms from each module's config_schema.json.
The form generator reads standard JSON Schema properties and picks the correct input control automatically.
Below are all supported field types — copy the schema snippet into your module's config_schema.json to get the rendered control.
The city name to display weather for.
"city": {
"type": "string",
"title": "City",
"description": "The city name to display weather for.",
"default": "Stockholm"
}
Display seconds in the clock readout.
"show_seconds": {
"type": "boolean",
"title": "Show Seconds",
"description": "Display seconds in the clock readout.",
"default": true
}
Maximum number of calendar events to show.
"max_events": {
"type": "integer",
"title": "Max Events",
"description": "Maximum number of calendar events to show.",
"default": 10
}
Screen backlight brightness level (10–100%).
"brightness": {
"type": "integer",
"title": "Brightness",
"description": "Screen backlight brightness level (10–100%).",
"minimum": 10,
"maximum": 100,
"default": 85
}
Display language for labels and dates.
"language": {
"type": "string",
"title": "Language",
"description": "Display language for labels and dates.",
"enum": ["sv", "en"],
"default": "sv"
}
Select one or more preconfigured news sources.
"builtin_feeds": {
"type": "array",
"title": "Built-in Feeds",
"description": "Select one or more preconfigured news sources.",
"items": {
"type": "string",
"enum": ["bbc_news", "sr_ekot", "cnn_edition"]
},
"default": ["bbc_news"]
}
Allows administrators to manage a list of structured items. Each item is rendered inside an expandable card. The layout features built-in controls to re-order items (Move Up / Move Down buttons) and delete entries dynamically.
Dynamic Sub-field Types & Naming Conventions:
- Color Swatches: A property named exactly
"color"triggers a grid of preset swatches (White, Ice Blue, Rose Pink, Green, Red, Gray, Charcoal) along with a text box for custom Hex/CSS values. - Icon Picker: A property named exactly
"icon"or ending in"_icon"renders a visual grid of outline icon picker buttons (Calendar, Clock, Home, etc.) plus a text box for custom Lucide icon keywords. - Dropdown Selection: Any property defining an
"enum"array of options automatically renders as a selection dropdown (<select>) instead of a text entry box. - Default Inputs: All other schema properties of type
"string","number", or"integer"default to standard text input fields.
A list of devices to display with specific styling.
"entities": {
"type": "array",
"title": "Tracked Entities",
"description": "Configure a list of devices to display.",
"items": {
"type": "object",
"title": "Entity",
"properties": {
"entity_id": {
"type": "string",
"title": "Entity ID",
"description": "The full Home Assistant entity ID."
},
"layout": {
"type": "string",
"title": "Layout Style",
"default": "compact",
"enum": ["detailed", "compact"]
},
"custom_icon": {
"type": "string",
"title": "Custom Icon"
},
"color": {
"type": "string",
"title": "Indicator Color",
"default": "#ffffff"
}
},
"required": ["entity_id"]
},
"default": []
}
"advanced": {
"type": "object",
"title": "Advanced Settings",
"description": "Fine-grained options for advanced users.",
"properties": {
"timeout": {
"type": "integer",
"title": "Timeout",
"default": 30
}
}
}
Override the primary highlight color.
"accent_color": {
"type": "string",
"format": "color",
"title": "Accent Color",
"description": "Override the primary highlight color.",
"default": "#ffffff"
}
Your service API key. Stored locally only.
"api_key": {
"type": "string",
"format": "password",
"title": "API Key",
"description": "Your service API key. Stored locally only.",
"default": ""
}
Text to show as the welcome message.
"message": {
"type": "string",
"format": "textarea",
"title": "Greeting Message",
"description": "Text to show as the welcome message.",
"default": "Good morning!"
}
System & Connection Status
System-level indicators displayed at the top-center to show connection statuses. Use the buttons below to preview the states.
<div class="ws-status ws-status--connecting">
<span class="ws-status__dot"></span>
<span class="ws-status__label">Connecting…</span>
</div>
Module Loading Skeletons
Visual skeletons displayed on page load before WebSocket messages populate data. Click "Simulate Load Done" to see the fade-in and slide entrance animation.
<!-- Pre-rendered loading state -->
<div data-module="mirrordash_namnsdag" class="module-loading-placeholder">
<div class="module-loading-content">
<span class="module-loading-spinner"></span>
<span class="module-loading-text">Loading Swedish Name Day…</span>
</div>
</div>