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

@sigitex/glim

v1.10.0

Published

Compact, tactile UI components for SolidJS

Readme

Glim for SolidJS

Compact tactile UI components, scoped design tokens, sizing tokens, dark and light palettes, and five procedural surface textures.

This README sucks!

ToggleButton

ToggleButton is a native button with persistent on/off state exposed through aria-pressed. It accepts Button variants, sizes, native attributes, and events. It defaults to unpressed and type="button".

<ToggleButton defaultPressed>Bold</ToggleButton>
<ToggleButton pressed={pinned()} onPressedChange={setPinned} size="sm">
  Pin
</ToggleButton>

Use defaultPressed for local state or pressed with onPressedChange for controlled state. Disabled buttons do not toggle. Calling preventDefault() in onClick cancels the toggle. Keep the label stable as state changes. Use Switch for a boolean form field instead.

Drawer

Drawer supports all four edges, native modal or modeless behavior, overlay or scoped push layout, and pointer/keyboard resizing. Its default surface is window; surface also accepts panel, inset, group, raised, and overlay.

import { createSignal } from "solid-js";
import { Drawer, DrawerLayout, Button, Surface } from "@sigitex/glim";

function Inspector() {
  const [open, setOpen] = createSignal(false);
  const [size, setSize] = createSignal(320);
  return (
    <DrawerLayout style={{ height: "100dvh" }}>
      <Surface style={{ height: "100%" }}>
        <Button onClick={() => setOpen(true)}>Project details</Button>
        {/* Application content */}
      </Surface>
      <Drawer
        title="Project details"
        description="Settings for the current project."
        open={open()}
        onOpenChange={setOpen}
        placement="right"
        layout="push"
        modal={false}
        resizable
        size={size()}
        onSizeChange={setSize}
        defaultSize={320}
        minSize={160}
        maxSize={600}
        surface="window"
        footer={<Button onClick={() => setOpen(false)}>Done</Button>}
      >
        Project settings
      </Drawer>
    </DrawerLayout>
  );
}

placement defaults to right; layout defaults to overlay; modal, closeOnEscape, closeOnOutsideClick, and showCloseButton default to true. Modal drawers use native focus trapping and document inertness; non-modal drawers leave the application interactive. Escape dismisses only the foremost Drawer or Dialog. Outside dismissal requires a press and click outside, so releasing a resize drag over the page does not dismiss it. Closing restores the opener's focus; outside clicks in non-modal mode preserve focus on the clicked control.

size / onSizeChange control the size, or initialize an uncontrolled drawer with defaultSize (320). Sizes, minSize (160), and maxSize are CSS pixels: width for left/right, height for top/bottom. The available viewport or visible layout bounds always constrain the final size. resizable defaults to false. Focus the exposed resize edge and use arrows to grow/shrink, Shift for ten times resizeStep (8), Home/End for limits, or double-click to restore defaultSize. onResizeEnd reports committed pointer/keyboard resizing; pointer cancellation restores the starting size.

Drawers are flush to viewport edges by default, including inside a DrawerLayout. offset adds an optional outer gutter in CSS pixels (default 0); contained opts into the visible bounds of a DrawerLayout (default false). DrawerLayout defines the content affected by push mode. Push mode requires this ancestor and reserves space on the corresponding edge without altering the document body. Reservations follow size/placement changes and clear on close or unmount. Give contained layouts an explicit height. Contained drawers also fall back to viewport edges when no DrawerLayout is present. Only the drawer body scrolls; header and optional footer stay visible. class and style are supported on both primitives; explicit styles override Drawer's generated geometry. Prefer size and placement props for geometry that should remain synchronized with push-layout reservations. Opening and closing slide the drawer its full width or height over 250ms, without fading the panel or backdrop. Push space follows the visible drawer throughout the slide instead of jumping to its final size. Modal focus remains active until the exit finishes. Both animations are skipped for reduced motion.

For page-wide push behavior, wrap the page—not just the drawer preview—in DrawerLayout. Push mode uses a transparent backdrop, including when modal is true; focus trapping and document inertness still apply. Overlay mode retains its dimming backdrop.

Install

Install the downloaded package with npm install ./sigitex-glim-0.11.0.tgz. It requires SolidJS 1.9. Use Solid's Vite plugin or another Solid JSX compiler.

LED And LEDBar

LED is a recessed indicator lens, off by default. on lights it; tone defaults to success, or set color to a CSS color. Sizes are xs (6px), sm (10px), md (16px, default), lg (24px), and xl (32px), shared by LEDBar. Emitted glow is enabled by default; disable it with glow={false}. A label exposes the light and its on/off state to assistive technology; unlabelled LEDs are decorative, not interactive controls.

