Package Exports
- aporia
- aporia/package.json
- aporia/styles.css
Readme
aporia
A collection of production-quality React components for building configurators - sliders, color pickers, gradient editors, toggles, and more.
Installation
npm install aporiaImport aporia/styles.css once in your app entry (for example main.tsx). If you use Tailwind or another global reset, import Aporia’s stylesheet after those layers so Aporia’s opinionated panel / popover typography and control chrome win predictable conflicts.
Maintainers: package demo + consumer sandbox
If you keep a sibling app (for example ../aporia-test) next to this repo:
- Canonical UI (this repo) —
npm run devstarts the Vite demo (--mode demo, default port 5178). That is the reference layout: white page shell (var(--color-page)),Paneluses the default 360px max width from--aporia-panel-max-width. - NPM-style consumer — in the sibling app, depend on
file:../aporia, runnpm run devthere (this workspace uses port 5173 for the consumer). After library changes, runnpm run sync-libinside the consumer to rebuildaporiaand refresh thefile:install. - From the parent folder — with both repos under the same parent directory,
npm run dev:packageandnpm run dev:consumerrun each app;npm run sync:consumerrebuilds the library and reinstalls into the consumer.
Publishing for end users: bump version, run npm run build, then npm publish. Consumers should use a normal semver range (for example ^0.2.6) and import 'aporia/styles.css'. The prepublishOnly script runs build automatically on publish. Until you publish, sibling apps can use file:../aporia and npm run sync-lib after edits — no publish required for local reflection.
Usage
The usual setup is a Panel with one or more Category blocks, each containing rows (SliderRow, ColorRow, etc.). The package default export is Panel, so you can import it as the default or by name.
import { useState } from 'react'
import Panel, { Category, SliderRow, ColorRow, ToggleRow, GradientRow, ThemeProvider } from 'aporia'
import 'aporia/styles.css'
function App() {
const [intensity, setIntensity] = useState(50)
const [color, setColor] = useState('#E8470C')
const [enabled, setEnabled] = useState(false)
return (
<ThemeProvider>
<Panel>
<Category title="Basics">
<SliderRow
label="Intensity"
value={intensity}
min={0}
max={100}
step={1}
onChange={setIntensity}
/>
<ColorRow label="Accent" value={color} onChange={setColor} />
<ToggleRow label="Enabled" checked={enabled} onChange={setEnabled} />
</Category>
<Category title="Background">
<GradientRow label="Gradient" onChange={(gradient) => console.log(gradient)} />
</Category>
</Panel>
</ThemeProvider>
)
}Use Panel (and usually Category) for any real configurator surface so spacing, typography, and the black card shell stay consistent. Rows still work as leaf nodes, but they are designed to live inside that shell.
Components
Panel
Rounded shell for a configurator: drop in Category sections and row components as children. By default the panel is width: 100% with max-width: min(360px, 100%) via the :root token --aporia-panel-max-width (override on html or a wrapper if you need a wider shell).
Category
Collapsible section with a title and optional disable-all behavior for its children.
SliderRow
A polished slider with spring animations, keyboard support, and inline editing.
<SliderRow
label="Volume"
value={volume}
min={0}
max={100}
step={1}
onChange={setVolume}
fmt={(n) => `${n}%`} // Optional formatter
/>ColorRow
Hex color picker with inline editing and native color picker fallback.
<ColorRow
label="Color"
value={color}
onChange={setColor}
/>ToggleRow
Accessible toggle switch with keyboard support.
<ToggleRow
label="Dark Mode"
checked={isDark}
onChange={setIsDark}
/>GradientRow
Multi-stop gradient editor with shuffle, invert, and auto-distribute controls.
<GradientRow
label="Gradient"
initialStops={[
{ color: '#000000', position: 0 },
{ color: '#ffffff', position: 100 },
]}
angle={90}
onChange={(cssGradient) => console.log(cssGradient)}
/>GradientPicker
Lower-level gradient editor component with full control.
import { GradientPicker, stopsToGradient, parseGradient } from 'aporia'
<GradientPicker
stops={stops}
onChange={setStops}
/>
// Utilities
const css = stopsToGradient(stops, 90) // Convert to CSS
const stops = parseGradient(css) // Parse CSS gradientTheming
Aporia ships a dark visual system: the Panel surface is always #000000 with light text, and color-scheme: dark is scoped to the panel and floating pickers only so your host page (light theme, marketing site, etc.) is not forced into document-wide dark UA chrome. CSS variables remain on :root so portaled popovers still inherit them.
ThemeProvider is for React context (useTheme) only; it does not mutate <html> or set global data-theme.
import { ThemeProvider, useTheme } from 'aporia'
function App() {
return (
<ThemeProvider>
<YourApp />
</ThemeProvider>
)
}
// `theme` is `'dark'` today; useful for future light mode without changing call sites.
function ThemeLabel() {
const { theme } = useTheme()
return <span>{theme}</span>
}Peer Dependencies
Aporia requires these peer dependencies:
react>= 18.0.0react-dom>= 18.0.0motion>= 12.0.0@base-ui/react>= 1.0.0
Development
# Install dependencies
npm install
# Start dev server (demo app)
npm run dev
# Build library
npm run build
# Lint
npm run lintLicense
MIT