npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

desert-ui

v0.1.4

Published

A themeable Svelte 5 component library — 42 components, 18 themes, zero hardcoded colors.

Readme

desert-ui

A themeable Svelte 5 component library — 42 components, 18 themes, zero hardcoded colors, runtime theme-switching.

desert ui — hero


Highlights

  • 42 components — primitives, feedback, data display, layout, overlays, forms
  • 18 built-in themes — 11 dark + 7 light, from warm stone to Primer navy
  • Runtime theming — swap an entire theme with a single prop; no re-render flicker
  • Responsive-first — sidebars become drawers, modals become bottom sheets at ≤1024px
  • Svelte 5 runes$state, $derived, $props, snippets, {@render}
  • Tailwind 4 peer dependency — no config file; CSS-first
  • Ships typed — every component's props, variants, and option types exported

Screenshots

Default dark — component showcase

Components showcase

Figmint — light theme

Figmint light theme

Source theme — Table with status badges

Table in Source theme


Install

npm install desert-ui

Peer dependencies (you must have these installed in your app):

npm install svelte@^5 tailwindcss@^4

Setup

1. Wrap your app with <ThemeProvider>

In your root layout (e.g. src/routes/+layout.svelte):

<script lang="ts">
	import { ThemeProvider, defaultDarkTheme } from 'desert-ui';
	import 'desert-ui/styles/base.css';

	let { children } = $props();
</script>

<ThemeProvider theme={defaultDarkTheme}>
	{@render children()}
</ThemeProvider>

2. Import Tailwind and use components

Your src/app.css (or wherever you import Tailwind):

@import 'tailwindcss';

Then anywhere:

<script>
	import { Button, Card, SectionHeading, MetricCard } from 'desert-ui';
</script>

<Card>
	<SectionHeading title="Dashboard" />
	<MetricCard label="Active users" value="12,480" hint="+4.2% vs last month" />
	<Button variant="accent">Go</Button>
</Card>

Theming

A Theme is a typed object. <ThemeProvider> walks it and writes CSS custom properties (--ds-*) onto its wrapping element. Every component styles itself with those variables — never with raw Tailwind colors. Swapping the theme prop is instant.

18 built-in themes

// Dark themes
import {
	defaultDarkTheme,  // warm stone + emerald
	claudeDarkTheme,   // terracotta + warm black
	merchantTheme,     // commerce green + cool black
	atlasTheme,        // sky blue + graphite
	cipherTheme,       // mint green + true black
	sourceTheme,       // link blue + Primer navy
	emberTheme,        // warm orange + charcoal
	amethystTheme,     // violet + cool gray
	reefTheme,         // teal + slate blue
	carbonTheme,       // IBM red + true black
	auroraTheme,       // aurora green + dark blue
} from 'desert-ui';

// Light themes
import {
	sandLightTheme,    // sand + emerald
	figmintTheme,      // parchment cream + mint
	blossomTheme,      // pink-white + rose
	linenTheme,        // warm beige + forest green
	glacierTheme,      // ice white + ocean blue
	meadowTheme,       // pale mint + leaf green
	ivoryTheme,        // warm white + indigo
} from 'desert-ui';

Author your own

import type { Theme } from 'desert-ui';

export const brandX: Theme = {
	name: 'brand-x',
	isDark: true,
	tokens: {
		surface: { /* ... */ },
		text: { /* ... */ },
		accent: { /* ... */ },
		// ...see src/lib/theme/types.ts for the full shape
	}
};

Switch themes at runtime

<script>
	import { ThemeProvider, defaultDarkTheme, sandLightTheme } from 'desert-ui';
	let active = $state(defaultDarkTheme);
</script>

<button onclick={() => (active = sandLightTheme)}>Light</button>
<button onclick={() => (active = defaultDarkTheme)}>Dark</button>

<ThemeProvider theme={active}>
	<!-- the whole app re-tokenizes instantly -->
</ThemeProvider>

Responsiveness

breakpoint.isMobile is a rune-backed getter that flips at 1024px. Layout components (Sidebar, MobileDrawer, MobileTopbar) use CSS queries internally. Overlays (Modal, BottomSheet) use the rune directly to pick a presentation style.

<script>
	import { breakpoint, Modal, BottomSheet } from 'desert-ui';

	let open = $state(false);
</script>

{#if breakpoint.isMobile}
	<BottomSheet bind:open>
		<!-- ... -->
	</BottomSheet>
{:else}
	<Modal bind:open>
		<!-- ... -->
	</Modal>
{/if}

Loading state

loadingState is a dual-counter store: pending API requests and route navigations both push the top-edge <NProgressBar /> into its "loading" mode, with debounced show/hide so it never flashes.

Wire it into your API client and router once:

import { loadingState } from 'desert-ui';

export async function apiRequest(url: string, init?: RequestInit) {
	loadingState.beginRequest();
	try {
		return await fetch(url, init);
	} finally {
		loadingState.endRequest();
	}
}
<!-- routes/+layout.svelte -->
<script>
	import { NProgressBar, loadingState } from 'desert-ui';
	import { beforeNavigate, afterNavigate } from '$app/navigation';

	beforeNavigate(() => loadingState.startRoute());
	afterNavigate(() => loadingState.endRoute());
</script>

<NProgressBar />

Available components

Primitives (9)

AccessStateCard · BrandMark · Button · DesertLogo · Flag · Icon · LinkifiedText · SectionHeading · Typography

Feedback (6)

LoadingPanel · NProgressBar · ProgressBar · SkeletonBlock · Spinner · TableSkeleton

Data display (8)

Accordion · Card · EmptyState · MetricCard · Pill · SegmentedBar · StatusBadge · Table

Layout (7)

AppShell · Breadcrumb · MobileDrawer · MobileTopbar · PageHeader · Sidebar · SidebarNav

Overlays (4)

BottomSheet · Dropdown · Modal · SpotlightSearch

Forms (10)

ChoiceCard · Combobox · DatePicker · DateRangePicker · FieldLabel · FileDropzone · Select · TextInput · Textarea · TimePicker


Development

npm install          # install dependencies
npm run dev          # playground at http://localhost:5173
npm run storybook    # Storybook at http://localhost:6006
npm run check        # svelte-check + TypeScript
npm run build        # build + publint-checked package in dist/

Project layout

src/lib/
├── primitives/     — leaf components (Button, Icon, Typography, …)
├── feedback/       — loaders, skeletons, progress
├── data-display/   — cards, badges, tables, accordions
├── layout/         — AppShell, Sidebar, MobileDrawer
├── overlays/       — Modal, BottomSheet, Dropdown
├── forms/          — inputs, selects, date/time pickers, dropzones
├── stores/         — breakpoint, loadingState
├── theme/          — ThemeProvider, types, 18 built-in themes
├── styles/         — base.css (resets + skeleton + animations)
└── utils/          — cn()

Every component has a .stories.svelte file beside it — run npm run storybook to explore variants interactively and preview themes via the toolbar.


Philosophy

  1. Tokens over classes. Components reference var(--ds-*), never bg-emerald-500. Themes are the only place colors live.
  2. One breakpoint. 1024px is the one line that splits "mobile" from "desktop". Consistency > granularity.
  3. Snippets over slot prop hell. Composition uses Svelte 5 snippets so consumers fully control layout bits like workspace switchers, sidebar footers, and modal CTAs.
  4. No surprises. No hidden global state, no side-effectful imports, no component-local fetch. Everything observable is a store you can wire into.

License

MIT