@skdx/svelte
Headless, accessible, unstyled Svelte 5 components for the SkandaDX design system.
Every component ships behavior, accessibility, and state — no styles. Components render plain semantic HTML decorated with data-* attributes; you (or a SkandaDX brand package) style them with CSS against those attributes, typically using the --skdx-* tokens from @skdx/tokens. One component tree, any brand.
@skdx/svelte (behavior) + --skdx-* variables (theme) + your CSS (skin) = branded UI
Installation
pnpm add @skdx/svelte
# or: npm install @skdx/svelte
Svelte 5.46 or newer is required (svelte is a peer dependency — components are authored with runes and snippets). The only runtime dependency is @floating-ui/dom, used for positioning overlays. Components ship as .svelte source with TypeScript declarations, so your app's Svelte compiler compiles them.
Quick start
Components are exported unprefixed from the package root; every component also has a subpath export (@skdx/svelte/dialog/Dialog.svelte) if you prefer to import the file directly.
<script lang="ts">
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogTitle,
DialogTrigger,
} from '@skdx/svelte';
</script>
<Dialog>
<DialogTrigger>Edit profile</DialogTrigger>
<DialogContent class="panel">
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>Make changes to your profile here.</DialogDescription>
<DialogClose>Close</DialogClose>
</DialogContent>
</Dialog>
Nothing is styled until you say so:
.panel {
background: var(--skdx-color-semantic-surface);
color: var(--skdx-color-semantic-text);
border-radius: var(--skdx-radius-lg);
padding: var(--skdx-space-6);
}
.panel[data-state='open'] {
animation: fade-in var(--skdx-motion-duration-normal) var(--skdx-motion-easing);
}
Conventions
- Compound components. Complex widgets are split into parts (
Tabs+TabsList+TabsTrigger+TabsContent) that share state through context. You compose exactly the DOM you want. - Data attributes are the styling API. State is exposed as
data-state="open",data-disabled,data-orientation="horizontal", … — never as class names. The same hooks, with the same values, exist in all five framework packages. - Controlled or uncontrolled. Stateful components take
value/defaultValue/onValueChange(open/defaultOpen/onOpenChangefor overlays). Pass the controlled prop to own the state, or thedefault*prop to let the component manage it. - Accessible by default. Roles, ARIA wiring, focus trapping, roving tabindex, typeahead and Esc/outside-click dismissal are built in — see each component's Keyboard table in the docs.
Documentation
Live demos, props tables, keyboard maps and styling hooks for every component (with a Svelte tab on every example): https://skdx.skandadx.dev/components/.
Development
pnpm --filter @skdx/svelte build # svelte-package → dist/
pnpm --filter @skdx/svelte test # vitest + @testing-library/svelte
pnpm --filter @skdx/svelte typecheck # svelte-check
Component demos live in the docs portal (apps/docs): pnpm --filter @skdx/docs dev.