LEDBar composes LEDs into determinate progress with accessible numeric bounds. min defaults to 0, max to 100, value to the minimum, and segments to 20 (clamped to 1–100). Only completed segments light, while assistive technology receives the exact clamped value. It shares LED appearance props, defaults to the accent tone, and shows a percentage unless showValue={false}. Neither component animates or blinks.

The bar has no housing, background, or border. orientation defaults to horizontal (left-to-right); vertical fills bottom-to-top with the label and readout above. Optional colorStops={[{ at: 0, color: "var(--g-success)" }, { at: 70, color: "var(--g-warning)" }, { at: 90, color: "var(--g-error)" }]} creates discrete color zones. at is a percentage of the range, independent of min/max; LEDs use their segment's starting percentage. Both lit and unlit lenses retain their zone color. Stops are sorted without changing the supplied array, finite positions clamp to 0–100, and later stops win at duplicate positions. Invalid positions and empty colors are ignored. Below the first stop, or without stops, color/tone applies.

<LED label="Power" on={powered()} tone="success" />
<LED on color="#ff9900" size="lg" glow={false} />
<LEDBar label="Charge" value={charge()} segments={20} tone="success" />
<LEDBar label="Signal" min={-60} max={0} value={-12} showValue={false} />
<LEDBar label="Level" value={level()} orientation="vertical" colorStops={[{ at: 0, color: "var(--g-success)" }, { at: 70, color: "var(--g-warning)" }, { at: 90, color: "var(--g-error)" }]} />

Mouse Wheel

Hover the tab strip and scroll down/right for the next enabled tab, or up/left for the previous one. Wheel switching skips disabled tabs and stops at the first or last enabled tab without wrapping; at either end, normal scrolling continues. Scrolling panel content does not switch tabs. Focus follows selection only if focus was already in the tab strip.

Hover a Slider or Knob (including its label/readout) and scroll up/right to increase or down/left to decrease by one step per wheel event. This also works for vertical and specialized sliders. Values respect their bounds; disabled controls, zoom/modifier gestures, and gestures with no delta are ignored. Handled gestures prevent page scrolling without taking focus; at range limits, normal scrolling continues. Native onWheel handlers can cancel adjustment with preventDefault().

Knob

Knob is a rotary numeric range control. Compare variant="instrument" (default, recessed dial and indicator groove), minimal (shallow disc and recessed dimple), and console (dark domed face, raised outer housing, ivory pointer, and dot scale). Instrument and minimal use neutral inset indicators; console uses an ivory pointer extending inward from the rim. All share the same behavior.

The optional value arc sits between the face and scale ticks. Instrument seats it in a recessed well with matching inset shading; minimal keeps only the value arc, without a backing rail, well, or inset shading. Console defaults to no arc; set showArc to add a value arc in its outer ring without a backing rail. At the range minimum, the value stroke is omitted entirely. Hide the arc with showArc={false}. Scale ticks sit outside the arc and can be controlled independently with showTicks. Ticks default to visible for instrument and console, and hidden for minimal; console uses dots rather than lines. Its dots and labels become dark in light mode; the pointer stays ivory. Sizes are xs (36px), sm (56px), md (88px, default), lg (128px), and xl (168px). These dimensions describe the dial, excluding its label and readout.

Set showMinMax to show small, muted endpoint labels close to the lower sides of the dial. It defaults to false, includes unit when supplied, and works independently of the arc, ticks, and current-value readout. The labels are decorative; the native range already exposes its bounds to assistive technology. Keyboard focus adds Glim's shared glow to the visible dial face and its material shadows, not the larger input hit area. Forced-colors mode retains its system outline on the face.

<Knob label="Gain" variant="instrument" defaultValue={40} unit="%" />
<Knob label="Mix" variant="instrument" value={mix()} onValueChange={setMix} />
<Knob label="Pan" variant="minimal" min={-1} max={1} step={0.1} defaultValue={0} />
<Knob label="Threshold" variant="console" min={-60} max={0} defaultValue={-18} unit=" dB" />
<Knob label="Tinted" variant="console" hue={260} intensity={40} />
<Knob label="Trim" size="xs" showArc={false} showTicks={false} />
<Knob label="Master" size="xl" showMinMax />

