@dnotrever2/super-kit
v0.1.47
Published
React component kit for reusable application primitives.
Readme
@dnotrever2/super-kit
React component library for the Verazor design system. Dark, layered, technical aesthetic built on Geist + Geist Mono. Zero runtime dependencies.
Install
npm install @dnotrever2/super-kitPeer dependencies — install if not already present:
npm install react react-domSetup
Import the global stylesheet once at your app's entry point. It provides all design tokens, base resets, and scrollbar utilities:
import "@dnotrever2/super-kit/styles.css";To override colors or tokens in the consuming app, define the same CSS variables after the SuperKit stylesheet:
@import "@dnotrever2/super-kit/styles.css";
:root {
--color-blue-1: #2563eb;
--color-blue-1-hover: #1d4ed8;
--color-blue-1-active: #1e40af;
}CSS modules are injected into the JS bundle by Vite — there is no separate
.cssoutput per component. Only the global stylesheet needs a manual import.
Components
Accordion
Expandable content sections with single or multiple open items.
import { Accordion, Badge } from "@dnotrever2/super-kit";
<Accordion
defaultValue="deploy"
items={[
{
value: "deploy",
title: "Deploy pipeline",
content: "Build, test, and publish the current release."
},
{
value: "limits",
title: <>Usage limits <Badge variant="yellow2" pill>3</Badge></>,
content: "Requests are throttled when the workspace reaches its quota."
}
]}
/>
<Accordion
defaultValue={["deploy", "access"]}
items={items}
/>
<Accordion
multiple={false}
indicator="plus-minus"
border="divider"
radius="square"
highlight="header"
spacing={0}
defaultValue="deploy"
items={items}
/>
<Accordion
highlight="item"
hoverHighlight={false}
headerStyle={{ minHeight: 46, padding: "12px 16px" }}
bodyStyle={{ padding: "0 16px", color: "var(--fg-2)" }}
items={items}
/>| Prop | Type | Default |
| ------------------ | ------------------------------------- | ------------------ |
| items | AccordionItem[] | required |
| value | string \| string[] | — controlled |
| defaultValue | string \| string[] | "" or [] |
| onValueChange | (value: string \| string[]) => void | — |
| multiple | boolean | true |
| hideIndicator | boolean | false |
| indicator | "chevron" \| "plus-minus" | "chevron" |
| border | "boxed" \| "none" \| "divider" | "boxed" |
| highlight | "none" \| "item" \| "header" | "none" |
| radius | "rounded" \| "square" | "rounded" |
| hoverHighlight | boolean | true |
| spacing | number \| string | 8 |
| disabled | boolean | false |
| itemClassName | string | — |
| headerClassName | string | — |
| headerStyle | CSSProperties | — |
| bodyClassName | string | — |
| bodyStyle | CSSProperties | — |
| triggerClassName | string | — |
| contentClassName | string | — |
AccordionItem fields
| Field | Type |
| -------------- | ----------------------------------------- |
| value | string |
| title | ReactNode |
| content | ReactNode |
| disabled | boolean |
| icon | ReactNode |
| className | string |
| triggerProps | ButtonHTMLAttributes<HTMLButtonElement> |
| contentProps | HTMLAttributes<HTMLDivElement> |
By default, opening one item does not close the others. Use multiple={false} when only one item should stay open. Use hideIndicator to remove the arrow, or indicator="plus-minus" to show a small +/- indicator. Use border="none" to remove borders, border="divider" to show only separators between items, and spacing to control the gap between items. Use radius="square" for square corners. Use highlight="item" to highlight the whole open item, or highlight="header" to highlight only the header. Use hoverHighlight={false} to disable hover highlight. Use headerClassName/headerStyle and bodyClassName/bodyStyle to customize colors, height, width, padding, margin, and other layout styles.
Badge
Status labels and tags.
import { Badge } from "@dnotrever2/super-kit";
<Badge variant="green2">Deployed</Badge>
<Badge variant="red2" pill>Failed</Badge>
<Badge variant="red2" coloredText>Failed</Badge>
<Badge variant="red2" outline>Failed</Badge>
<Badge variant="blue2" label labelDirection="left">New</Badge>
<Badge variant="green2" indicator aria-label="Online" />
<Badge dismissable onDismiss={() => {}}>api-gateway</Badge>| Prop | Type | Default |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| variant | blue/green/yellow/red/gray + 1/2/3, optional Soft suffix, e.g. "blue2Soft" | "gray2" |
| icon | ReactNode | — |
| pill | boolean | false |
| outline | boolean | false |
| coloredText | boolean | false |
| indicator | boolean | false |
| label | boolean | false |
| labelDirection | "left" \| "right" | "right" |
| dismissable | boolean | false |
| onDismiss | () => void | — |
Badge variants match Button variants. Tones run from 1 lighter, 2 medium, and 3 darker. Use coloredText when the text should follow the badge variant color on a soft background. Outline badges use the variant color for the text. Use pill for the rounded shape. Use indicator for a small status dot; provide an accessible label with aria-label.
Badge also accepts native <span> attributes, including className, style, id, title, and aria-*. For one-off visual tweaks, use style or className; the internal CSS variables --badge-bg, --badge-fg, and --badge-border can be overridden for custom background, text, and border colors.
Breadcrumb
Page hierarchy navigation with links, actions, and automatic current-page state.
import { Breadcrumb } from "@dnotrever2/super-kit";
<Breadcrumb
items={[
{ label: "Projects", href: "/projects" },
{ label: "super-kit", href: "/projects/super-kit" },
{ label: "Components" }
]}
/>| Prop | Type | Default |
| ----------- | ------------------ | -------------- |
| items | BreadcrumbItem[] | required |
| separator | ReactNode | chevron icon |
| label | string | "Breadcrumb" |
| colorCurrent | boolean | false |
BreadcrumbItem fields
| Field | Type |
| ------------- | --------------------------------------- |
| label | ReactNode |
| href | string |
| current | boolean |
| disabled | boolean |
| onClick | () => void |
| linkProps | AnchorHTMLAttributes<HTMLAnchorElement> |
| buttonProps | ButtonHTMLAttributes<HTMLButtonElement> |
The last item is treated as the current page unless current is set explicitly.
Button
Standard action button with muted color-scale variants.
import { Button, Spinner } from "@dnotrever2/super-kit";
<Button variant="blue2" onClick={deploy}>Deploy</Button>
<Button variant="green2" icon={<CheckIcon />}>Approve</Button>
<Button variant="red2">Delete</Button>
<Button variant="red2Soft">Delete</Button>
<Button variant="blue2" outline>Configure</Button>
<Button variant="blue2" transparent>Deploy</Button>
<Button variant="blue2" rounded>Deploy</Button>
<Button variant="gray2Soft" icon={<SettingsIcon />} aria-label="Settings" />
<Button variant="blue2" disabled icon={<Spinner size="sm" onColor />}>
Loading...
</Button>| Prop | Type | Default |
| ---------- | ------------------------------------------------------------------------------------ | --------- |
| variant | blue/green/yellow/red/gray + 1/2/3, optional Soft suffix, e.g. "blue2Soft" | "gray2" |
| size | "sm" \| "md" \| "lg" | "md" |
| icon | ReactNode | — |
| outline | boolean | false |
| rounded | boolean | false |
| coloredText | boolean | false |
| transparent | boolean | false |
| disabled | boolean | false |
Tones run from 1 lighter, 2 medium, and 3 darker. Every tone has a Soft variant, and the same soft color is reused by disabled and subdued appearances.
Normal buttons use the stronger tone background. Use coloredText when the text should follow the button variant color on a soft background. Use transparent when the button should start without a background and reveal the variant background on hover. Outline buttons use the variant color for the text and border.
To create a loading button, pass a Spinner as icon, use text such as "Loading...", and set disabled to block interaction.
To create an icon button, pass only icon and no text. Provide an accessible label with aria-label.
Forwards a ref to the underlying <button>. Accepts all native button attributes.
Card
Container surface with optional tilt hover effect and close button.
import { Card, CardHeader, CardStat } from "@dnotrever2/super-kit";
<Card padding="md" tilt onClose={() => setVisible(false)}>
<CardHeader
icon={<DatabaseIcon />}
title="orders-db"
subtitle="postgres · us-east-1"
/>
<CardStat value="2.4" unit="ms" delta="-12%" deltaDirection="positive" />
</Card>Card props
| Prop | Type | Default |
| --------------- | ----------------------------------------- | --------------------------------------------------------------- |
| padding | "none" \| "sm" \| "md" \| "lg" | "md" |
| tilt | boolean | false — hover lifts the card with a subtle 3D tilt and shadow |
| onClose | () => void | — — renders a × button (fades in on hover) |
| closeBtnProps | ButtonHTMLAttributes<HTMLButtonElement> | — |
CardHeader props
| Prop | Type |
| ---------- | ---------------------- |
| title | ReactNode (required) |
| subtitle | ReactNode |
| icon | ReactNode |
CardStat props
| Prop | Type | Default |
| ---------------- | --------------------------------------- | ------------ |
| value | ReactNode (required) | — |
| unit | string | — |
| delta | string | — |
| deltaDirection | "positive" \| "negative" \| "neutral" | "positive" |
Input
Text input with label, icon, mask, and clear button.
import { Input } from "@dnotrever2/super-kit";
// Basic
<Input label="name" placeholder="api-gateway" clearable />
// With icon
<Input label="search" icon={<SearchIcon />} placeholder="Search services" />
// Rounded
<Input label="search" rounded placeholder="Search services" />
// With mask — only digits allowed, formatted as 000-0000
<Input
label="code"
mask="XXX-XXXX"
maskAllowedPattern={/\d/}
placeholder="000-0000"
onValueChange={({ value, rawValue }) => console.log(rawValue)}
/>
// Select all on focus, right-aligned text
<Input selectOnFocus textAlign="right" defaultValue="127.0.0.1" />
// Number input — rendered as text, accepts digits only
<Input label="replicas" type="number" showNumberControls min="0" max="9" />
// Password input — includes a reveal button by default
<Input label="password" type="password" />How masks work
mask— format string where every character is a literal except positions matched bymaskAllowedPattern. Example:"(XX) XXXXX-XXXX"for Brazilian phone numbers.maskAllowedPattern—RegExpthat each typed character must match (default: any character). Use/\d/for digits-only,/[A-Za-z]/for letters-only.maskPlaceholder— character used to fill empty mask positions (e.g."_"). If omitted, empty positions are left blank.onValueChangereceives bothvalue(formatted with mask) andrawValue(mask characters stripped).
| Prop | Type | Default |
| ------------------------ | --------------------------------------- | ------- |
| label | string | — |
| type | InputHTMLAttributes["type"] | "text" |
| icon | ReactNode | — |
| mask | string | — |
| maskAllowedPattern | RegExp | — |
| maskPlaceholder | string | — |
| clearable | boolean | false |
| showNumberControls | boolean | false |
| showPasswordToggle | boolean | true |
| rounded | boolean | false |
| selectOnFocus | boolean | false |
| textAlign | "left" \| "center" \| "right" | — |
| value / defaultValue | string | "" |
| onChange | ChangeEvent<HTMLInputElement> | — |
| onValueChange | (change: { value, rawValue }) => void | — |
| inputProps | InputHTMLAttributes<HTMLInputElement> | — |
| wrapperProps | HTMLAttributes<HTMLSpanElement> | — |
| fieldProps | HTMLAttributes<HTMLDivElement> | — |
Forwards a ref to the underlying <input>.
DateTimeInput
Date and time input with custom date, month, and time pickers using Super Kit styling.
import { DateTimeInput } from "@dnotrever2/super-kit";
<DateTimeInput label="Date" mode="date" clearable />
<DateTimeInput label="Time" mode="time" />
<DateTimeInput label="Date and time" mode="datetime" />
<DateTimeInput label="Month and year" mode="month" />
<DateTimeInput
label="Release date"
mode="date"
min="2026-05-01"
max="2026-05-31"
onValueChange={(value) => setDate(value)}
/>| Prop | Type | Default |
| ------------------- | ------------------------------------------------ | -------- |
| mode | "date" \| "time" \| "datetime" \| "month" | "date" |
| label | string | — |
| value / defaultValue | string | "" |
| clearable | boolean | false |
| clearLabel | string | "Clear" |
| showIcon | boolean | true |
| openPickerOnClick | boolean | true |
| disabled | boolean | false |
| onChange | ChangeEvent<HTMLInputElement> | — |
| onValueChange | (value: string) => void | — |
| inputProps | InputHTMLAttributes<HTMLInputElement> | — |
| wrapperProps | HTMLAttributes<HTMLSpanElement> | — |
| fieldProps | HTMLAttributes<HTMLDivElement> | — |
Native min, max, step, required, and other input attributes are supported. Values follow browser-native formats: YYYY-MM-DD for date, HH:mm for time, YYYY-MM-DDTHH:mm for datetime, and YYYY-MM for month.
Forwards a ref to the underlying <input>.
Link
Text link with variants aligned to Button and Badge colors.
import { Link } from "@dnotrever2/super-kit";
<Link href="/docs" variant="blue2">Documentation</Link>
<Link href="/status" variant="green2" underlined>Status</Link>
<Link href="https://example.com" target="_blank" noreferrer>
External docs
</Link>
<Link href="/danger-zone" variant="red2" opacity={0.72}>
Danger zone
</Link>
<Link href="/settings" disabled>Settings</Link>| Prop | Type | Default |
| ------------- | -------------------------------------------------------------------------------------- | --------- |
| variant | blue/green/yellow/red/gray + 1/2/3, optional Soft suffix, e.g. "blue2Soft" | "blue2" |
| href | string | — |
| target | HTMLAttributeAnchorTarget | — |
| underlined | boolean | false |
| opacity | number | 1 |
| disabled | boolean | false |
| noreferrer | boolean | false |
| noopener | boolean | true for target="_blank" |
| icon | ReactNode | — |
Forwards a ref to the underlying <a>. Accepts native anchor attributes such as download, rel, referrerPolicy, and aria-*. When disabled, navigation is blocked and the link is removed from the tab order.
List
Grouped list of items with optional collapsible title, selection, and pointer-based drag and drop (including dragging between lists).
import { List } from "@dnotrever2/super-kit";
// Selectable, collapsible group
<List
title="Services"
collapsible
selectable
defaultSelectedValue="api"
onSelectedValueChange={(value, item) => setActive(value)}
items={[
{ value: "api", label: "API gateway", description: "Routes public traffic" },
{ value: "worker", label: "Background worker", meta: <Badge variant="green2" pill>live</Badge> },
{ value: "archive", label: "Archive store", disabled: true }
]}
/>
// Drag and drop within a list
const [items, setItems] = useState(initialItems);
<List
title="Pipeline"
draggable
items={items}
onItemDrop={({ item, targetItem, position }) => {
setItems((prev) => reorder(prev, item, targetItem, position));
}}
/>
// Drag between two lists in the same group
<List title="Available" draggable dragGroup="stages" listId="available" items={available} onItemDrop={handleDrop} />
<List title="Selected" draggable dragGroup="stages" listId="selected" items={selected} onItemDrop={handleDrop} />| Prop | Type | Default |
| ------------------------- | ------------------------------------------ | ------------ |
| items | ListItem[] | required |
| title | ReactNode | — |
| showTitle | boolean | true |
| collapsible | boolean | false |
| collapsed | boolean | — controlled |
| defaultCollapsed | boolean | false |
| onCollapsedChange | (collapsed: boolean) => void | — |
| emptyLabel | ReactNode | "No items" |
| selectable | boolean | false |
| selectedValue | string \| null | — controlled |
| defaultSelectedValue | string \| null | null |
| onSelectedValueChange | (value: string, item: ListItem) => void | — |
| spacing | "sm" \| "md" \| "lg" | "sm" |
| itemSize | "sm" \| "md" \| "lg" | "sm" — 12px, 14px, 16px |
| titleSize | "sm" \| "md" \| "lg" | "md" — 12px, 14px, 16px |
| fullWidthBackgroup | boolean | false |
| titleHover | boolean | false |
| ariaLabel | string | — |
| itemClassName | string | — |
| headerProps | ButtonHTMLAttributes<HTMLButtonElement> | — |
| listProps | HTMLAttributes<HTMLUListElement> | — |
| draggable | boolean | false |
| dragGroup | string | internal |
| listId | string | auto |
| isItemDraggable | (item: ListItem) => boolean | — |
| onItemDrop | (event: ListItemDropEvent) => void | — |
| dragGhost | (item: ListItem) => ReactNode | item content |
| dropOutsideListId | string | — |
| dropOutsideBeforeListId | string | — |
| dropOutsideAfterListId | string | — |
ListItem fields
| Field | Type |
| ------------- | ------------------------------------------------------------------- |
| value | string |
| label | ReactNode |
| description | ReactNode |
| meta | ReactNode |
| icon | ReactNode |
| active | boolean |
| disabled | boolean |
| className | string |
| itemProps | HTMLAttributes<HTMLLIElement> |
| buttonProps | ButtonHTMLAttributes<HTMLButtonElement> |
| onClick | (event, item: ListItem) => void |
ListItemDropEvent fields
| Field | Type |
| -------------- | --------------------------------- |
| item | ListItem — the dragged item |
| sourceListId | string |
| targetListId | string |
| targetItem | ListItem — undefined when dropped on empty space |
| position | "before" \| "after" \| "inside" |
Provide a title to render a header; set collapsible to make it toggle. Use selectable for single-selection highlight, or per-item active/onClick for custom behavior. Set draggable to enable reordering — lists that share a dragGroup can exchange items, and each list resolves drops through its own onItemDrop. The dropOutsideListId family redirects drops that land outside any list (e.g. back into a source pool). value must be unique within a list — it is the React key and the drag identity.
Markers
Form selection primitives: Checkbox, Radio, and Switch.
import { Checkbox, Radio, RadioGroup, Switch } from "@dnotrever2/super-kit";
<Checkbox label="Auto-deploy" checked={checked} onChange={setChecked} />
<Checkbox label="Auto-deploy" variant="green2" checked={checked} onChange={setChecked} />
<Checkbox label="Partial" indeterminate />
<Checkbox label="Required" isInvalid />
<Checkbox label="Disabled" disabled />
<RadioGroup>
<Radio label="main → production" value="prod" checked={v === "prod"} onChange={setV} />
<Radio label="main → staging" value="staging" checked={v === "staging"} onChange={setV} />
</RadioGroup>
<Switch label="SSO required" checked={on} onChange={setOn} />
<Switch label="SSO required" variant="yellow2" checked={on} onChange={setOn} />
<Switch label="SSO required" thinTrack checked={on} onChange={setOn} />Checkbox / Switch props
| Prop | Type |
| ---------------- | ---------------------------- |
| label | ReactNode |
| variant | ColorVariant |
| checked | boolean |
| defaultChecked | boolean |
| indeterminate | boolean (Checkbox only) |
| thinTrack | boolean (Switch only) |
| isInvalid | boolean |
| disabled | boolean |
| onChange | (checked: boolean) => void |
Radio props
| Prop | Type |
| ---------- | ------------------------- |
| label | ReactNode |
| variant | ColorVariant |
| value | string |
| checked | boolean |
| isInvalid | boolean |
| disabled | boolean |
| onChange | (value: string) => void |
Menu
Composable dropdown menu — render inside a positioned container toggled by your own trigger button.
import { Menu, MenuItem } from "@dnotrever2/super-kit";
const [open, setOpen] = useState(false);
<div style={{ position: "relative", display: "inline-block" }}>
<Button onClick={() => setOpen(o => !o)}>Options</Button>
{open && (
<div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, zIndex: 20 }}>
<Menu shadow style={{ width: 240 }}>
<MenuItem icon={<PlayIcon />} kbd="⌘R">Run workflow</MenuItem>
<MenuItem icon={<CopyIcon />}>Duplicate</MenuItem>
<MenuItem
submenuMode="external"
submenuTrigger="hover"
submenu={
<>
<MenuItem>Production</MenuItem>
<MenuItem>Staging</MenuItem>
<MenuItem>Preview</MenuItem>
</>
}
>
Deploy target
</MenuItem>
<MenuItem icon={<TrashIcon />} danger>Delete</MenuItem>
</Menu>
</div>
)}
</div>Menu props
| Prop | Type | Default |
| -------- | --------- | ------- |
| shadow | boolean | true |
| spacing | "sm" \| "md" \| "lg" | "sm" |
MenuItem props
| Prop | Type | Default |
| ---------- | ----------- | ------------------------ |
| icon | ReactNode | — |
| kbd | string | — keyboard shortcut hint |
| active | boolean | false |
| danger | boolean | false |
| submenu | ReactNode | — submenu items |
| submenuMode | "external" \| "internal" | "external" |
| submenuTrigger | "hover" \| "click" | "hover" |
| disabled | boolean | false |
Modal
Dialog overlay with backdrop. Renders as a div-based panel, not <dialog>.
There is no separate Dialog component by design: projects can compose their own dialog patterns using existing design system primitives such as Modal, Button, Input, and Textarea.
import { Modal } from "@dnotrever2/super-kit";
<Modal
open={isOpen}
title="Confirm delete"
overlay="blur"
shadow
draggable
onClose={() => setIsOpen(false)}
>
<p>This action cannot be undone.</p>
</Modal>| Prop | Type | Default |
| ---------- | ------------ | ------- |
| open | boolean | false |
| title | ReactNode | — |
| overlay | "none" \| "blur" \| "dim" | "blur" |
| shadow | boolean | true |
| draggable | boolean | false — drag by the modal header |
| onClose | () => void | — |
| children | ReactNode | — |
Popover
Anchored floating panel, controlled or uncontrolled.
import { Popover } from "@dnotrever2/super-kit";
<Popover
trigger={<Button variant="gray2">Settings</Button>}
title="Connection"
side="bottom-start"
shadow
>
<p>Content here</p>
</Popover>| Prop | Type | Default |
| ----------------- | ------------------------------------------------------------ | ---------------- |
| trigger | ReactNode | — |
| title | ReactNode | — |
| side | "bottom-start" \| "bottom-end" \| "top-start" \| "top-end" | "bottom-start" |
| shadow | boolean | true |
| openOnHover | boolean | false |
| open | boolean | — (controlled) |
| defaultOpen | boolean | false |
| showCloseButton | boolean | true |
| onOpenChange | (open: boolean) => void | — |
| popProps | HTMLAttributes<HTMLDivElement> | — |
The popover automatically flips between top/bottom and start/end when the preferred side would be clipped by the viewport.
Progress
Determinate or indeterminate progress bar.
import { Progress } from "@dnotrever2/super-kit";
<Progress value={64} label="Deploy progress" showValue />
<Progress shape="circle" value={72} label="Deploy" />
<Progress indeterminate label="Publishing" showValue />
<Progress value={82} variant="green2" size="lg" />| Prop | Type | Default |
| --------------- | -------------------------------------------------------------------------------------- | --------- |
| value | number | — |
| max | number | 100 |
| variant | blue/green/yellow/red/gray + 1/2/3, optional Soft suffix, e.g. "blue2Soft" | "blue2" |
| size | "sm" \| "md" \| "lg" | "md" |
| shape | "bar" \| "circle" | "bar" |
| label | ReactNode | — |
| valueLabel | string | — |
| showValue | boolean | false |
| indeterminate | boolean | false |
When value is omitted or indeterminate is true, the progress renders in loading mode. Circular progress shows the percentage in the center.
PushButton
Toggle button and grouped toolbar segments.
import { PushButton, PushButtonGroup } from "@dnotrever2/super-kit";
// Standalone toggle
<PushButton on={active} onClick={() => setActive(a => !a)} icon={<GridIcon />} />
// Grouped toolbar
<PushButtonGroup gap="sm" padding="sm" width={320} background rounded>
<PushButton on={view === "list"} onClick={() => setView("list")}>List</PushButton>
<PushButton
on={view === "grid"}
badge={<Badge variant="blue2" pill>2</Badge>}
onClick={() => setView("grid")}
>
Grid
</PushButton>
</PushButtonGroup>PushButtonGroup props
| Prop | Type | Default |
| ----- | ----------------------- | ------- |
| gap | "sm" \| "md" \| "lg" | "sm" |
| padding | "sm" \| "md" \| "lg" | "sm" |
| background | boolean | true |
| rounded | boolean | false |
| width | CSSProperties["width"] | — |
PushButton props
| Prop | Type | Default |
| ---------- | ----------- | --------------------------------------------- |
| on | boolean | false |
| icon | ReactNode | — |
| badge | ReactNode | — |
| badgePosition | "left" \| "right" | "right" |
| disabled | boolean | false |
Scrollable
Scrollable container with custom styled scrollbar.
import { Scrollable } from "@dnotrever2/super-kit";
<Scrollable height={200} track arrows scrollbarSize={8}>
{items.map(item => <div key={item}>{item}</div>)}
</Scrollable>| Prop | Type | Default |
| --------------- | -------------------------------------- | ---------------------------------------------------- |
| direction | "vertical" \| "horizontal" \| "both" | "vertical" |
| track | boolean | false — shows a visible track behind the thumb |
| arrows | boolean | false — shows arrow buttons (WebKit only) |
| scrollbarSize | number | 10 — scrollbar track width in pixels (WebKit only) |
| height | string \| number | — |
The scrollbar classes (sb, sb-track, sb-arrows) are also available as global CSS classes — apply them directly to any scrollable element.
Select
Single or multi-value dropdown with optional search and clearable state.
import { Select } from "@dnotrever2/super-kit";
const options = [
{ value: "us-east-1", label: "US East (N. Virginia)" },
{ value: "eu-west-1", label: "EU West (Ireland)" },
];
// Single
<Select label="region" options={options} onValueChange={(v) => setRegion(v)} />
// Options above
<Select label="region" options={options} optionsPosition="top" />
// Centered options
<Select label="region" options={options} optionsAlign="center" />
// Multi + searchable
<Select
label="regions"
options={options}
multiple
searchable
clearable
showSelectedValues={false}
showSelectedCount={false}
showClearAll={false}
onValueChange={(values) => setRegions(values)}
/>| Prop | Type | Default |
| ------------------------ | -------------------------------------- | ------------------------- |
| options | { value, label, meta?, disabled? }[] | required |
| value / defaultValue | string \| string[] \| null | null |
| multiple | boolean | false |
| searchable | boolean | false |
| clearable | boolean | false |
| disabled | boolean | false |
| placeholder | string | "Select" |
| optionsPosition | "bottom" \| "top" | "bottom" |
| optionsAlign | "left" \| "center" \| "right" | "left" |
| showSelectedCount | boolean | true |
| showClearAll | boolean | true |
| showSelectedValues | boolean | true |
| closeOnSelect | boolean | true single, false multi |
| selectProps | ButtonHTMLAttributes<HTMLButtonElement> | — |
| label | string | — |
| onValueChange | (value, selectedOptions) => void | — |
| filterOptions | (options, search) => options | built-in case-insensitive |
| isLoading | boolean | false |
| loadingLabel | string | "Loading..." |
| emptyLabel | string | "No options found" |
In multiple mode, the footer action toggles between Clear all and Check all.
Forwards a ref to the root <div>.
Tabs
Accessible tab navigation with raised, rounded, or underline styles.
import { Badge, Tabs } from "@dnotrever2/super-kit";
<Tabs
variant="raised"
items={[
{ value: "overview", label: "Overview", content: <Overview /> },
{
value: "deployments",
label: <>Deployments <Badge variant="blue2" pill>4</Badge></>,
content: <Deployments />
},
{ value: "settings", label: "Settings", content: <Settings /> }
]}
/>
<Tabs
variant="underline"
transparent
closable
onTabClose={(value) => closeTab(value)}
items={[
{ value: "overview", label: "Overview" },
{ value: "deployments", label: "Deployments" }
]}
/>
<Tabs
variant="rounded"
items={[
{ value: "daily", label: "Daily" },
{ value: "weekly", label: "Weekly" }
]}
/>| Prop | Type | Default |
| --------------- | ---------------------------- | ------------------- |
| items | TabItem[] | required |
| variant | "raised" \| "underline" \| "rounded" | "raised" |
| value | string | — controlled |
| defaultValue | string | first enabled item |
| onValueChange | (value: string) => void | — |
| ariaLabel | string | "Tabs" |
| disabled | boolean | false |
| closable | boolean | false |
| closeLabel | string | "Close tab" |
| onTabClose | (value: string) => void | — |
| tabItemClassName | string | — |
| tabClassName | string | — |
| transparent | boolean | false — underline only |
| inactiveTransparent | boolean | false — raised/rounded only |
TabItem fields
| Field | Type |
| ------------ | ----------------------------------------- |
| value | string |
| label | ReactNode |
| content | ReactNode |
| disabled | boolean |
| closable | boolean |
| closeLabel | string |
| onClose | (value: string) => void |
| className | string — class for the tab wrapper |
| tabProps | ButtonHTMLAttributes<HTMLButtonElement> |
| closeButtonProps | ButtonHTMLAttributes<HTMLButtonElement> |
| panelProps | HTMLAttributes<HTMLDivElement> |
Use tabItemClassName or TabItem.className to customize the tab wrapper dimensions such as height, width, and margin. Use tabClassName or tabProps.className to customize the inner tab button, such as padding.
Spinner
Loading indicator in three styles.
import { Spinner } from "@dnotrever2/super-kit";
<Spinner type="ring" variant="blue3" size="md" />
<Spinner type="dots" variant="green2" />
<Spinner type="bar" variant="yellow2" />
// Inside a blue button (light spinner on solid color background)
<Button variant="blue2" disabled icon={<Spinner size="sm" onColor />}>
Deploying
</Button>
// Subdued — when the selected variant is too visually dominant
<Spinner muted />| Prop | Type | Default |
| ---------- | --------------------------- | --------------------------------------------------------- |
| type | "ring" \| "dots" \| "bar" | "ring" |
| variant | ColorVariant | "blue3" |
| size | "sm" \| "md" \| "lg" | "md" — ring only |
| muted | boolean | false — gray animation instead of the selected variant |
| onColor | boolean | false — white arc for use on solid color backgrounds |
Steps
Step progress indicator with circular or arrow styles.
import { Steps } from "@dnotrever2/super-kit";
const items = [
{ label: "Step 1", description: "Configure the workspace" },
{ label: "Step 2", description: "Review the changes" },
{ label: "Step 3", description: "Publish the release" }
];
<Steps items={items} currentStep={2} variant="blue3" />
<Steps
items={items}
type="arrow"
variant="green2"
currentStep={2}
showNumbers={false}
/>| Prop | Type | Default |
| --------------- | ---------------------------- | ------------ |
| items | StepItem[] | required |
| currentStep | number | 1 |
| type | "line" \| "arrow" | "line" |
| variant | ColorVariant | "blue3" |
| size | "sm" \| "md" \| "lg" | "md" |
| clickable | boolean | false |
| showNumbers | boolean | true |
| onStepChange | (step: number) => void | — |
| stepClassName | string | — |
StepItem fields
| Field | Type |
| ------------- | ----------------------------------------- |
| label | ReactNode |
| description | ReactNode |
| icon | ReactNode |
| disabled | boolean |
| className | string |
| stepProps | ButtonHTMLAttributes<HTMLButtonElement> |
currentStep is 1-based. Completed steps are the steps before currentStep; the current step is highlighted.
Textarea
Multi-line text input with custom scrollbar, character count, and clear button.
import { Textarea } from "@dnotrever2/super-kit";
<Textarea
label="Description"
placeholder="Describe the deployment..."
maxLength={500}
helpText="Markdown supported"
clearable
/>
// Monospace (for code / config)
<Textarea label="config.yaml" mono />| Prop | Type | Default |
| ------------------------ | ------------------------- | ------- |
| label | string | — |
| helpText | string | — |
| maxLength | number | — |
| clearable | boolean | false |
| mono | boolean | false |
| value / defaultValue | string | "" |
| onValueChange | (value: string) => void | — |
| textareaProps | TextareaHTMLAttributes<HTMLTextAreaElement> | — |
| wrapperProps | HTMLAttributes<HTMLDivElement> | — |
| fieldProps | HTMLAttributes<HTMLDivElement> | — |
Forwards a ref to the underlying <textarea>.
Toast
Notification toasts via context provider and useToast hook.
// 1. Wrap your app
import { ToastProvider } from "@dnotrever2/super-kit";
<ToastProvider maxVisible={3} position="bottom-right" offset={24}>
<App />
</ToastProvider>
// 2. Trigger from anywhere
import { useToast } from "@dnotrever2/super-kit";
function DeployButton() {
const { toast } = useToast();
return (
<Button onClick={() => toast({ variant: "ok", title: "Deployed", message: "v2.4.1 is live" })}>
Deploy
</Button>
);
}
toast({
variant: "ok",
title: <>Deployed <Badge variant="green2" pill>live</Badge></>,
message: <span style={{ color: "var(--fg-1)", fontSize: 13, fontWeight: 600 }}>v2.4.1 is live</span>
});
function LoadingDeployButton() {
const { toast, dismiss } = useToast();
return (
<Button
onClick={() => {
const id = toast({
variant: "loading",
title: "Deploying",
message: "Please wait",
overlay: true,
shadow: true
});
deploy().finally(() => dismiss(id));
}}
>
Deploy
</Button>
);
}toast(options) options
| Field | Type | Default |
| ---------- | ---------------------------------------- | --------------------------- |
| variant | "ok" \| "error" \| "warning" \| "info" \| "loading" \| "neutral" | required |
| title | ReactNode | required |
| message | ReactNode | — |
| duration | number (ms) | 4000; loading defaults to 0 |
| overlay | boolean | false — only applies to loading |
| shadow | boolean | true |
toast() returns the toast id. Dismiss manually with dismiss(id). Loading toasts show a spinner and do not render a close button.
ToastProvider props
| Prop | Type | Default |
| ------------ | ----------- | ------------ |
| maxVisible | number | 3 |
| position | "top-left" \| "top-center" \| "top-right" \| "bottom-left" \| "bottom-center" \| "bottom-right" | "bottom-right" |
| offset | number | 24 |
| children | ReactNode | required |
Tooltip
Hover/focus tooltip around a child element.
import { Tooltip } from "@dnotrever2/super-kit";
<Tooltip content="Deploy to production" side="top">
<Button variant="blue2">Deploy</Button>
</Tooltip>
<Tooltip content="No delay" delay={0}>
<Button variant="blue2">Deploy</Button>
</Tooltip>
<Tooltip content="Fixed top" side="top" dynamic={false}>
<Button variant="blue2">Deploy</Button>
</Tooltip>
<Tooltip content="Cursor hint" cursor>
<Button variant="blue2">Hover me</Button>
</Tooltip>| Prop | Type | Default |
| -------------- | --------------------------------- | -------- |
| content | ReactNode | required |
| side | "top" \| "bottom" \| "left" \| "right" | "top" |
| delay | number (ms) | 800 |
| dynamic | boolean | true |
| cursor | boolean | true |
| viewportPadding | number | 8 |
| disabled | boolean | false |
| wrapperProps | HTMLAttributes<HTMLSpanElement> | — |
By default, the tooltip flips to the opposite side when the requested side would be clipped by the viewport. When cursor is enabled, the tooltip appears near the cursor only after it stops moving for the configured delay.
Design tokens
All tokens are CSS custom properties defined in src/styles/index.css and available globally via var(--token).
Surfaces
| Token | Value |
| -------- | -------------------------------- |
| --bg-0 | #0a0b0e — deepest background |
| --bg-1 | #15181f — card surface |
| --bg-2 | #1f242c — hover state |
| --bg-3 | #2a2f38 — popover / selected |
| --bg-4 | #343b46 — elevated highlight |
| --bg-input-1 | #151a22 — input surface, original |
| --bg-disabled-1 | #1c252e — disabled input surface |
| --bg-input-2 | #1d242d — lighter input surface |
| --bg-disabled-2 | #222a34 — lighter disabled input surface |
App Surfaces
| Token | Value |
| ---------------------------------- | ----------- |
| --surface-topbar | #222731 |
| --surface-sidebar | #1a2028 |
| --surface-tabs-default | #1d252e |
| --surface-tabs-hover | #29313b |
| --surface-tabs-selected | #333c47 |
| --surface-tabs-selected-hover | #3d4754 |
| --surface-push-button-bg | #1f242c |
| --surface-push-button-default | #252b34 |
| --surface-push-button-hover | #2d3540 |
| --surface-push-button-selected | #36404d |
| --surface-push-button-selected-hover | #404b59 |
| --surface-list-row-hover | rgba(255, 255, 255, 0.045) |
| --surface-list-row-selected | #354254 |
| --surface-list-row-selected-hover | #405068 |
| --surface-content | #252d37 |
| --surface-content-header | #2a333e |
| --surface-content-toolbar | #303946 |
| --surface-content-footer | #2a333e |
| --surface-modal | #1f2731 |
| --surface-modal-dialog | #29313b |
Modal uses --surface-modal by default. Apps can use --surface-modal-dialog in their own dialog class when they need the dialog-specific background.
Foreground
| Token | Value |
| ---------------- | ----------------------------- |
| --fg-1 | #f1f3f5 — primary text |
| --fg-2 | #aab2bd — secondary text |
| --fg-3 | #6b7280 — muted / labels |
| --fg-4 | #4a505a — placeholder |
| --fg-disabled | disabled foreground |
| --fg-on-color | #ffffff — text on solid color bg |
Color Scale
Buttons, badges, links, and progress variants use the global color scale tokens:
| Token pattern | Purpose |
| ------------- | ------- |
| --color-{blue|green|yellow|red|gray}-{1|2|3} | Solid tone |
| --color-{color}-{tone}-hover / -active | Solid interaction states |
| --color-{color}-{tone}-fg | Text on solid tone |
| --color-{color}-{tone}-soft | Soft background |
| --color-{color}-{tone}-soft-hover / -soft-active | Soft interaction states |
| --color-{color}-{tone}-soft-fg | Text/icon color for soft usage |
| --color-{color}-{tone}-outline | Outline border |
| --color-{color}-{tone}-link-hover | Link hover color |
Typography
| Token | Value |
| ------------- | -------------------------- |
| --font-sans | Geist, system-ui |
| --font-mono | Geist Mono, JetBrains Mono |
Spacing (4px base)
--s-1 (4px) · --s-2 (8px) · --s-3 (12px) · --s-4 (16px) · --s-5 (20px) · --s-6 (24px) · --s-8 (32px) · --s-10 (40px)
Radii
--r-1 (4px) · --r-2 (6px) · --r-3 (8px) · --r-full (999px)
Input heights
--h-input-sm (28px) · --h-input (32px) · --h-input-lg (36px)
Development
Start Storybook
npm run dev # http://localhost:6006Or with Docker:
make build # docker compose up --build
make restartStorybook visual customization
The Storybook manager uses the default Storybook UI. There is no custom manager.ts, manager theme, or visual addon beyond the standard addons in .storybook/main.ts.
The improved story canvas styling is defined in .storybook/preview.tsx:
backgroundsis disabled.controls.expandedis enabled.- The default
layoutispadded. - A global decorator wraps each story in a SuperKit-themed surface using
var(--bg-2),var(--border),var(--fg-1), andvar(--font-sans).
Stories can opt out of that wrapper with:
parameters: {
chromeless: true
}Use chromeless for fullscreen compositions such as Color Study; normal component stories should keep the default wrapper.
Type check
Always use tsconfig.build.json — the root config produces a false positive about vite.config.d.ts:
npm run typecheckBuild the library
npm run build
ls dist/super-kit.js dist/super-kit.cjs dist/index.d.tsPublish
make publish # verifies npm auth, builds, publishes current package versionConfiguração inicial (uma vez por máquina)
O make publish exige autenticação no npm. Siga os passos abaixo:
1. Entrar no npm
npm login
npm whoamiO npm whoami deve mostrar o usuário que tem permissão para publicar o pacote.
2. Alternativa com token
Acesse npmjs.com → seu perfil → Access Tokens → Generate New Token.
Escolha Granular Access Token e configure:
- Bypass two-factor authentication (2FA) → marque o checkbox
- Packages and scopes → Permissions → selecione "All packages" com "Read and write"
- Defina uma data de expiração e gere o token
Atenção: o token só é exibido uma vez. Copie antes de sair da página.
Configure o token no .npmrc do usuário ou no .npmrc local do projeto:
//registry.npmjs.org/:_authToken=npm_SEU_TOKEN_AQUISe existir .npmrc na raiz do projeto, ele tem prioridade sobre o login feito em ~/.npmrc.
3. Publicar
make publishPara incrementar a versão antes de publicar:
make version TYPE=patch # ou minor / major
make publishAdd a new component
- Create
src/ComponentName/ComponentName.tsx,ComponentName.module.css,ComponentName.stories.tsx,index.ts - Export from
src/index.ts:export * from "./ComponentName" - Run
npm run typecheckand confirm the story renders in Storybook
