-
Notifications
You must be signed in to change notification settings - Fork 14
Architecture
A high-level tour of how Ultra Card is built. Useful if you're contributing, debugging, or just curious.
| Area | Tech |
|---|---|
| UI framework | Lit 3 (web components) |
| Language | TypeScript |
| Bundler | webpack 5 |
| Charts | Custom SVG / canvas + Three.js (three@0.181.1) for WebGL |
| Drag & drop | Pragmatic drag-and-drop |
| Code editor | CodeMirror 6 (Jinja2 syntax) |
| Markdown |
marked@16 + DOMPurify |
| WYSIWYG | TipTap |
| Maps | Leaflet 1.9 |
| Carousels | Swiper 12 |
| HA integration |
home-assistant-js-websocket, custom-card-helpers
|
| State | Lit's reactive properties + service singletons |
| Tests | Vitest |
See package.json for the exact dependency versions.
src/
├── cards/ Top-level <ultra-card> Lit element
├── modules/ 74+ module implementations + registry
├── editor/ Visual editor (4 tabs + selectors)
├── tabs/ Reusable Actions / Logic / Design tab implementations
├── components/ Reusable UI pieces (color picker, dialogs, …)
├── services/ Cross-cutting services (templates, logic, cloud)
├── translations/ 15 locale JSON files
├── utils/ Helpers (template processor, layout utils)
├── pro/ Pro/Cloud-gated features
├── panels/ Ultra Card Hub sidebar panel (separate bundle)
├── workers/ Web Worker(s) for heavy computation
├── types.ts Single source of truth for config types
└── version.ts Version constant (single source of truth)
src/modules/module-manifest-data.ts is the static metadata table — the single source of truth for module type, title, icon, category, and tags. No implementation is loaded to read this.
The selector and editor use the manifest to render the module library without bundling every module's implementation upfront.
src/modules/module-registry.ts and module-loaders.ts provide:
- A
Map<ModuleType, BaseUltraModule>populated lazily. - A loader that dynamically imports a module's implementation on first use.
src/modules/base-module.ts defines BaseUltraModule — the contract every module implements:
abstract class BaseUltraModule {
abstract metadata: ModuleMetadata;
abstract createDefault(): CardModule;
abstract renderGeneralTab(...): TemplateResult;
abstract renderModule(...): TemplateResult;
// optional: renderActionsTab, renderLogicTab, renderDesignTab, getStyles
}A canonical pattern for renderGeneralTab is documented in /.cursor/rules/module-general-tab.mdc.
Webpack code-splits each module — they ship as separate chunks (uc-*.js) loaded on demand. This is why all release assets must be installed, not just ultra-card.js. See Installation.
A preload scheduler (uc-module-preload-scheduler.ts) opportunistically warms chunks based on the layout, so the first interaction with any module type is instant.
Core services live in src/services/. They're singletons attached as cross-cutting concerns:
| Service | Purpose |
|---|---|
LogicService |
Evaluates display conditions, owns template subscriptions |
TemplateService |
Wraps HA render_template WebSocket subs with caching, dedupe, signature-based reuse |
UcActionService |
Centralized action dispatcher (toggle / nav / service call / assist) with confirmation dialogs |
ucCloudAuthService / ucCloudSyncService / ucSnapshotService
|
Pro Cloud features |
ucNavigationService |
Tracks current dashboard path for active-route highlighting |
responsiveDesignService |
Resolves the active breakpoint (mobile/tablet/desktop) per card |
ucModulePreviewService |
Editor-only preview throttling for heavy modules |
ucCustomVariablesService |
$variable resolution |
ucFavoriteColorsService |
Saved colors, cloud-synced for Pro |
clockUpdateService |
Single 1Hz tick shared across all clock-bearing modules |
ucPresetsService / wordpressPresetsApi
|
Local + Marketplace preset I/O |
ucExportImportService |
Card-level export/import with privacy stripping |
ucPrivacyService |
Strips entity IDs / IPs / tokens before share |
externalCardContainerService |
Manages 3rd-party card containers |
The visual editor lives in src/editor/:
-
ultra-card-editor.ts— the main<ultra-card-editor>element -
uc-modules-selector-tab.ts— module library -
uc-card-selector-tab.ts— native + 3rd-party card library -
uc-presets-selector-tab.ts— local + Marketplace presets -
uc-favorites-selector-tab.ts— favorited modules / colors -
uc-module-selector-shell.ts— the container that hosts the live preview alongside the selector
Each module's per-tab content is rendered by the module class itself (renderGeneralTab, etc.), with shared Actions, Logic, and Design tabs implemented in src/tabs/.
The card config is a single tree:
UltraCardConfig
├─ type: 'custom:ultra-card'
├─ _config_version: number (migration version)
├─ _contentOrigin: 'local' | 'imported' | 'preset_*'
├─ disable_navigation_js_templates: bool
├─ global_css, card_background, card_border_*, card_padding, card_margin, card_overflow
├─ card_shadow_*, ...other card-level design fields
└─ layout
└─ rows: [
{
columns: [
{
modules: [ CardModule, ... ],
...row/column design + logic
}
],
...row design + logic
}
]
Where CardModule is the discriminated union of all 74 module types. See src/types.ts for the full definition (~7000 lines — it's the contract for the whole project).
src/utils/template-migration.ts bumps _config_version and runs migrations for old configs:
-
v1→ modules rely on implicit 8px top/bottom margin default -
v2→ default margin removed; modules carry explicitdesign.margin_* -
v3→ repair pass for v2 partial-margin migration edge cases
_contentOrigin tags every config with its source:
-
local— created in this dashboard -
imported— imported via paste / file -
preset_standard— installed from a vetted marketplace preset -
preset_community— installed from the community marketplace
Some features (Navigation JS templates, custom CSS evaluation) only run when origin is local. See docs/navigation-js-templates.md.
- Template subscriptions are deduped — same template string = one HA subscription.
-
Clock ticks are shared — one
setIntervalfor all visible clocks. - Modules code-split — first paint loads only modules in use.
- Animations pause when the dashboard tab is hidden.
-
Service workers (
src/workers/) offload heavy parsing where appropriate. - Selectors throttle the editor preview when many modules are visible.
If you find performance issues, see FAQ-and-Troubleshooting § Performance.
- Development — local setup and common tasks
-
docs/— per-module reference docs (used as the basis of theModule-*wiki pages) -
/.cursor/rules/— internal patterns for contributors - The TypeScript source — Ultra Card has aggressive JSDoc comments where things get subtle.
Ultra Card · Website · Discord · GitHub Issues · HACS · MIT licensed
- Layout-System
- Logic-and-Conditions
- Templates-and-Jinja
- Actions
- Design-System
- Custom-Variables
- Presets-and-Marketplace
- Pro-and-Cloud
- Modules-Overview
- Content
- Data
- Interactive
- Layout
- Media / Background
- Animated (Pro)
- Inputs (Helpers)
- Card embeds