All variants accept optional hue (degrees, wrapping at 360) and intensity (tint strength from 0 to 100, not brightness). Omit them to preserve each variant's existing appearance exactly. Intensity 0 removes material saturation; hue alone keeps its existing strength. These adjust the material's hue and saturation while retaining lightness, relative shade differences, geometry, and lighting. The accent arc, focus glow, text, ticks, and console pointer retain their existing colors. Set --g-knob-hue and --g-knob-intensity on an ancestor for CSS theme overrides; explicit props override the inherited values. Instrument/minimal use the theme's neutral hue/intensity as their baseline. Console preserves its approved palette with baseline hue 180 and intensity 20 in both modes; supplying those values is also an identity adjustment.

Console's hardware stays dark in light mode: face, housing, reflections, bevel, and ivory pointer do not change. Only the surrounding dots and text adapt to the theme. Optional lightness (0–100) sets the console face's base HSL lightness and shifts the other material shades by the same amount, clipping at black/white. Omit it to preserve the approved material (base lightness approximately 11.37). This changes material colors, not a brightness filter; lighting geometry, pointer, text, arc, and focus glow remain unchanged. For example, <Knob label="Gain" variant="console" lightness={25} /> uses lighter hardware in either mode. --g-knob-lightness supplies an inherited CSS override; an explicit prop takes precedence. Instrument and minimal accept but ignore lightness.

Drag upward to increase and downward to decrease; hold Shift for fine adjustment. A native range input supplies keyboard controls, accessible slider semantics, disabled behavior, and form submission via name. min, max, step, value, defaultValue, onValueChange, showValue, and unit follow Slider conventions. The defaults are 0–100, step 1, and value 40. Values clamp and snap to the step. Native attributes, refs, and input events apply to the underlying input. style targets the wrapper; inputStyle targets the native input. Pointer gestures do not jump the value on press; cancellation, disabling, and unmounting clean up dragging.

Slider Styles And Sizes

Slider defaults to variant="standard" and size="md", preserving the original 13×18px horizontal thumb and 160px vertical travel area. Choose variant="fader" for a raised rectangular cap with notched grip faces and an inset center mark. variant="console" has a more compact mixer cap: two smooth sloping faces, flared end lips, and a narrow center seam, without grip notches. Both material caps use Glim's grain texture. The rail stays recessed, without an accent value fill. All styles preserve the native range input, keyboard, wheel, form, and disabled behavior.

Console uses a wide, near-black recessed slot with rounded ends and subtle edge lighting. Its vertical caps measure 14×20px (sm), 22×32px (md), and 34×50px (lg); horizontal caps rotate those dimensions.

All variants and specialized sliders accept guide, off by default. guide={true} adds five evenly spaced numeric labels from min to max, plus minor ticks. Labels include unit when supplied. Use guide={[{ value: 0, label: "Quiet" }, { value: 80, label: "Nominal" }, { value: 100, label: "Max" }]} for custom labeled marks; omitted labels use the value and unit. Non-finite, out-of-range, and duplicate values are ignored. An empty guide renders nothing. Guides sit below horizontal controls and to the right of vertical controls, aligned to thumb-center travel rather than the full slot length. Guides are decorative, do not change the numeric scale or step, and are hidden from assistive technology. The demo's “Scale guides” checkbox toggles guides across every style, size, and orientation.

| Size | Standard cap | Fader cap | Console cap | Vertical input length | | --- | --- | --- | --- | --- | | sm | 8×12px | 22×14px | 18×12px | 100px | | md | 13×18px | 34×22px | 26×18px | 160px | | lg | 22×32px | 56×36px | 42×28px | 260px |

Cap dimensions are horizontal width×height; vertical caps and grip direction rotate. Horizontal length follows the container. inputStyle can override input dimensions; coarse pointers retain at least a 44px cross-axis hit area. All Slider specializations inherit both variant and size.

<Slider label="Volume" variant="fader" orientation="vertical" size="lg" defaultValue={60} />
<Slider label="Channel" variant="console" orientation="vertical" defaultValue={60} />
<Slider label="Mix" size="sm" defaultValue={40} />

Slider Specializations

Slider accepts orientation="horizontal" (default) or orientation="vertical". Vertical sliders increase from bottom to top, retain native keyboard controls, and place the value below the track. Labels stay horizontal. The medium vertical input defaults to 160px tall; override its height with inputStyle={{ height: "240px" }}. All three specializations accept orientation and adjust their gradients to match.

<Slider label="Volume" orientation="vertical" defaultValue={40} />
<HueSlider orientation="vertical" />

HueSlider, IntensitySlider, and TextureStrengthSlider are independent wrappers composing Slider; they do not read or change ThemeProvider. They accept all Slider props, with optional labels. Hue defaults to 0–360, value 205 and °; intensity defaults to 0–100, value 0 and %; texture strength defaults to 0–100, value 35 and %. All defaults can be overridden.

