Svelte app ui
  • Svelte 78.5%
  • TypeScript 13.4%
  • CSS 3.6%
  • MDX 2.7%
  • JavaScript 1.1%
  • Other 0.7%
Find a file
Ankit Patial a1996c0b35 v0.9.2 - accept SvelteKit 3 prereleases; trim unused peer dependencies
Widen the @sveltejs/kit peer range to "^2.49.0 || ^3.0.0-next.0" so apps
tracking kit@next (currently 3.0.0-next.12) install without a peer warning.
The "-next.0" lower bound is required: npm semver excludes prereleases from
a range unless a comparator shares the same major.minor.patch tuple and
carries a prerelease tag, so plain ^3.0.0 does not match 3.0.0-next.12.

Verified against the 3.0.0-next.12 tarball that all four $app/* imports the
library uses still resolve: browser ($app/environment), afterNavigate
($app/navigation), resolve ($app/paths), page ($app/state). Kit 3 removes
none of them. $app/environment is now a deprecation shim that re-exports
$app/env and warns in dev; kept as-is because $app/env sits behind the
experimental.explicitEnvironmentVariables flag in kit 2, and the ^2.49.0
half of the range must keep working.

Drop peers that src/lib never resolves:
  - svelte-check                   CLI type-checker, never imported
  - @tailwindcss/postcss           build tooling; this repo uses the Vite plugin
  - @tailwindcss/vite              build tooling; consumer's choice
  - @tailwindcss/aspect-ratio      obsolete in Tailwind 4 (aspect-* is core)
  - @tailwindcss/container-queries obsolete in Tailwind 4 (folded into core)
  - @tailwindcss/typography        no prose classes in src/lib

@tailwindcss/forms is retained: base.css counter-patches the specific CSS it
emits for .checkbox/.radio, so that reset needs a version contract.

Also syncs package-lock.json, which was stale at 0.9.0. Its large textual
diff is npm re-serialisation; semantically it is the version bump plus six
transitive entries under @tailwindcss/oxide-wasm32-wasi, with no removals
and no other version changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 21:18:36 +05:30
.storybook v0.7.42 - dependency upgrades, publish fix, formatting 2026-07-25 12:29:21 +05:30
docs/superpowers v0.7.42 - dependency upgrades, publish fix, formatting 2026-07-25 12:29:21 +05:30
src v0.9.1 - anchor floating panels to the viewport, not the scroll ancestor 2026-07-25 19:25:58 +05:30
static tweaking on form 2026-03-26 12:15:19 +05:30
.editorconfig Form init fix 2025-11-30 21:00:15 +05:30
.gitignore DataTable and DataList 2026-02-28 16:25:08 +05:30
.npmrc initial setup 2025-11-10 11:36:53 +05:30
.postcss.config.js v0.4.6 - Fixed Prettier compatibility by downgrading to v3.3.3 2025-12-10 19:37:47 +05:30
.prettierignore v0.4.6 - Fixed Prettier compatibility by downgrading to v3.3.3 2025-12-10 19:37:47 +05:30
.prettierrc initial setup 2025-11-10 11:36:53 +05:30
CLAUDE.md v0.9.0 - apply top-layer popover to all floating panels; fix dev script 2026-07-25 17:23:39 +05:30
LICENSE Initial commit 2025-09-25 16:22:11 +00:00
llms.txt modal: add confirmOnDirty guard to ModalWithForm 2026-07-18 20:19:14 +05:30
Makefile relase tag change 2026-02-28 12:18:14 +05:30
package-lock.json v0.9.2 - accept SvelteKit 3 prereleases; trim unused peer dependencies 2026-07-28 21:18:36 +05:30
package.json v0.9.2 - accept SvelteKit 3 prereleases; trim unused peer dependencies 2026-07-28 21:18:36 +05:30
README.md v0.7.42 - dependency upgrades, publish fix, formatting 2026-07-25 12:29:21 +05:30
svelte.config.js initial setup 2025-11-10 11:36:53 +05:30
tailwind.config.js v0.7.42 - dependency upgrades, publish fix, formatting 2026-07-25 12:29:21 +05:30
tsconfig.json v0.4.6 - Fixed Prettier compatibility by downgrading to v3.3.3 2025-12-10 19:37:47 +05:30
vite.config.ts v0.7.42 - dependency upgrades, publish fix, formatting 2026-07-25 12:29:21 +05:30

kuku-ui

Beautiful, accessible Svelte 5 + DaisyUI component library with Tailwind CSS 4.

MIT License

Installation

npm install kuku-ui

Peer Dependencies

Your project needs these installed:

npm install svelte@^5 @sveltejs/kit@^2 tailwindcss@^4 daisyui@^5 yup@^1
npm install @tailwindcss/vite @tailwindcss/forms @tailwindcss/typography

CSS Setup

Add these lines to your main CSS file (e.g. app.css), after @import "tailwindcss":

@import 'tailwindcss';
@source '../node_modules/kuku-ui/src/lib/**/*.svelte';
@import 'kuku-ui/base.css';
  • @source — tells Tailwind to scan kuku-ui component templates for utility classes
  • @import "kuku-ui/base.css" — provides shared theme tokens, base styles, and utility overrides that components depend on

