Skip to content

Latest commit

Β 

History

443 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Svelte Widgets
Svelte Widgets

Tests GitHub Pages NPM version Needs Svelte version Playground Open in StackBlitz

Keyboard-friendly, accessible and highly customizable Svelte components. View the docs

🧩   Components

Every component is a named export from the package root, and every one also has a direct subpath import (svelte-widgets/Toc.svelte) so bundlers can skip the rest.

Component What it does Docs
MultiSelect Keyboard-friendly multi/single select with grouping, async loading and deep style hooks docs
CommandMenu Command palette with fuzzy search, hotkeys, recents and async actions docs
PageSearch Pagefind-backed site search built on CommandMenu docs
Popover Floating surface that positions, dismisses and traps focus for you docs
ActionMenu Action list opened from a trigger or right-click, with complete menu keyboard semantics docs
ConfirmDialog Promise-based dialog queue, so two racing prompts can't share one answer docs
Dialog Native modal with composable sections, close reasons and nested-dialog handling docs
DraggablePane Floating panel you can drag by its header, resize and reset to its anchor docs
NumberRangeInput Paired number and range inputs bound to one value, with optional schema defaults docs
SettingsGroup Collapsible group for organizing related settings sections docs
SettingsSearch Settings-row filter that expands matching groups and restores their prior state docs
SettingsSection Titled settings region with change tracking, resets, descriptions and shared-grid layout docs
Sheet Dialog-based modal edge panel with side placement and shared dismissal policies docs
Tabs Controlled ARIA tabs with automatic or manual keyboard activation docs
Accordion Single or multi-open disclosure group with snippet-rendered content docs
FindBar In-DOM find-in-page bar that highlights, counts and steps through matches docs
CodeEditor Virtualized editable code surface with injectable highlighting and persistence docs
DiffView Virtualized side-by-side and unified diffs with an injectable backend docs
Toast Notification queue with priorities, dedupe and pause-on-hover docs
Nav Navigation bar with dropdowns, pinning and active-route styling docs
Toc Sticky table of contents that finds and tracks its own headings docs
Masonry Column-balancing masonry grid with SSR support and virtualization docs
Footer Centered row of icon links, sized and themed with --footer-* docs
ActionButton Async action button with pending, success and error feedback docs
CopyButton Copy-to-clipboard button with success and error feedback docs
ButtonGroup Segmented control over a set of options, single or multi select docs
FullscreenButton Fullscreen toggle scoped to one wrapper, so viewers don't fight over the flag docs
ThemeToggle Light/dark/system theme cycler with persistence and cross-tab synchronization docs
Toggle Accessible switch with a bindable checked docs
CodeExample Collapsible source viewer used by the live examples docs
FileDetails Collapsible <details> viewer for a set of files docs
PrevNext Previous/next links for sequential pages docs
SubpageGrid Card grid linking to child pages docs
Icon Inline SVG icon from the bundled set docs
GitHubCorner The classic corner ribbon link docs
CircleSpinner Minimal loading spinner docs
ContributorList Avatar row of GitHub contributors, grayscale until hover docs
LiteYouTubeEmbed YouTube poster that only loads the player iframe once clicked docs
Wiggle Spring-animated shake wrapper docs

Fifteen attachments work on any element: fourteen come from svelte-widgets/attachments, while heading_anchors has its own subpath. dismiss_on_outside_press is the lower-level multi-surface primitive behind click_outside.

<script>
  import { CommandMenu, MultiSelect, Popover, Tabs, Toc } from 'svelte-widgets'
</script>

πŸ’‘   Features

  • No run-time deps: every component needs only Svelte as a peer dependency
  • Keyboard friendly: every interactive component is fully operable without a mouse
  • Bindable: component state is exposed through $bindable props, so you can both read it and drive it from the outside
  • Themeable: CSS variables with sensible defaults on every element, plus prop bags to spread arbitrary attributes onto internals
  • SSR-safe: nothing touches window or localStorage before mount
  • Typed: props, snippets and events are inferred from the data you pass

πŸ§ͺ   Coverage

Statements Branches Lines
Statements Branches Lines

πŸ”¨   Installation

npm install --dev svelte-widgets

🚚   Migrating from svelte-multiselect