<HueSlider value={hue()} onValueChange={setHue} />
<IntensitySlider hue={hue()} value={intensity()} onValueChange={setIntensity} />
<TextureStrengthSlider texture="brushed-metal" value={strength()} onValueChange={setStrength} />

IntensitySlider accepts hue?: number (default 205). TextureStrengthSlider accepts texture?: SurfaceTexture (default fine-grain). The selected texture is previewed from smooth to full strength inside the rail. The base Slider stays neutral. Its trackStyle?: JSX.CSSProperties styles the track container; set --g-slider-track-background to customize the rail while retaining its bevel and thumb. Caller track styles override wrapper effects. style targets the wrapper; inputStyle targets the native input.

Progress

Progress defaults to a recessed continuous fill. Use variant="segmented" for individual tactile cells, with segmentCount (default 20, clamped to 1–100). Partial cells preserve the exact value. Both styles accept label, value, max, tone, and showValue. Omit value for indeterminate progress; no numeric value is announced. Segmented indeterminate progress respects reduced-motion preferences. Invalid or nonpositive max falls back to 100; determinate values are clamped to the range. Completion does not automatically change tone.

<Progress label="Upload assets" value={62} />
<Progress label="Upload assets" value={62} variant="segmented" segmentCount={20} />
<Progress label="Build assets" value={100} tone="success" />
<Progress label="Working" variant="segmented" />

Status

Status renders a compact status label with an optional dot. It accepts children, tone (default neutral), and dot (default true).

<Status tone="success">Active</Status>
<Status dot={false}>Modified</Status>
import { createSignal } from "solid-js";
import { ThemeProvider, Button, Select } from "@sigitex/glim";

export function App() {
  const [status, setStatus] = createSignal("active");
  return (
    <ThemeProvider mode="dark" hue={205} intensity={20}>
      <Select
        label="Project status"
        name="status"
        options={[
          { value: "active", label: "Active" },
          { value: "paused", label: "Paused" },
        ]}
        value={status()}
        onValueChange={setStatus}
      />
      <Button variant="primary" onClick={() => console.log(status())}>
        Save
      </Button>
    </ThemeProvider>
  );
}

State and composition

  • String and number controls: value / onValueChange, or defaultValue for local state.
  • Boolean controls: checked / onCheckedChange, or defaultChecked.
  • Dialog: open / onOpenChange, or defaultOpen. modal defaults to true; draggable defaults to false; closeOnEscape defaults to true.
  • Pagination: page / onPageChange and pageSize / onPageSizeChange.
  • Accordion: string arrays through value / onValueChange, or defaultValue.
  • Table is controlled: provide rows, columns, rowKey, and optional selection/sort callbacks. Filtering, sorting, fetching, and paging remain app concerns.
  • Tag removal calls onRemove; update your tag collection in that callback.
  • FormField accepts a render function supplying the control ID, descriptions, validation, and required/disabled props. Spread them onto Input or Select.
  • Wrap notification consumers with ToastProvider, then call useToast().show(message, {tone, duration, action}). A zero duration remains visible until dismissed. The provider supports six viewport placements, spacing, a visible-stack limit with queued delivery, and hover/focus pausing. Use update(id, patch) to change an existing notification.

Positioned notifications

<ToastProvider
  placement="top-right"
  offset={18}
  gap={8}
  maxVisible={3}
  pauseOnHover
  pauseOnFocus
>
  <App />
</ToastProvider>

placement accepts top-left, top-center, top-right, bottom-left, bottom-center, and bottom-right (default). offset is the outer gutter in CSS pixels (18 by default); gap is the spacing between notifications (8). Gutter sizes adapt to narrow viewports. maxVisible defaults to 3, with a minimum of 1. The oldest notifications appear first. Additional entries wait in a FIFO queue and start their timers only when they become visible. Reducing the limit requeues extra entries; their duration starts afresh when displayed again.

Hover and keyboard focus pause dismissal by default and preserve the remaining time. Leaving one action for another inside the same toast keeps it paused. Set pauseOnHover or pauseOnFocus to false on the provider to disable either behavior. Standalone Toast components expose the same pause options. Individual notifications retain tone, duration (4500ms by default; 0 is persistent), dismissible, and an optional action.

const toast = useToast();
const id = toast.show("Saving…", { duration: 0, tone: "accent" });
// After saving:
toast.update(id, {
  message: "Saved",
  tone: "success",
  duration: 4500,
});
// Or remove it, even while queued:
toast.dismiss(id);