If you use the Form component (which generates grid column classes dynamically), also add:

@source inline(
	'sm:col-span-1 sm:col-span-2 sm:col-span-3 sm:col-span-4 sm:col-span-5 sm:col-span-6 sm:col-span-7 sm:col-span-8 sm:col-span-9 sm:col-span-10 sm:col-span-11 sm:col-span-12'
);

Quick Start

Import components from kuku-ui:

<script lang="ts">
	import { Button, Modal, toast, Toaster } from 'kuku-ui';
</script>

<Button look="primary" onclick={() => toast.success('Done', 'It works!')}>Click me</Button>

<Toaster />

Components

Buttons

Component Description
Button Primary button with variants, loading state, and size options
ButtonIcon Button with icon + text side-by-side
ButtonLink Anchor element styled as a button
BtnFloat Fixed floating action button (bottom-right)
BtnSwap Toggle button with swap animation (sun/moon theme toggle)
<Button look="primary" size="md" busy={loading} outline>Save</Button>
<Button look="error" size="sm" circle><Cancel /></Button>
<ButtonLink href="/about" look="accent">About</ButtonLink>

Button looks: neutral | primary | secondary | accent | info | success | warning | error | ghost | link

Form System

The Form component renders fields from a schema with built-in Yup validation:

<script lang="ts">
	import { Form, strRequired, emailRequired, type FormSchema } from 'kuku-ui';

	const schema: FormSchema = [
		[
			[
				{ name: 'name', type: 'text', label: 'Full Name', col: '6', validate: strRequired },
				{ name: 'email', type: 'email', label: 'Email', col: '6', validate: emailRequired }
			]
		],
		[[{ name: 'bio', type: 'textarea', label: 'Bio', col: '12' }]]
	];

	async function handleSubmit(data: { name: string; email: string; bio: string }) {
		await api.save(data);
	}
</script>

<Form id="profile" {schema} onSubmit={handleSubmit} submitText="Save Profile" />

Individual Form Components

Component Description
Textbox Text/email/password/number input with autocomplete
Textarea Multi-line text input
Select Dropdown with options
Checkbox Checkbox with label
Radio Radio button group
SearchList Async search with single/multi-select and badges
Phone International phone input with country selector and masking
DateTimePicker Calendar picker with optional time selection
TimePicker Time-only picker with minute intervals
<Textbox name="city" label="City" value={city} onChange={(n, v) => (city = v)} />

<SearchList
	name="tags"
	label="Tags"
	multiple
	onSearch={async (q) => await fetchTags(q)}
	bind:value={selectedTags}
/>

<DateTimePicker
	name="date"
	label="Event Date"
	format="YYYY-MM-DD"
	showTime
	minuteInterval={15}
	bind:value={eventDate}
/>

<Phone name="phone" label="Phone" defaultCountry="US" bind:value={phone} />

Validation Helpers

import {
	strRequired,
	strOptional,
	emailRequired,
	emailOptional,
	numRequired,
	validatePwd,
	validatePhone,
	validateMinLength,
	validateMaxLength,
	validateNumberRange,
	validateUrl
} from 'kuku-ui';

Modals

<script lang="ts">
	import { Modal, ModalWithForm } from 'kuku-ui';
	let open = $state(false);
</script>

<!-- Simple modal -->
<Modal bind:open title="Confirm" onOk={handleOk} onCancel={() => (open = false)}>
	<p>Are you sure?</p>
</Modal>

<!-- Modal with integrated form -->
<ModalWithForm
	{open}
	title="Edit User"
	formID="edit-user"
	formSchema={schema}
	formInitialData={user}
	onSubmit={handleSubmit}
	onCancel={() => (open = false)}
/>

Sizes: xs | sm | md | lg | xl

Toast Notifications

<script lang="ts">
	import { toast, Toaster } from 'kuku-ui';
</script>

<!-- Place once in your layout -->
<Toaster />

<!-- Trigger from anywhere -->
<button onclick={() => toast.success('Saved', 'Your changes have been saved')}> Save </button>
<button onclick={() => toast.error('Error', 'Something went wrong')}> Error </button>

Methods: toast.info() | toast.success() | toast.warning() | toast.error()

Each accepts (title, message, clearInSeconds?) where clearInSeconds defaults to 7 (use 0 for persistent).

Alert

<Alert type="warning" title="Heads up">This action cannot be undone.</Alert>

Types: info | success | warning | error

ConfirmAction

Wraps any trigger (button, link, toggle) with a confirmation dialog:

<ConfirmAction
	title="Delete Item"
	onConfirm={async () => await deleteItem(id)}
	askForReason
	btnLook="error"
>
	Delete
</ConfirmAction>

Icons

39 SVG icons with optional outline variants:

