@ibis-design/react
v0.9.0
Published
React component library for the IBIS design system.
Keywords
Readme
@ibis-design/react
React component library for the IBIS design system—buttons, inputs, feedback, and app shells—styled with tokens from @ibis-design/css.
Requirements
- React 18 or 19
- @ibis-design/css (peer dependency for tokens and component styles)
Installation
npm install @ibis-design/react @ibis-design/cssApp setup
Load tokens and layout/card styles once at the root of your app, then set brand and color mode on <html>.
import "@ibis-design/css";
import "@ibis-design/react/styles.css";
import { setTheme } from "@ibis-design/css/theme";
import { Button, TextInput } from "@ibis-design/react";
import { useEffect } from "react";
export const App = () => {
useEffect(() => {
setTheme({ brand: "ib", colorMode: "light" });
}, []);
return (
<>
<Button variant="primary" size="md">
Continue
</Button>
<TextInput label="Email" placeholder="[email protected]" />
</>
);
};| data-brand | Brand |
| ------------ | ------- |
| ib | Ibis |
| alc | Alchemy |
| data-color-mode | Mode |
| ----------------- | ----- |
| light | Light |
| dark | Dark |
import { setTheme } from "@ibis-design/css/theme";
setTheme({ brand: "alc", colorMode: "dark" });Your bundler must process CSS imports from @ibis-design/css and from this package (styles.css covers layout and Card styles; individual components import their own CSS).
Components
Button
Primary actions, form submit, and secondary cancel flows.
| Prop | Type | Default | Description |
| ---------- | --------------------------------- | ----------- | ------------------------ |
| variant | 'primary' \| 'secondary' | 'primary' | Visual style |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Padding and type size |
| type | 'button' \| 'submit' \| 'reset' | 'button' | Native button type |
| disabled | boolean | false | Disables interaction |
| loading | boolean | false | Spinner + disabled |
| skeleton | boolean | false | Placeholder shimmer |
| iconOnly | boolean | false | Square icon button |
| label | string | — | Text when no children |
import { Button } from "@ibis-design/react";
<Button variant="primary" size="md">Submit payment</Button>
<Button variant="secondary" size="sm">Cancel</Button>
<Button type="submit" loading>Saving…</Button>
<Button iconOnly aria-label="Search">🔍</Button>TextInput
Labeled text fields with help, error, prefix/suffix, and prepend blocks. Extends native <input> props (except prefix, which is a React node slot).
| Prop | Type | Default | Description |
| ------------- | ----------------------- | ------- | ------------------ |
| label | string | — | Field label |
| inputSize | 'sm' \| 'md' \| 'lg' | 'md' | Size scale |
| invalid | boolean | false | Error styling |
| error | string | — | Shown when invalid |
| description | string | — | Below label |
| helpText | string | — | Below field |
| loading | boolean | false | Loading state |
| prefix / suffix / prepend | ReactNode | — | Icons or blocks |
| prefixText / suffixText / prependText | string | — | Plain text slots |
import { useState } from "react";
import { TextInput } from "@ibis-design/react";
const [email, setEmail] = useState("");
<TextInput
label="Email"
type="email"
placeholder="[email protected]"
description="Used for receipts."
helpText="We never share your email."
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<TextInput
label="Amount"
invalid={!!error}
error={error}
prefixText="$"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>TextArea
Multi-line input; same sizing and validation props as TextInput (inputSize, invalid, error, loading, rows, etc.).
import { useState } from "react";
import { TextArea } from "@ibis-design/react";
const [notes, setNotes] = useState("");
<TextArea
label="Notes"
rows={4}
placeholder="Optional details…"
value={notes}
onChange={(e) => setNotes(e.target.value)}
/>Checkbox
| Prop | Type | Default | Description |
| --------------- | ---------------------- | ------- | --------------- |
| label | string | — | Label text |
| checked | boolean | — | Controlled |
| onChange | — | — | Native handler |
| inputSize | 'sm' \| 'md' \| 'lg' | 'md' | Control size |
| disabled | boolean | false | |
| invalid | boolean | false | |
| description | string | — | Helper copy |
| helpText | string | — | |
| error | string | — | |
| checkboxIcon | ReactNode | — | Custom checkmark |
import { useState } from "react";
import { Checkbox } from "@ibis-design/react";
const [accepted, setAccepted] = useState(false);
<Checkbox
label="I accept the terms"
description="Required to open an account."
checked={accepted}
onChange={(e) => setAccepted(e.target.checked)}
/>Radio
Share name across options and control selection with group + onGroupChange (or checked via group === value).
| Prop | Type | Default | Description |
| ---------------- | ---------------------- | ------- | -------------- |
| label | string | — | Option label |
| value | string \| number | required | Option value |
| group | string \| number | — | Selected value |
| onGroupChange | (value) => void | — | Group handler |
| name | string | — | Radio group |
| inputSize | 'sm' \| 'md' \| 'lg' | 'md' | |
| disabled / invalid | boolean | | |
import { useState } from "react";
import { Radio } from "@ibis-design/react";
const [accountType, setAccountType] = useState("personal");
<Radio
name="account"
label="Personal"
value="personal"
group={accountType}
onGroupChange={setAccountType}
/>
<Radio
name="account"
label="Business"
value="business"
group={accountType}
onGroupChange={setAccountType}
/>Switch
Toggle for settings; uses checkbox input semantics with checked and onChange.
import { useState } from "react";
import { Switch } from "@ibis-design/react";
const [enabled, setEnabled] = useState(true);
<Switch
label="Email notifications"
checked={enabled}
onChange={(e) => setEnabled(e.target.checked)}
/>Dropdown
Select-style control with options: { label, value }[] and controlled value / onValueChange.
import { useState } from "react";
import { Dropdown } from "@ibis-design/react";
const [currency, setCurrency] = useState("usd");
const options = [
{ label: "US Dollar", value: "usd" },
{ label: "Euro", value: "eur" },
];
<Dropdown
label="Currency"
options={options}
value={currency}
onValueChange={setCurrency}
placeholder="Choose currency"
/>DropdownButton
Menu anchored to a button. Pass menu as a render prop that receives close to dismiss after an action.
| Prop | Type | Description |
| ------------- | ---------------------------- | ------------------------ |
| label | string | Trigger button text |
| trigger | ReactNode | Custom trigger element |
| menu | (close: () => void) => ReactNode | Menu items |
| disabled | boolean | |
import { DropdownButton } from "@ibis-design/react";
<DropdownButton label="Account">
{(close) => (
<>
<button type="button" className="ibis-dropdown-menu__item" onClick={close}>
Profile
</button>
<button type="button" className="ibis-dropdown-menu__item" onClick={close}>
Sign out
</button>
</>
)}
</DropdownButton>Chips
Filter or toggle chips.
| Prop | Type | Default | Description |
| ------------------ | ---------------------- | ------- | ------------------ |
| size | 'sm' \| 'md' \| 'lg' | 'md' | |
| selected | boolean | false | Toggle state |
| onSelectedChange | (selected) => void | — | |
| loading / skeleton | boolean | false | |
| label | string | — | Text if no children |
| icon / iconText | ReactNode / string | — | Leading icon |
import { useState } from "react";
import { Chips } from "@ibis-design/react";
const [active, setActive] = useState(false);
<Chips label="Pending" selected={active} onSelectedChange={setActive} />
<Chips label="Starred" iconText="★" size="sm" />PillTab & PillTabs
Segmented control built on radio inputs. Wrap tabs in PillTabs; share name and group / onGroupChange.
import { useState } from "react";
import { PillTabs, PillTab } from "@ibis-design/react";
const [tab, setTab] = useState("overview");
<PillTabs>
<PillTab name="main" label="Overview" value="overview" group={tab} onGroupChange={setTab} />
<PillTab name="main" label="Activity" value="activity" group={tab} onGroupChange={setTab} />
<PillTab name="main" label="Settings" value="settings" group={tab} onGroupChange={setTab} />
</PillTabs>TextLink
Styled anchor with underline modes and optional icons.
| Prop | Type | Default |
| ----------- | --------------------------------- | --------- |
| href | string | — |
| underline | 'always' \| 'hover' \| 'none' | 'hover' |
| linkSize | 'sm' \| 'md' \| 'lg' | 'md' |
| disabled / loading / skeleton | boolean | false |
import { TextLink } from "@ibis-design/react";
<TextLink href="/help" underline="hover">
Need help?
</TextLink>Banner
Inline page-level message. Types: success, error, default. Optional closable, loading, icon / iconText, and children for rich body content.
import { Banner } from "@ibis-design/react";
<Banner
type="success"
title="Transfer complete"
message="Funds will arrive in 1–2 days."
closable
/>
<Banner type="error" title="Payment failed">
Check your card details and try again.
</Banner>Toaster
Compact toast-style message. Types: success, error, accent, default.
import { Toaster } from "@ibis-design/react";
<Toaster
type="accent"
title="Session expiring"
message="Sign in again to continue."
closable
/>TipIndicator
Hover/focus tooltip on an information indicator. Requires text.
| Prop | Type | Default |
| ---------- | ----------------------------------------- | -------- |
| text | string | required |
| position | 'top' \| 'bottom' \| 'left' \| 'right' | 'top' |
| width | 'auto' \| '240' \| '360' | 'auto' |
| disabled | boolean | false |
import { TipIndicator } from "@ibis-design/react";
<TipIndicator
text="International transfers may include fees."
position="top"
width="240"
/>Card
Simple surfaced container (requires styles.css import at app root).
import { Card } from "@ibis-design/react";
<Card>
<h2>Account summary</h2>
<p>Balance and recent activity.</p>
</Card>Layouts
Optional region props accept ReactNode. Main content goes in children.
AppLayout
header, sidebar, footer, and children (main).
import { AppLayout } from "@ibis-design/react";
<AppLayout
header={<nav>My app</nav>}
sidebar={<aside>Navigation</aside>}
footer={<footer>© IBIS</footer>}
>
<h1>Dashboard</h1>
<p>Main content.</p>
</AppLayout>AuthLayout
Centered column for sign-in / sign-up. Optional logo and footer.
import { AuthLayout } from "@ibis-design/react";
<AuthLayout logo={<img src="/logo.svg" alt="IBIS" />}>
<form>
<h1>Sign in</h1>
{/* fields */}
</form>
</AuthLayout>DashboardLayout
Dashboard shell with themed header and sidebar (same region API as AppLayout).
import { DashboardLayout } from "@ibis-design/react";
<DashboardLayout header={<header>Dashboard</header>} sidebar={<nav>Menu</nav>}>
<p>Widgets and charts.</p>
</DashboardLayout>Vanilla HTML / other frameworks
Component classes and tokens live in @ibis-design/css. Use this package for ready-made React components; use the CSS package directly for Vue or plain HTML. See the @ibis-design/css README.
For Svelte 5, see @ibis-design/svelte.
Exports
Components: Button, DropdownButton, PillTab, PillTabs, TextInput, TextArea, Checkbox, Radio, Switch, Dropdown, Chips, TextLink, Banner, Toaster, TipIndicator, Card, AuthLayout, AppLayout, DashboardLayout
Types: ButtonVariant, ButtonSize, BannerType, ToasterType, DropdownOption, AuthLayoutProps, AppLayoutProps, DashboardLayoutProps
import { Button, type ButtonVariant } from "@ibis-design/react";