show returns an ID. dismiss removes a visible or queued entry. update(id, patch) returns false if that ID no longer exists; otherwise it updates the same notification, including queued entries. IDs and visible DOM elements stay stable, so action focus is retained. Changing message, tone, action, or dismissible does not reset its clock. Changing duration restarts the countdown; setting duration to 0 cancels automatic dismissal. Passing action: undefined removes an action. Provider class applies to its notification region. Timers are cleaned up when notifications or the provider unmount.

Icons default to the tone's status symbol. Pass icon to show, update, or standalone Toast: JSX replaces the automatic icon, false hides its entire frame, and undefined restores the automatic icon. Icons are decorative and inert; interactive content belongs in the action slot. SVG icons fit the 14px inset icon space.

toast.show("Custom status", { icon: <MyIcon /> });
toast.show("Message only", { icon: false });
toast.update(id, { icon: undefined });

New notifications slide in for 200ms without an opacity fade. Left/right placements enter horizontally; centered placements enter vertically. In-place updates do not replay the entrance. Reduced motion disables the slide.

  • Tabs items contain {value, label, content: () => JSX.Element}. By default, inactive panel content unmounts. Set keepMounted to mount all panel content and hide inactive panels without losing local state. Effects in hidden panels keep running. variant="classic" joins the active tab to its content; separate adds space between them.
  • Breadcrumb items support native links, app navigation callbacks, sibling menus, searchable menus, and responsive middle-level collapse. separator accepts text, icons, or other JSX between items, including the collapsed menu. It defaults to /; null, false, or an empty string hides separators without spacing. Separators are decorative and hidden from assistive technology.

Standard Solid DOM attributes and events pass through Button, Input, Textarea, Surface, Checkbox, Switch, and Slider. Select's custom trigger uses a native hidden select for form submission. Labels should be supplied explicitly for icon-only and custom controls.

On a focused, closed Select, arrow keys change the selection without opening its popup, skipping disabled options and stopping at either end. Home/End select the first/last enabled option. Click, Enter, Space, or Alt+ArrowDown opens the popup; while open, Up/Down moves option focus and Enter confirms.

Component Styles

Every visual component accepts style. It targets the outer visible element, except overlays, where it targets the visible panel rather than the anchor or backdrop. Styles update reactively; explicit styles override generated inline defaults, including positioning. Removing an override restores the generated default. Overriding geometry can bypass built-in layout constraints.

| Component | style target | Additional style props | | --- | --- | --- | | Table | Scroll wrapper | tableStyle for the table | | Slider and specializations | Outer label | inputStyle, trackStyle | | Knob, NumberInput | Outer wrapper | inputStyle | | Checkbox, Switch | Label when supplied, otherwise input | inputStyle (wins on the unlabeled input) | | Popover, Tooltip, Menu, ContextMenu, TableColumnControls | Floating panel | anchorStyle for the anchor wrapper | | Dialog, Drawer | Visible dialog panel | — | | Select | Outer wrapper | triggerStyle, panelStyle | | Menubar | Menu bar | panelStyle for all popups | | Toast | Notification | Also accepted by toast.show and toast.update | | ToastProvider | Notification viewport | — |

Use Solid JSX.CSSProperties objects with hyphenated CSS names. Components derived from native DOM props also retain string-style support. CSS custom properties are supported throughout.

Breaking change: existing style values intended for the native input on Slider, its specializations, Knob, NumberInput, or labeled Checkbox/Switch must move to inputStyle. Other native attributes and events retain their existing targets.

<Slider label="Volume" orientation="vertical" style={{ "margin-inline": "12px" }} inputStyle={{ height: "240px" }} />
<Popover label="Details" style={{ width: "280px" }} anchorStyle={{ display: "inline-block" }}>Details</Popover>

Component Surfaces

Dialog, Popover, Tabs, Accordion, Toolbar, Menubar, and Toast accept surface: panel, window, inset, group, raised, or overlay, matching Surface variants. Omitting it preserves each component's existing appearance.

The option styles Dialog's shell and body, Popover's floating panel, Tabs' content panels, Accordion's expanded bodies, Toolbar's container, Menubar's bar (not its popups), and each Toast. Classic Tabs match the selected tab to the panel surface; variant="separate" only changes the panels. Surface changes do not remount content.

ToastProvider surface="overlay" supplies a default. Override it per notification with toast.show("Saved", { surface: "raised" }), or change it through toast.update(id, { surface: "inset" }). Setting a notification's surface to undefined restores the provider default. Drawer retains its existing surface="window" default.

