Skip to content
czarandyPublic

About

A comprehensive themeable React component library

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

 

History

660 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

silver-ui

npm version license

A complete, themeable React component library, built with Panda CSS.

Documentation — components with live examples and API reference.

Storybook — browse and interact with all components.

Installation

npm install silver-ui
# or
pnpm add silver-ui
# or
yarn add silver-ui

silver-ui requires React 19+ as a peer dependency, and the Temporal API (see Temporal below).

Temporal

silver-ui's date and time components use the Temporal API. silver-ui does not bundle a polyfill: it uses the global Temporal, which is built into Chrome/Edge (144+) and Firefox (139+). Where it is missing — Safari (including every browser on iOS), and Node — your app installs @js-temporal/polyfill as the global. Rendering a silver-ui component that needs Temporal without one throws an error pointing here.

1. Add the polyfill:

npm install @js-temporal/polyfill

2. Create a module that installs it only when needed. Browsers with native Temporal skip the download entirely; elsewhere the polyfill loads as a separate chunk.

// src/temporal.ts
if (!('Temporal' in globalThis)) {
  const polyfill = await import('@js-temporal/polyfill');
  Object.assign(globalThis, {Temporal: polyfill.Temporal});
  // Lets Intl.DateTimeFormat#format accept the polyfill's Temporal objects.
  Object.assign(Intl, {DateTimeFormat: polyfill.Intl.DateTimeFormat});
}

export {};

3. Import it in the entry file that renders your app:

// src/main.tsx
import './temporal';
import {createRoot} from 'react-dom/client';
import App from './App';

createRoot(document.getElementById('root')!).render(<App />);

How the await makes this safe: a module with a top-level await delays every module that imports it, so main.tsx's own code — and therefore the first render — runs only once the polyfill is installed. silver-ui reads Temporal when components render, never at import time, so nothing else is needed for silver-ui itself.

Note that sibling imports are not delayed: in the example above, App's module code may run before the polyfill is installed. If one of your own modules uses Temporal at its top level (outside any function or component), add import './temporal'; to that module too.

4. TypeScript: silver-ui's types use the global Temporal types that ship with TypeScript 6. Add them to lib in your tsconfig.json:

{
  "compilerOptions": {
    "lib": ["DOM", "DOM.Iterable", "ES2022", "ESNext.Temporal"]
  }
}

Leave out ESNext.Intl: its Temporal-aware Intl.DateTimeFormat types conflict with the polyfill's own declarations unless skipLibCheck is on.

Top-level await needs an ES2022 module target, which Vite and other modern bundlers support by default. If your package.json declares "sideEffects", list src/temporal.ts there too, or bundlers will drop the side-effect-only import './temporal'. Once every browser you support ships Temporal, delete src/temporal.ts and its imports.

Usage

Import the stylesheet once in your app's entry point, then use components:

import 'silver-ui/styles.css';
import {Button, Card, Heading, Text} from 'silver-ui';

function App() {
  return (
    <Card padding={4}>
      <Heading level={2}>Welcome back</Heading>
      <Text>Your project is ready to deploy.</Text>
      <Button
        label="Deploy"
        onClick={() => console.log('Deploying…')}
        size="md"
        variant="primary"
      />
    </Card>
  );
}

For smaller JS bundles, import component subpaths:

import 'silver-ui/styles.css';
import {Button} from 'silver-ui/Button';
import {SideNav, SideNavItem} from 'silver-ui/SideNav';

Theming

The easiest way to theme silver-ui is the <Theme> component. It maps friendly token names to the underlying CSS variables and applies them app-wide or to a scoped subtree. Wrap your app (or any branded area) and override the tokens you care about:

import {Button, Theme} from 'silver-ui';

export function App() {
  return (
    <Theme
      mode="light"
      tokens={{
        colors: {
          primary: 'teal-500',
          primaryHover: 'teal-600',
          primaryActive: 'teal-700',
        },
        radii: {componentMd: '0.5rem'},
        fonts: {body: 'Inter, system-ui, sans-serif'},
      }}>
      <Button label="Save" />
    </Theme>
  );
}

