Svelte UI Design Guide
Component Library Priority
Always use @podman-desktop/ui-svelte first. This library provides
Podman Desktop's design system. Only create custom components when the library
does not cover the use case.
- Source: https://github.com/podman-desktop/podman-desktop/tree/main/packages/ui
- Storybook: https://podman-desktop.io/storybook
<script lang="ts">
import { Button, Input, Table, FormPage, NavPage, EmptyScreen } from '@podman-desktop/ui-svelte';
</script>
Check API
Component APIs change across versions. Read the installed package directly:
- List all exports: Read
node_modules/@podman-desktop/ui-svelte/dist/index.d.ts - Check component props: Read the
.d.tsfile for any component, e.g.node_modules/@podman-desktop/ui-svelte/dist/button/Button.svelte.d.ts - Browse all component dirs:
ls node_modules/@podman-desktop/ui-svelte/dist/ - Check package exports: Read
node_modules/@podman-desktop/ui-svelte/package.jsonfor the fullexportsmap (shows all importable paths) - See how we already use them:
grep -r "from '@podman-desktop/ui-svelte'" packages/frontend/src/
Always verify props against the actual .d.ts files before using a component.
Svelte 5 Runes (Required)
This project uses Svelte 5 runes. Never use legacy Svelte 4 reactive syntax.
<script lang="ts">
// Correct: Svelte 5 runes
let items = $state<MyItem[]>([]);
let loading = $state(true);
let filtered = $derived(items.filter(i => i.active));
$effect(() => {
fetchItems().then(result => {
items = result;
loading = false;
});
});
// Wrong: Legacy Svelte 4
// $: filtered = items.filter(i => i.active);
// let items = writable([]);
</script>
Props Pattern
<script lang="ts">
// Svelte 5 props with defaults
let {
title,
items = [],
onselect,
}: {
title: string;
items?: MyItem[];
onselect?: (item: MyItem) => void;
} = $props();
</script>
Routing
Hash-based routing via tinro. Routes are defined in App.svelte.
<!-- App.svelte -->
<Route path="/my-feature" breadcrumb="My Feature">
<MyFeature />
</Route>
<!-- Add navigation entry in Navigation.svelte -->
<SettingsNavItem title="My Feature" href="/my-feature" icon={faMyIcon} selected={meta.url.startsWith('/my-feature')} />
Navigate programmatically:
import { router } from 'tinro';
router.goto('/disk-images/build');
Navigation helpers are in packages/frontend/src/lib/navigation.ts.
RPC Reactive Stores
Auto-refreshing stores that subscribe to backend messages:
import { rpcReadable } from '/@/stores/rpcReadable';
import { bootcClient } from '/@/api/client';
import { Messages } from '/@shared/src/messages/Messages';
export const imageInfos = rpcReadable<ImageInfo[]>([], Messages.MSG_IMAGE_UPDATE, () => bootcClient.listBootcImages());
Use in components:
<script lang="ts">
import { imageInfos } from '/@/stores/imageInfo';
let images = $derived($imageInfos);
</script>
Tailwind CSS
Use Podman Desktop CSS variables (var(--pd-*)) for all theme-dependent styles.
Font sizes are smaller than Tailwind defaults (text-base = 12px).
Common variables: --pd-content-card-bg, --pd-content-text, --pd-content-header,
--pd-content-divider, --pd-button-primary-bg.
For the full Tailwind config (custom font sizes, content paths, theme overrides):
cat packages/frontend/tailwind.config.js
Grep existing components for CSS variable usage patterns:
grep -rn "var(--pd-" packages/frontend/src/ | head -30
Upstream Components
Local components in packages/frontend/src/lib/upstream/ provide patterns not
covered by @podman-desktop/ui-svelte. Browse them to find available components:
ls packages/frontend/src/lib/upstream/
Checklist: Adding a New UI Page
- Create the component in
packages/frontend/src/orsrc/lib/ - Add route in
App.svelteusing<Route path="..." /> - Add navigation entry in
Navigation.svelteusing<SettingsNavItem /> - Use the appropriate layout:
NavPage(list),FormPage(form), orDetailsPage(detail) - Use
@podman-desktop/ui-sveltecomponents for all standard UI elements - Connect to backend via
bootcClientfrom/@/api/client - Add empty state with
EmptyScreenorFilteredEmptyScreen - Run
pnpm svelte:checkto validate component types - Test with
pnpm watch+ Podman Desktop dev mode