<Dialog title="Settings" surface="window">Settings content</Dialog>
<Popover label="Details" surface="raised">Details content</Popover>
<Toolbar label="Tools" surface="inset"><Button>Save</Button></Toolbar>

Theme

ThemeProvider scopes its variables to a wrapper. Use multiple sibling providers for independent themes. Defaults reproduce the original Glim palette: dark hue 205, intensity 20, texture strength 35; light hue 205, intensity 12, texture strength 15. Size and spacing are controlled directly by CSS tokens: --g-control (28px), --g-row (32px), --g-gap (6px), --g-control-pad (2px), and --g-row-pad (4px).

Textures: fine-grain, brushed-metal, polished-metal, bead-blasted-metal, paper-fiber. Strength is 0–100; zero disables texture.

Override tokens with tokens={{'--g-radius':'2px', '--g-font-family':'"Source Sans 3", sans-serif'}}. The component package does not load fonts. The demo includes Source Sans 3 under the SIL Open Font License.

Components import plain CSS automatically, including shared theme, focus, and native scrollbar foundations. Source modules and compiled ESM retain separate component imports, so a bundler can omit unused components and their CSS. Import your application styles after Glim. Demo layout styles are separate. Overlays use the native popover top layer, and Dialog uses native dialog focus containment when modal. Use current browsers supporting these APIs.

Exports

The solid condition provides TSX source for Solid-aware compilation, including server rendering. The import condition provides the compiled browser ESM library. Solid is a peer dependency, never bundled. CSS and declarations ship alongside the source and browser output. Package creation does not publish to an npm registry.

Component declarations in lib/index.d.ts document the complete typed API.

For a Vite SSR build, configure solid({ ssr: isSsrBuild }) in your Vite configuration callback. This selects Solid's server JSX generation for the package source as well as your app.

Keyboard shortcuts

Keys displays shortcut keycaps without registering keyboard listeners.

<Keys keys={["Ctrl", "Shift", "P"]} />
<Keys keys={["↑", "↓"]} separator="" aria-label="Up or down arrow" />

Working surfaces

<SplitPane
  label="Resize inspector"
  defaultSize={76}
  min={55}
  max={85}
  first={<ProjectList />}
  second={<Inspector />}
/>

size / onSizeChange makes the split controlled. defaultSize is uncontrolled. Sizes and limits are percentages of available pane space. orientation="vertical" stacks panes. The divider supports pointer dragging, arrows, Shift+arrows, Home/End, and Enter or double-click to reset; onResizeEnd reports completed changes. Give vertical splits a definite height. Pane contents stay mounted while resizing. Set disabled to lock a divider.

Compose Toolbar with ToolbarButton, ToolbarGroup, ToolbarSeparator, and ToolbarSpacer. Give it a label. It uses roving focus for actions, skips disabled buttons, and leaves text-input and select keys alone. Set orientation="vertical" for a vertical toolbar. Optional overflowItems and onOverflowAction add a More actions menu; the application chooses which actions go there. The toolbar wraps on narrow screens.

<ContextMenu
  label="Project actions"
  items={[
    { label: "Edit", value: "edit" },
    { label: "Duplicate", value: "duplicate" },
    {
      label: "Delete",
      value: "delete",
      separatorBefore: true,
      destructive: true,
    },
  ]}
  onAction={handleAction}
>
  <ProjectCard />
</ContextMenu>

Context menus open by right-click, the Context Menu key, or Shift+F10. Menus stay within the viewport, support arrows, Home/End and typeahead, and return focus on Escape or selection. Outside clicks and scrolling dismiss them. disabled or Shift+right-click preserves the browser context menu. Use onOpenChange to observe menu visibility. Keep an ordinary action button/menu available for touch users, as the demo does.

Dialog windows

<Dialog
  open={settingsOpen()}
  onOpenChange={setSettingsOpen}
  title="Project settings"
  modal={false}
  draggable
>
  <ProjectSettings />
</Dialog>

Omit modal for a blocking modal dialog. Set modal={false} to keep the page interactive. Dragging is opt-in: pass draggable to move the title bar with a pointer or focus it and use arrows (Shift for larger steps; Home centers). Positions are constrained to the viewport. Both options can change while open without remounting content.

Set closeOnEscape={false} to disable Escape dismissal in either mode. This option can change while open. Close buttons and other dismissal actions remain available. A foremost window with Escape disabled also shields windows behind it from the same keypress.

Render multiple Dialog instances with independent open state. Modeless windows come forward on focus or pointer interaction; Escape closes the foremost window. Modal dialogs use the browser’s native top layer and focus containment. Closing restores focus to the opener when it is still available.

Menubar