Palette references (teal-500) and custom CSS values (#e11d48, oklch(...), var(...)) are both supported, and multiple <Theme> regions can coexist on one page.

Prefer plain CSS? Every component is driven by --silver-* variables, so you can override them directly. All library styles use CSS @layer, so your overrides always win without specificity battles.

:root {
  --silver-colors-primary: #e11d48;
  --silver-colors-primary-hover: #be123c;
  --silver-colors-primary-active: #9f1239;
  --silver-radii-component-md: 0.5rem;
  --silver-fonts-body: Inter, system-ui, sans-serif;
}

Variables can be scoped to containers, dark mode ([data-theme="dark"]), or individual instances via className/style props.

See THEME.md for the full <Theme> API, variable reference, dark mode details, scoped theming examples, and per-instance overrides.

Components

Layout & Structure

  • Accordion — collapsible content sections
  • AppShell — application-level layout shell with responsive mobile navigation
  • AspectRatio — constrains children to a fixed aspect ratio
  • Card / ClickableCard / CheckboxCard / RadioCard — static, actionable, and selectable container surfaces
  • Center — centers content horizontally, vertically, or both
  • Divider — visual separator between sections
  • Grid — responsive equal-width columns with minimum-child-width reflow
  • Layout — page shell with header, footer, side panels, and content slots
  • OverflowList — shows the items that fit and collapses the rest into a custom overflow indicator
  • HStack / VStack — horizontal and vertical flex containers with gap

Typography

  • Blockquote — styled quotation block
  • CodeBlock — read-only code display with line numbers, line highlighting, and copy button
  • Highlight — marks case-insensitive substring matches in text
  • Kbd — keyboard shortcut display with accessible labels
  • Text / Heading — typography primitives with size, color, and truncation

Navigation

  • Breadcrumbs — hierarchical page location trail
  • Link — polymorphic link with external link handling and router integration
  • NavIcon — solid primary FeaturedIcon for navigation headers
  • Pagination — page navigation controls
  • SideNav — vertical side navigation panel with collapsing support
  • Stepper — multi-step progress indicator
  • Tabs — tabbed content switching
  • TopNav — horizontal top navigation bar

Buttons & Actions

  • Button — versatile action element supporting links, loading states, and icon-only mode
  • ButtonGroup — connected group of related buttons
  • Clickable / ClickableContainer — turns content into a button or link target, including surfaces with nested controls
  • CopyButton — clipboard action with copied-state feedback and accessible announcements
  • ContextMenu — right-click context menu
  • DropdownMenu — button-triggered dropdown menu
  • SegmentedControl — mutually exclusive option selector
  • SplitButton — primary action paired with a dropdown menu of related actions
  • ToggleButton — button with pressed/unpressed state
  • Toolbar — slotted action bar with roving-tabindex keyboard navigation and size cascading

Forms

  • AutocompleteInput — searchable autocomplete input (combobox)
  • CheckboxGroup — controlled checkbox group for multi-value selection
  • CheckboxInput — checkbox with label and description
  • ColorSwatchPicker — swatch picker for the named theme palette
  • Field — form field wrapper with label, description, and validation
  • Fieldset — native legend container for normally stacked, related fields
  • FileInput — file upload with drag-and-drop support
  • InputGroup — combines controls and addons into one connected input row
  • MultiSelect — multi-value dropdown selector
  • NumberInput — numeric input with increment/decrement
  • PasswordInput — text input with show/hide toggle
  • PinInput — multi-cell PIN and one-time-code input
  • RadioGroup — radio button group
  • Rating — star rating input
  • SearchFilterInput — search input with structured filter tags
  • Select — single-value dropdown selector
  • Slider — range slider with marks and labels
  • Switch — toggle switch
  • TagsInput — free-form tag entry field
  • TextArea — multi-line text input
  • TextInput — single-line text input

Dates & Time

  • Calendar — date picker calendar grid with single and range selection
  • DateInput — date picker field
  • DateRangeInput — date range picker field
  • DateTimeInput — combined date and time picker
  • Schedule — event calendar with day, week, month, and list views
  • TimeInput — time picker field
  • Timestamp — displays a point in time as relative or absolute text

Data Display

  • Avatar — user or entity avatar with status indicators
  • AvatarGroup — stacked avatar collection with overflow count
  • Badge — small status label with icon support
  • FeaturedIcon — icon inside a tinted circle for headlining empty, success, and error states
  • Icon — renders Lucide icons with size and color tokens
  • Image — responsive image with intrinsic loading and error fallback
  • Item — generic list item with icon, label, and description
  • List — ordered or unordered list with dividers
  • MetadataList — key-value metadata display
  • Stat — prominent label/value summary with optional change, context, and icon
  • Table — data table with sorting, filtering, and column resize plugins
  • Tag — removable label for categories or filters
  • Thumbnail — image preview with fallback and remove action
  • Timeline — ordered chronological event sequence with timestamps and custom indicators
  • TreeView — hierarchical expandable tree

Chat & Messaging

  • Chat — composable chat/message UI: layout with streaming auto-scroll, message list, bubbles, metadata, system messages, and a composer with send/stop

Feedback & Status

  • Alert — dismissable notification banner
  • CircularProgress — determinate or indeterminate circular progress ring
  • EmptyState — placeholder for empty content areas
  • Progress — determinate progress bar
  • Skeleton — loading placeholder with pulse animation
  • Spinner — indeterminate loading indicator
  • StatusDot — compact unlabelled presence indicator dot (Badge is the labelled equivalent)
  • Toast — temporary notification popup

Overlays

  • AlertDialog — confirmation dialog requiring explicit action
  • Dialog — modal dialog with header and content
  • Drawer — slide-in panel from screen edge
  • HoverCard — rich content popup on hover
  • Lightbox — full-screen media viewer
  • Popover — anchored popup with arbitrary content
  • Tooltip — informational popup on hover or focus

Utilities

  • Theme — scoped theme provider backed by CSS custom properties
  • VisuallyHidden — hides content visually while keeping it available to screen readers

Hooks

The primitives silver-ui's own components are built on are published from silver-ui/hooks, so you can build custom widgets that behave the same way.

import {
  useAnnounce,
  useClipboard,
  useHotkey,
  useListFocus,
  useMergeRefs,
  useTypeahead,
} from 'silver-ui/hooks';
  • useAnnounce — visually hidden polite and assertive live regions, plus an announce(message, politeness?) function
  • useClipboard — clipboard writes with transient copied state and accessible success or failure announcements
  • useHotkey — global, IME-safe keyboard shortcuts with exact modifier matching and automatic listener cleanup
  • useListFocus — roving-tabindex keyboard navigation over a list of elements (arrow keys, Home/End, wrapping, RTL)
  • useMergeRefs — combines up to six object or callback refs, stays stable while its inputs are unchanged, and preserves React 19 callback cleanup
  • useTypeahead — WAI-ARIA typeahead: multi-character search, repeat-press cycling, and wrap-around matching
  • useConstant — computes a value once and returns the same reference for the lifetime of the component

useHotkey(keys, handler, options?) accepts one case-insensitive descriptor such as f6, shift+k, or mod+k. Separate tokens with +; supported modifiers are mod, ctrl, alt, and shift, and the special-key vocabulary matches Kbd: enter, backspace, escape, tab, up, down, left, right, and plus. mod resolves to Command on Apple platforms and Control elsewhere.

The listener targets document by default; pass target: 'window' or a React ref to listen elsewhere. It is enabled by default and does not call preventDefault() unless hasPreventDefault: true is set. Composition events are always ignored, and shortcuts do not fire from inputs, selects, textareas, editable content, or textbox roles unless isEnabledOnFormElements: true is set. Sequences and priority arbitration between duplicate registrations are not supported.

Relay integration

Relay EntryPoint-backed controllers for Drawer, Dialog, Popover, and HoverCard are available from the opt-in silver-ui/relay entry. Relay is an optional peer dependency, so applications that do not import this entry do not need to install it.

pnpm add react-relay relay-runtime
import {createJSResourceReference, usePreloadedDrawer} from 'silver-ui/relay';

The controllers own EntryPoint loading, retry, visibility, and reference lifetime. Use preload(params) for pointer or focus intent, show(params, runtimeProps) to open the surface, and render the returned element alongside the trigger. The application remains responsible for its Relay environment, compiler, generated artifacts, and EntryPoint definitions.

usePreloadedDrawer and usePreloadedDialog accept onBeforeHide, which is called once, synchronously, whenever the user tries to close the surface (Escape, a backdrop click, or the LayoutHeader close button). Its return value can veto the hide: return false to keep the surface open, for example to confirm discarding unsaved changes. hide() and the EntryPoint's close prop always close without calling it.

const drawer = usePreloadedDrawer(requestEntryPoint, {
  label: 'Request credentialing',
  onBeforeHide: () =>
    !hasUnsavedChanges() || window.confirm('Leave without submitting?'),
});

Contributing

See CONTRIBUTING.md for setup, development scripts, and component contribution guidelines.

License

MIT

About

A comprehensive themeable React component library

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages