A themeable Svelte 5 component library where every visual property is a CSS custom property. Build any design system on top — no source changes needed.
npm install @juspay/svelte-ui-components
Requires svelte ^5.41.2 and type-decoder ^2.1.0 as peer dependencies.
Most component libraries ship with a fixed look. Changing it means fighting overrides, patching internals, or forking.
This library takes a different approach: components are unstyled by default and expose every visual decision — colors, spacing, typography, borders, shadows, radii — as CSS custom properties. You define the design system. The components render it.
<!-- A button that looks however you want -->
<div class="my-theme">
<Button text="Continue" onclick={handleClick} />
</div>
<style>
.my-theme {
--button-color: #000;
--button-text-color: #fff;
--button-border-radius: 8px;
--button-padding: 12px 24px;
--button-font-size: 14px;
--button-font-weight: 600;
--button-hover-color: #222;
--button-border: 1px solid #333;
}
</style>
<script lang="ts">
import { Button, Input, Toggle, Toast } from '@juspay/svelte-ui-components';
</script>
<!-- Basic button -->
<Button text="Submit" onclick={() => console.log('clicked')} />
<!-- Input with validation -->
<Input
value=""
placeholder="Enter email"
dataType="email"
onStateChange={(state) => console.log(state)}
/>
<!-- Toggle switch -->
<Toggle checked={false} text="Dark mode" onclick={(val) => console.log(val)} />
| Component | Description | Docs |
|---|---|---|
| Button | Action trigger with circular loader, progress bar, icon/children snippets, and aria-expanded support. | docs |
| Input | Text field with built-in validation for email, phone, password, and custom patterns. Supports text transformers and textarea mode. | docs |
| InputButton | Input field fused with action buttons — for search bars, OTP entry, coupon codes. | docs |
| Select | Dropdown picker with single-select (with search), multi-select (checkboxes + Select All + Apply), and custom content slots. | docs |
| Toggle | Labeled on/off switch with sliding ball animation. | docs |
| Checkbox | Styled checkbox input with custom SVG checkmark. | docs |
| Radio | Styled radio button with custom circular indicator. | docs |
| Slider | Range slider with configurable min, max, step, and optional value display. | docs |
| Choicebox | Selectable option group with single-select (radio) or multi-select (checkbox) behavior and custom content. | docs |
| Component | Description | Docs |
|---|---|---|
| Avatar | Circular avatar with image (Img with fallback) or text initial. | docs |
| Badge | Icon with a numeric/text badge overlay in the corner. | docs |
| GridItem | Grid cell with icon, label, and loading overlay animation. | docs |
| Icon | Clickable icon with optional text label. | docs |
| IconStack | Layered horizontal stack of overlapping circular icons/avatars. | docs |
| Img | Image with automatic fallback on load error. | docs |
| ListItem | Multi-section list row with images, labels, and accordion expansion. | docs |
| Pill | Compact label/tag for status or categories, optionally clickable with a11y. | docs |
| Status | Full-screen status display for success/failure screens. | docs |
| Table | Sortable data table with sticky headers and per-cell scrolling. | docs |
| RelativeTime | Auto-updating relative time display ("5 minutes ago") with locale support and optional tooltip. | docs |
| Component | Description | Docs |
|---|---|---|
| Banner | Sticky notification banner with icon snippet, dismiss button, link text, and click interaction. | docs |
| BrandLoader | Full-screen branded splash/loading animation. | docs |
| Loader | Circular spinner with gradient foreground. | docs |
| LoadingDots | Animated inline dot sequence with bounce/pulse animations. | docs |
| Shimmer | Loading placeholder with animated shimmer effect. All visuals via CSS variables. | docs |
| Toast | Animated slide-in notification with type variants and auto-dismiss. | docs |
| Progress | Horizontal progress bar with animated fill. | docs |
| Gauge | Semicircular gauge/meter with configurable segments and animated value. | docs |
| Component | Description | Docs |
|---|---|---|
| Modal | Dialog overlay with configurable size, alignment, header, footer, transitions, and back-press support. | docs |
| ModalAnimation | Fly/fade transition wrapper for modal content. | docs |
| OverlayAnimation | Fade transition wrapper for overlay backgrounds. | docs |
| Sheet | Slide-in panel from any edge (left/right/top/bottom) with header, scrollable content, footer, and focus trap. | docs |
| CommandMenu | Command palette (Ctrl+K) with fuzzy search, grouped commands, and keyboard navigation. | docs |
| ContextMenu | Right-click context menu with nested submenus, separators, and keyboard navigation. | docs |
| Menu | Dropdown action menu with keyboard navigation, typeahead, disabled/danger items, and separators. | docs |
| Tooltip | Hover-triggered tooltip with configurable position and delay. | docs |
| Component | Description | Docs |
|---|---|---|
| Accordion | Expandable/collapsible container with CSS grid animation. | docs |
| Carousel | Horizontal content slider with swipe and pagination dots. | docs |
| CheckListItem | Checklist row with checkbox, label, and toggleable checked state. | docs |
| Pagination | Page navigation with previous/next buttons and numbered page links. | docs |
| Scroller | Overflowing item list with arrow navigation, gradient edges, drag-to-scroll, and snap support. | docs |
| Stepper | Multi-step progress indicator with completed, active, and pending states. | docs |
| Step | Individual step within a Stepper — renders number, label, and connector. | docs |
| Tabs | Tabbed interface with animated active indicator. | docs |
| Toolbar | Fixed header bar with back button, title, and customizable content areas. | docs |
| Component | Description | Docs |
|---|---|---|
| Snippet | Copyable command-line snippet with prompt prefix and copy button. | docs |
| SplitButton | Primary action button with dropdown secondary actions via Menu component. | docs |
| KeyboardInput | Keyboard shortcut display with styled key caps. | docs |
| ThemeSwitcher | Segmented control for light/dark/system theme switching. | docs |
| Component | Description | Docs |
|---|---|---|
| Book | 3D book display with animated page turning. | docs |
| Browser | Browser window chrome frame for content display. | docs |
| Phone | Smartphone device frame for app previews. | docs |
Every component reads its visual properties from CSS custom properties with sensible defaults. To create a theme, define the variables on any ancestor element:
/* theme.css */
.my-design-system {
/* Button */
--button-color: #0070f3;
--button-text-color: #fff;
--button-border-radius: 6px;
--button-padding: 10px 20px;
--button-font-family: 'Inter', sans-serif;
--button-font-size: 14px;
--button-hover-color: #0060df;
/* Input */
--input-background: #fafafa;
--input-border: 1px solid #eaeaea;
--input-radius: 6px;
--input-font-family: 'Inter', sans-serif;
--input-focus-border: 1px solid #0070f3;
/* Toast */
--toast-border-radius: 8px;
--toast-font-family: 'Inter', sans-serif;
--toast-success-background-color: #0070f3;
/* Modal */
--modal-border-radius: 12px;
--modal-content-background-color: #fff;
--background-color: #00000066;
}
<div class="my-design-system">
<Button text="Save" onclick={save} />
<Input value="" placeholder="Search..." />
</div>
Because CSS variables cascade, you can scope different themes to different parts of your app:
<div class="light-theme">
<Button text="Light" onclick={handleClick} />
</div>
<div class="dark-theme">
<Button text="Dark" onclick={handleClick} />
</div>
<style>
.light-theme {
--button-color: #fff;
--button-text-color: #000;
--button-border: 1px solid #eaeaea;
}
.dark-theme {
--button-color: #111;
--button-text-color: #fff;
--button-border: 1px solid #333;
}
</style>
Each component documents its full set of CSS variables in the docs/ directory. The variable naming convention follows the pattern:
--{component}-{element}-{property}
For example:
--button-color — button background color--input-error-msg-text-color — input error message text color--modal-footer-primary-button-border-radius — border radius of the primary button inside a modal footer--toast-success-background-color — background color for success variant toastsComponents use a typed props pattern split into mandatory, optional, and event properties:
// Every component follows this pattern
type ButtonProperties = OptionalButtonProperties & ButtonEventProperties;
type OptionalButtonProperties = {
text?: string;
enable?: boolean;
disabled?: boolean;
showLoader?: boolean;
loaderType?: 'Circular' | 'ProgressBar';
type?: 'submit' | 'reset' | 'button';
icon?: Snippet;
children?: Snippet;
ariaLabel?: string;
ariaExpanded?: boolean;
// ...
};
type ButtonEventProperties = {
onclick?: (event: MouseEvent) => void;
onkeyup?: (event: KeyboardEvent) => void;
};
All types are exported from the package:
import type {
ButtonProperties,
InputProperties,
ModalProperties,
SelectProperties,
ToastProperties,
MenuItem,
SheetSide
// ...
} from '@juspay/svelte-ui-components';
This library is built on Svelte 5 and uses its modern APIs:
$props() for declaring component properties$state() and $derived() for reactive state$bindable() for two-way bound props (e.g., Sheet.open, Banner.visible, Button.showProgressBar)Snippet for composable content slots (e.g., Button.icon, Sheet.content, Banner.rightContent)<script lang="ts">
import { Sheet } from '@juspay/svelte-ui-components';
let open = $state(false);
</script>
<button onclick={() => (open = true)}>Open</button>
<Sheet bind:open title="Settings" side="right">
{#snippet content()}
<p>Sheet body goes here.</p>
{/snippet}
{#snippet footer()}
<button onclick={() => (open = false)}>Done</button>
{/snippet}
</Sheet>
An MCP (Model Context Protocol) server ships as a separate package for AI-assisted development:
npm install @juspay/svelte-ui-components-mcp
It provides full component documentation — props, events, CSS variables, type references — through a standard MCP tool interface. Useful for integrating component knowledge into AI coding assistants.
pnpm install # install dependencies
pnpm dev # start dev server with hot reload
pnpm build # build the library (vite + svelte-package + publint)
pnpm test # run integration + unit tests
pnpm lint # check formatting and lint rules
pnpm format # auto-format source files
src/lib/
{Component}/
{Component}.svelte # component implementation
properties.ts # prop type definitions
types.ts # shared types (ValidationState, InputDataType, etc.)
utils.ts # shared utilities (validateInput, createDebouncer)
index.ts # public exports
docs/ # component documentation (one markdown file per component)
mcp/ # MCP server package (separate npm package)
Pushing to the release branch triggers an automated CI pipeline:
feat: = minor, fix: = patch, ! = major)package.json version and generates a changelog--access public)MIT