<Menubar
  label="Application menu"
  menus={[
    {
      label: "File",
      items: [
        { label: "Save", value: "save", shortcut: "Ctrl+S" },
        {
          label: "Export",
          children: [
            { label: "PDF", value: "export-pdf" },
            { label: "Markdown", value: "export-md" },
          ],
        },
      ],
    },
  ]}
  onAction={handleAction}
/>

Top-level menus support disabled; items support disabled, separatorBefore, destructive, shortcut, and nested children. Use type="checkbox" or type="radio" with controlled checked; update application state in onAction. Selecting an action closes menus and restores focus. Shortcut labels do not register global keyboard bindings.

The menubar has a single Tab stop with Left/Right and Home/End navigation. Down/Up opens the first/last item. Menus support Up/Down, Home/End, typeahead, nested Right/Left navigation, and Escape one level at a time. Tab exits; outside clicks dismiss. Hover switches menus once one is open. Popups use the native top layer and stay within the viewport. Sizing, themes, and textures use the shared tokens.

Toolbar actions have a quiet resting state, raised hover, and inset pressed/selected states. Primary buttons keep their accent; joined ButtonGroups keep their existing appearance. Use <ToolbarButton icon aria-label="Bold" aria-pressed={bold()} onClick={toggleBold}>B</ToolbarButton> for square icon actions. Ordinary Buttons also get toolbar styling inside a Toolbar. Overflow uses an icon trigger with overflowLabel as its accessible name.

Toolbar spacing follows the sizing tokens through independently overridable tokens: --g-toolbar-gap, --g-toolbar-group-gap, --g-toolbar-separator-gap, and --g-toolbar-button-pad. Group gaps remain between 0 and 2px by default.

Button labels use a small optical offset for Source Sans 3: --g-button-text-offset: 0.1em (about 1px for small labels, 1.4px at 14px). It applies to regular, toolbar, and joined buttons without changing their outer dimensions. Override it through ThemeProvider tokens, including 0px for fonts that need no correction.

Tree view

<TreeView
  label="Files"
  items={[
    {
      id: "src",
      label: "src",
      children: [
        { id: "app", label: "App.tsx" },
        { id: "styles", label: "styles.css" },
      ],
    },
  ]}
  value={selected()}
  onValueChange={setSelected}
  defaultExpanded={["src"]}
  checkable
  checked={checkedFiles()}
  onCheckedChange={setCheckedFiles}
/>

Node IDs must be unique throughout a tree. Each node supports label, children, disabled, and an optional JSX icon. A disabled branch disables its descendants. Selection uses value / onValueChange or defaultValue; expansion uses expanded / onExpandedChange or defaultExpanded. Selection and keyboard focus are separate: Enter or a row click selects.

Enable checkable for hierarchical checks. checked / onCheckedChange or defaultChecked contain leaf IDs. Checking a branch toggles all enabled descendant leaves, including collapsed children; disabled leaves are excluded. Parent states are derived as checked, unchecked, or mixed. Checks do not change selection.

Up/Down moves through visible enabled rows; Right expands or enters a branch, Left collapses or returns to the parent. Home/End jumps to the first/last row. Space checks (or selects when checks are disabled); typeahead finds visible labels. The tree has one Tab stop. Set disabled to disable the whole tree. Override --g-tree-indent to change indentation.

Number input

<NumberInput
  label="Quantity"
  value={quantity()}
  onValueChange={setQuantity}
  min={0}
  max={20}
  step={0.25}
/>

The numeric value is number | null; null represents an empty field. Use value / onValueChange or defaultValue. In-range edits update the value immediately while preserving partial numeric drafts. Blur or Enter commits, clamps to bounds, and normalizes the displayed number; invalid text reverts to the current value. Decimal steps avoid common floating-point display artifacts. Step sizes are increments, rather than a validation lattice.

Up/Down steps, Shift multiplies the step by ten, and Home/End goes to a supplied bound. Escape cancels an unfinished draft. Optional hideSteppers removes the pointer controls while retaining keyboard support. disabled, readOnly, required, name, id, standard input attributes, onBlur, and onKeyDown pass through. The native named input submits its displayed value; commit it before programmatic form submission if an edit is still unfinished. FormField can supply its label and validation attributes. label provides a direct accessible name when needed.

Table column preferences

const [widths, setWidths] = createSignal<Record<string, number>>({});
const [order, setOrder] = createSignal<string[]>([]);
const [hidden, setHidden] = createSignal<string[]>([]);
const columns: TableColumn<Project>[] = [
  { key: 'name', label: 'Project', cell: p => p.name,
    width: 180, minWidth: 110, sticky: 'start' },
  { key: 'owner', label: 'Owner', cell: p => p.owner, width: 140 },
  { key: 'status', label: 'Status', cell: p => p.status,
    width: 120, sticky: 'end' },
];