<script lang="ts">
	import { Calendar, CalendarOutline, Home, Trash, Search } from 'kuku-ui';
	import Icon from 'kuku-ui/svg/Icon.svelte';
</script>

<!-- Direct import -->
<Calendar cls="h-5 w-5" />

<!-- Icon wrapper with outline toggle -->
<Icon icon="home" outline />

Available icons: bar3 calendar cancel checkcircle chevron-left chevron-right error eye folder home info moon pencil pencil-square success sun search trash user warning

DataTable

Server-side paginated table with virtual scrolling, sorting, search, row selection, and mobile card view:

<script lang="ts">
	import { DataTable, type DataTableColumn, type DataTableFetchParams } from 'kuku-ui';

	type User = { id: number; name: string; email: string; role: string };

	const columns: DataTableColumn<User>[] = [
		{ key: 'name', label: 'Name', sortable: true },
		{ key: 'email', label: 'Email', sortable: true },
		{ key: 'role', label: 'Role' }
	];

	async function fetchUsers({ page, pageSize, search, sortKey, sortDir }: DataTableFetchParams) {
		const res = await fetch(`/api/users?page=${page}&size=${pageSize}&q=${search ?? ''}`);
		return res.json(); // { rows: User[], total: number }
	}
</script>

<DataTable {columns} onFetch={fetchUsers} pageSize={50} cls="h-[600px]" />

Features: virtual scroll for 100K+ rows, column sorting, search with debounce, row selection (selectable), custom cell renderers, mobile-responsive card view, toolbar snippet, empty state snippet.

DataList

Simpler alternative to DataTable — renders arbitrary items via a snippet instead of columns. Same virtual scroll + infinite scroll engine:

<script lang="ts">
	import { DataList, type DataTableFetchParams } from 'kuku-ui';

	type User = { id: number; name: string; email: string };

	async function fetchUsers({ page, pageSize, search }: DataTableFetchParams) {
		const res = await fetch(`/api/users?page=${page}&size=${pageSize}&q=${search ?? ''}`);
		return res.json(); // { rows: User[], total: number }
	}
</script>

<DataList fetcher={fetchUsers} pageSize={50} itemHeight={80} cls="h-[600px]">
	{#snippet itemSnippet(row: User)}
		<div class="flex items-center gap-4 border-b px-4 py-3">
			<div class="font-medium">{row.name}</div>
			<div class="text-sm text-gray-500">{row.email}</div>
		</div>
	{/snippet}
</DataList>

Features: virtual scroll for 100K+ rows, infinite scroll pagination, search with debounce, toolbar snippet, empty state snippet, refresh() method.

DataSelect

Versatile select/combobox with search, single/multi-select, grouped options, async search, and creatable items:

<script lang="ts">
	import { DataSelect, type Option, type OptionGroup } from 'kuku-ui';

	const colors: Option[] = [
		{ key: 'red', value: 'Red' },
		{ key: 'blue', value: 'Blue' }
	];

	let selected = $state<Option | undefined>();
	let multiSelected = $state<Option[]>([]);
</script>

<!-- Single select with static options -->
<DataSelect name="color" label="Color" options={colors} bind:value={selected} />

<!-- Multi-select with async search and creatable -->
<DataSelect
	name="tags"
	label="Tags"
	multiple
	creatable
	onSearch={async (q) => await fetchTags(q)}
	bind:value={multiSelected}
/>

Features: client-side filtering, async search with debounce, grouped options (OptionGroup[]), multi-select with badge chips, creatable items, custom item/group/create snippets, clearable, keyboard navigation.

Other Components

Component Description
Badge Small decorative label with color and size variants

Portal Action

Teleport DOM elements to a different location:

<script lang="ts">
	import { portal, createPortal } from 'kuku-ui';
</script>

<!-- Target location -->
<div use:createPortal={'my-portal'}></div>

<!-- Content will be moved to target -->
<div use:portal={'my-portal'}>This renders at the target location</div>

Drag and Drop

Custom drag-and-drop with HTML5 Drag API and Pointer Events:

<script lang="ts">
	import { draggable } from 'kuku-ui/drag-and-drop/action-dragable';
	import { droppable } from 'kuku-ui/drag-and-drop/action-dropable';
</script>

<div use:draggable={{ container: 'list-a', dragData: item }}>Drag me</div>

<div
	use:droppable={{
		container: 'list-b',
		callbacks: {
			onDrop: (data) => handleDrop(data)
		}
	}}
>
	Drop here
</div>

Styling

kuku-ui uses Tailwind CSS 4 with DaisyUI 5. Components accept cls props for custom classes:

<Button cls="w-full mt-4" look="primary">Full Width</Button>

All form components support a col prop ('1'-'12') for grid layout control and a size prop ('xs' | 'sm' | 'md' | 'lg').

Development

npm run dev          # Dev server
npm run storybook    # Storybook on :6006
npm run check        # Type check
npm run build        # Build library
npm run format       # Prettier

License

MIT