This package was called svelte-multiselect up to v11 (#432). Swap it out:

npm uninstall svelte-multiselect && npm install -D svelte-widgets

Then rewrite the imports. Matching on the opening quote (all three kinds) keeps prose and GitHub URLs untouched, and covers every subpath along with the bare import. It skips .md deliberately: in markdown a backtick-quoted mention is usually prose, not an import.

find src -type f \( -name '*.svelte' -o -name '*.ts' -o -name '*.js' \) -exec perl -pi -e "s{(['\"\`])svelte-multiselect}{\$1svelte-widgets}g" {} +

Three things the rewrite cannot do for you: CmdPalette is now CommandMenu and PagefindPalette is now PageSearch (#428), and click_outside changed shape (it dismisses on pointerdown, and exclude/include merged into one inside option) (#431). See the changelog for the details.

Coming from svelte-toc or svelte-bricks instead? Those are now Toc and Masonry here (#432), so the same swap applies with import { Toc } from 'svelte-widgets' and import { Masonry } from 'svelte-widgets'.

πŸ“¦   Subpath exports

Components have direct .svelte entry points, and headless/build-time APIs have focused subpaths:

import {
  auto_update_position, // coalesce floating-position updates and clean up listeners
  click_outside, // dismiss a surface when a press lands outside it
  draggable,
  float, // park an element next to an anchor and keep it there
  focus_trap, // keep Tab inside a surface, hand focus back when it closes
  highlight_matches,
  hotkey, // declarative keybindings, `mod` maps to Cmd or Ctrl
  register_escape_layer, // add a handler to the shared LIFO Escape stack
  sortable,
  tooltip,
} from 'svelte-widgets/attachments'
import { compute_position, fuzzy_match, get_label } from 'svelte-widgets/utils'
import { heading_anchors } from 'svelte-widgets/heading-anchors'
Subpath API
/attachments Element attachments and dismissal primitives
/clipboard Clipboard feedback state
/code-editor Backend-agnostic editing, diff rendering and primitives
/code-editor/editor.css Shared syntax-token and diff-view styles
/dialogs Queued choice, confirmation and prompt requests
/file-drop Directory expansion and accept filtering
/find-in-page Reactive find-in-page cursor behind FindBar
/fullscreen Shared fullscreen state
/heading-anchors Heading ID preprocessor, slugger and anchor attachment
/icons Dynamic icon registry
/katex KaTeX before/after preprocessor pair
/live-examples mdsvex live-example transform, Vite plugin and highlighter
/live-examples/create-highlighter Lightweight custom grammar highlighter factory
/print Element printing
/storage Non-throwing localStorage, persisted choices and MRU lists
/text-search Text ranges, highlighting and search-jump helpers
/theme Headless light/dark/system state
/toast-queue Toast reducer and reactive store
/utils Positioning, fuzzy matching, hotkeys and general helpers
/vite-config This repository's Vite Plus configuration helper

CodeEditor and DiffView take host-supplied EditorBackend and DiffBackend implementations, either through their backend props or once per app with set_editor_backend() and set_diff_backend(). Import svelte-widgets/code-editor/editor.css alongside them for the token palette and shared line metrics. The editor takes a host-owned model={create_editor_model({ uri, text })} whose rope, UTF-16 selection, transactions, dirty checkpoint, and bounded history remain usable at 100 MB / 1,000,000 lines. Saving is an optional callback, so file reads, persistence, conflicts and draft policy remain in the host. The editable DOM temporarily remains a full-document textarea and is therefore still subject to browser textarea and scroll-height limits. Both backend contracts are runtime-agnostic and can call a native process, worker, WASM module or server route.

Run the opt-in, hardware-sensitive editor stress target locally with RUN_LARGE_EDITOR_TESTS=1 npx vp test --run tests/vitest/code-editor-model.test.ts; normal CI deliberately skips it.

For $…$ and $$…$$ math in mdsvex, wrap mdsvex with katex_preprocess() and run heading_ids() last:

import { mdsvex } from 'mdsvex'
import { heading_ids } from 'svelte-widgets/heading-anchors'
import { katex_preprocess } from 'svelte-widgets/katex'

const katex = katex_preprocess()
export default {
  preprocess: [katex.before, mdsvex({ extensions: [`.md`] }), katex.after, heading_ids()],
}

Import katex/dist/katex.min.css once in the app so the generated markup is styled.

Popover and ActionMenu use the browser Popover API for top-layer rendering, light dismissal and Escape handling, while float supplies placement. Explicit custom dismissal policies still use click_outside. Dialog-like popovers can add focus_trap; action menus use Arrow/Home/End navigation and close on Tab so browser focus continues in page order.

<script lang="ts">
  import { ActionMenu, Popover } from 'svelte-widgets'

  const actions = [{ label: `Reload`, action: () => location.reload() }]
</script>

<Popover placement="bottom" align="start">
  {#snippet trigger(props)}
    <button {...props}>Options</button>
  {/snippet}
  <p>Anything you like in here.</p>
</Popover>

<ActionMenu {actions}>
  {#snippet trigger(props)}
    <button {...props}>Page actions</button>
  {/snippet}
</ActionMenu>

<ActionMenu {actions}>
  <div>Right-click anywhere in this region</div>
</ActionMenu>

See src/lib/live-examples/readme.md for optional live-example helpers.

πŸ†•   Changelog

View the changelog.

πŸ™   Contributing

Here are some steps to get you started if you'd like to contribute to this project!

Releases

Used by

Contributors

Languages