A modular UI library built for Svelte 5 designed for building web apps. This is a personal project, very unstable and likely to change without notice.
Wrap your application in the UIRoot component to set up the design system's CSS variables and global styles.
<script>
import { UIRoot } from 'svelte-akui';
</script>
<UIRoot>
<!-- Your application content here -->
</UIRoot>
UIRoot: Required parent wrapper. Sets the base font, HSL color tokens, and manages theme state.Panel: A bordered container. Use variant (regular, secondary, accent) to change background colors. Use the tag prop (e.g. tag="section") to specify a custom HTML element for better semantics.Padding: Adds consistent spacing. Use size (small, medium, large) and optional x or y flags to specify axes.Divider: A 1px horizontal or vertical line for visual separation.Components should be composed: wrap any input in a Field to add a label.
Field: Adds a label and hint (helper text) to a child. Automatically links the label to the input ID.TextInput: One-line field. Supports standard types (text, email, tel, url, search, number, date, color). Supports small, medium, and large sizes.ClearableInput: A text field with a one-click "X" button to reset the value. Ideal for search and filter fields.TextArea: Multi-line field. Supports small, medium, and large sizes and vertical resizing.Select: Drop-down menu for picking from options.Typeahead: An input field that supports suggestions and multi-selection via badges. Can be configured for "tagging" mode using allowFreetext, where commas or Enter create new items. Expands downward as content grows.PasswordInput: A text field with a toggle button to show or hide the password characters.InputWithIcon: A layout utility that wraps an input and handles the positioning and spacing for left or right icons/actions.ColourInput: A field with a manual hex code input and a clickable color swatch trigger.InputGroup: Aligns multiple inputs or buttons in a row. Use joined to merge their borders into a single unit. Inherits size to all children.Fieldset: Groups related fields with a legend. Use isInForm for correct ARIA role behavior in form tags.Button: Primary interactive element. Supports regular, accent, and ghost variants, and small, medium, and large sizes. The ghost variant has a transparent background and no border until hover. Can include an icon with iconPosition (left, right, or only).Badge: Informative label or tag. Supports regular and accent variants, backdrop blur, and text glows. Can be used as a link by providing an href.Tabs: A tabbed interface for switching between content sections. Supports a "full-featured" mode with content snippets or a navigation-only mode.Menu: A floating list of actions. Includes MenuButton (trigger), MenuItem (standard item), and MenuDivider. Supports the useMenu() hook for closing from custom controls.Sidebar: Sticky left-hand navigation. Transitions between a fixed desktop view and an overlay mobile view. The sidebar snippet is automatically wrapped in a ControlGroup for navigation items, while the sidebarBody snippet allows for generic content (like Trees or custom layouts).Header: Top navigation and branding bar. Includes a hamburger menu toggle for the sidebar on mobile.FeedItemRow: A standard list item for RSS feeds, news, or activity streams. Supports a title, excerpt, metadata (tag/time), icons, and images. Handles truncation and focus states out of the box.FeedItemsColumn: A vertical list of FeedItemRow components with built-in dividers and column-level snippet overrides for icons and images.DynamicImage: A smart image component that handles aspect-ratio fitting (cover/contain), loading placeholders, and fade-in transitions.FeedItemRow: A flexible grid-based item for list views.
title (2-line truncation) and excerpt (2-line truncation).icon (left) and image (right).tag and time) with custom alignment.<a> tag wrapper with active and focus states.layout="hero" for top-aligned large thumbnails.FeedItemsColumn: A container for stacked feed items.
icon and image snippet overrides for consistent branding (e.g., grayscale favicons).DynamicImage: Smart image handling with smooth transitions.
object-fit selection based on container ratio.Loader: A spinning animation for background tasks.
LoaderOverlay: Covers the parent container with a semi-transparent layer and a loader to block interaction.
Modal: A centered dialog box for critical actions or information. Supports titles, action bars, custom icons via snippets, and optional fullscreen view on mobile.
InfoBox: A non-intrusive notification or reminder. Supports variant (info, warning, error), custom icons or iconSnippet, a title, and an optional action snippet on the right side.
ProgressBar: Visualizes the completion of a task. Supports small, medium, and large sizes, and multiple semantic colors (accent, blue, green, etc.). Features 3D depth with sunken tracks and glowing fills.
Tooltip: A floating label that appears on hover or touch. Use the createTooltip construct to manage state and position. Supports glassmorphism, automatic positioning based on screen edges, and customizable radius (defaults to 's').
<script>
import { createTooltip, Tooltip, Button, Padding } from 'svelte-akui';
const tooltip = createTooltip({ position: 'top' });
</script>
<Button {...tooltip.handlers}>Hover me</Button>
<Tooltip
visible={tooltip.visible}
x={tooltip.x}
y={tooltip.y}
position={tooltip.position}
radius="m"
>
<Padding size="s">Tooltip Content</Padding>
</Tooltip>
Icon: Renders a vector icon by name. Supports custom size (px).Small: Semantic utility for secondary or small-print text..sr-only: CSS class for elements that should be hidden visually but remain accessible to screen readers. Use this for descriptive labels on icon-only buttons or additional context in lists.
<button aria-label="Close">
<Icon name="x" />
<span class="sr-only">Close dialog</span>
</button>
For most applications, you'll want a persistent navigation and branding bar. In svelte-akui, the Sidebar is the primary orchestrator of this layout.
The Sidebar component handles the transitions between desktop and mobile views. It accepts three key snippets: sidebar (the main navigation), header (the top branding bar), and footer (bottom navigation or user profile).
Key Responsibilities:
Header sits at the top of the screen. The Sidebar is fixed to the left.Header remains at the top. The Sidebar becomes a slide-out overlay. The hamburger menu (in the Header) automatically controls the Sidebar visibility.title snippet to the Sidebar which the shell will move between the Header (on desktop) and the Sidebar menu (on mobile) as needed.<script>
import { Sidebar, Header, ControlItem, ControlDivider, Padding } from 'svelte-akui';
let isSidebarOpen = $state(false);
</script>
<Sidebar bind:isOpen={isSidebarOpen}>
<!-- Main navigation -->
{#snippet sidebar()}
<ControlItem label="Dashboard" icon="house" href="/" active />
<ControlItem label="Messages" icon="chat-left-text" href="/messages" />
<ControlDivider />
<ControlItem label="Settings" icon="gear" href="/settings" />
{/snippet}
<!-- Custom sidebar content (e.g. Trees, user profiles) -->
{#snippet sidebarBody()}
<Padding size="m">
<Small>Custom Sidebar Content</Small>
</Padding>
{/snippet}
<!-- Top branding and actions -->
{#snippet header()}
<Header>
{#snippet title()}
<strong>MyApp</strong>
{/snippet}
{#snippet actions()}
<!-- Icons/Buttons for the right side of the header -->
{/snippet}
</Header>
{/snippet}
<!-- Main Application Content -->
<main>
<Padding size="l">
<h1>Dashboard</h1>
<p>Your content goes here.</p>
</Padding>
</main>
</Sidebar>
The input system avoids monolithic components. Use the Field component to wrap raw inputs (TextInput, Select, etc.) to add labels and technical metadata without bloating the individual input components.
Components support small, medium (default), and large sizes. When components are placed inside an InputGroup, they inherit the group's size using Svelte context.
Use Fieldset for groups of related inputs. Setting isInForm correctly assigns accessibility roles for better screen reader support.
svelte-akui supports automatic dark mode switching based on browser preference. By default, UIRoot will detect the system preference if no mode is provided.
For the best experience in a new project:
color-scheme: Set color-scheme: light dark in your global CSS to ensure native elements like scrollbars match the theme on page load before akui takes over.Maintain a consistent visual language of depth to communicate interactivity:
TextInput, TextArea, and Typeahead to signify they are "hollow" containers for data.Glow component to add soft, neumorphic transitions that enhance these effects without harsh borders.Any custom component nested inside a Menu can trigger it to close using the useMenu hook. This is useful for interactive content or custom actions that aren't using the standard MenuItem.
<script>
import { useMenu } from 'svelte-akui';
const menu = useMenu();
</script>
<button onclick={() => menu?.close()}> Clicking this will close the menu </button>
Forms and other interactive elements inside a Menu will not close the menu by default because click events are stopped at the menu container level. Only explicit calls to menu.close() or clicking a MenuItem will trigger a closure.
The Sidebar component's sidebar snippet is rendered inside a ControlGroup (<ul>) with role="navigation". This ensures consistent spacing and dividers between items. For generic content that shouldn't be wrapped in a list (like a Tree or a custom layout), use the sidebarBody snippet.
To maintain valid HTML, children within the sidebar snippet should be ControlItem, ControlDivider, or ControlContent (which wrap their content in <li>).
<Sidebar>
{#snippet sidebar()}
<!-- Standard nav items -->
<ControlItem label="Dashboard" icon="house" href="/" />
<ControlDivider />
<!-- Non-nav content still benefits from the layout -->
<ControlContent>
<div class="user-profile">
<img src="..." alt="" />
<span>User Name</span>
</div>
</ControlContent>
{/snippet}
</Sidebar>
<!-- sidebarBody is unwrapped -->
<Sidebar>
{#snippet sidebarBody()}
<div class="my-tree">...</div>
{/snippet}
</Sidebar>
ARIA Notes:
Header component automatically links to the sidebar via aria-controls="akui-sidebar-navigation".ControlGroup manually within a ControlContent if needed, but be mindful of nesting <ul> tags inappropriately.