<TableColumnControls
  columns={columns}
  order={order()} onOrderChange={setOrder}
  hidden={hidden()} onHiddenChange={setHidden}
/>
<Table
  label="Projects" rows={projects()} columns={columns} rowKey={p => p.id}
  resizable reorderable stickyHeader maxHeight="360px"
  columnWidths={widths()} onColumnWidthsChange={setWidths}
  columnOrder={order()} onColumnOrderChange={setOrder}
  hiddenColumns={hidden()}
/>

All existing table sorting, selection, row actions, and empty states remain available. New behavior is opt-in. Column keys must be unique. Widths are pixels; resizing defaults to a 160px width with 80–640px bounds. Override width, minWidth, and maxWidth per column, or set resizable: false to lock it. Resize by dragging the header edge, or focus its separator and use Left/Right (5px), Shift+arrows (20px), and Home/End for limits. Double-click resets to the column's configured width. Pointer resize listeners are removed when resizing ends or the table unmounts.

columnWidths / onColumnWidthsChange and columnOrder / onColumnOrderChange are controlled; defaultColumnWidths and defaultColumnOrder provide local defaults. hiddenColumns is an app-owned list; defaultHiddenColumns supplies an initial configuration. Use TableColumnControls to edit visibility and order through shared application state. It keeps the final column visible. If a caller hides every column, Table displays the first available column as a fallback. Unknown order keys are ignored, duplicates removed, and newly added columns appended.

With reorderable, drag a header grip to another header, or focus it and use Left/Right. TableColumnControls offers pointer-friendly earlier/later actions. sticky: 'start' and 'end' pin columns to logical edges, including RTL. Pinned columns stay grouped at their edge; reordering works within each pinned/unpinned region. Offsets recalculate after hiding or resizing. When start columns are pinned, the row-selection column is pinned too.

stickyHeader and maxHeight enable a vertically scrolling header. Configured widths, resizing, or pinned columns enable fixed layout and horizontal scrolling; widths remain literal pixels rather than stretching to fill the viewport. class and style target the scroll wrapper; tableStyle targets the table itself. Sorting, data operations, and persistence remain application responsibilities; store column state wherever your app keeps preferences.

Header grips use pointer capture for dragging, including touch, and highlight compatible drop targets. A single column in a pinned region has a disabled grip because it has nowhere to move within that region. Change the column's sticky option to unpin it before moving it into another region; the advanced demo provides a Pin edge columns toggle.

Lists

<List
  label="Project files"
  size="md"
  selectionMode="multiple"
  value={selectedFiles()}
  onValueChange={setSelectedFiles}
  checkboxes
  dividers
>
  <ListItem
    value="app"
    label="App.tsx"
    description="Application entry"
    icon={<FileIcon />}
    trailing={<Status>Modified</Status>}
  />
  <ListItem value="styles" label="styles.css" disabled />
</List>

One List API covers plain and selectable lists. selectionMode defaults to none and keeps native ul/li semantics, with freely interactive trailing buttons, menus, or links. single and multiple use listbox/option semantics and manage keyboard focus. Selection always uses string arrays through value / onValueChange or defaultValue; single mode reads at most one key. Give selectable ListItems stable, unique value keys.

Sizes are sm, md (default), and lg. The List supplies the default; individual ListItems can override size. Size sets row proportions and icon space; shared sizing tokens tune padding and height within each size. Primary/secondary text, icons, trailing decorations, dividers, separatorBefore, disabled states, and custom row content share the same styling. Text wraps instead of being clipped. class and style are available on both primitives.

Selectable lists have one Tab stop. Up/Down, Home/End, and typeahead move focus; Space or Enter selects. In multiple mode, unmodified clicks and Space toggle keys, Shift+arrows or Shift+click select a range, and Ctrl/Cmd+A selects all enabled rows. Ctrl/Cmd+Shift adds a range. Disabled rows are skipped. Selection does not automatically follow arrow-key focus. Optional checkboxes show decorative checks in multiple mode; the option's aria-selected remains the source of accessibility state.

Selectable row content should be descriptive rather than independently interactive. Trailing content is inert and hidden from the accessibility tree in selectable modes; labels and descriptions name each option. Use plain mode for rows with action buttons or other interactive content. Disabled plain rows make their contents inert. Icons are decorative. Removing or disabling the focused option transfers focus to an available option while focus remains inside the list.