A design system for Anephenix.
npm i @anephenix/ui --save
- Node.js (24+)
- React (18+)
Import the components and the stylesheet in your project:
import { Button, Input, NavBar } from '@anephenix/ui';
import '@anephenix/ui/dist/index.css';Components are grouped below by category.
Page
A full-page wrapper div. Wrap your entire app content inside it.
import { Page } from '@anephenix/ui';
<Page>
<p>Your content here</p>
</Page>| Prop | Type | Description |
|---|---|---|
children |
node |
Content to render inside the page wrapper |
Hero
A hero section with a heading, description, and call-to-action buttons.
import { Hero } from '@anephenix/ui';
const ctas = [
{ href: '/get-started', text: 'Get started', buttonClass: 'primary' },
{ href: '/docs', text: 'Documentation', buttonClass: 'secondary' },
];
<Hero
title="Welcome"
description="A short description of your product."
ctas={ctas}
Link={Link}
/>| Prop | Type | Description |
|---|---|---|
title |
string |
The main heading |
description |
string | node |
A short description (string is wrapped in <p>) |
ctas |
array |
Array of { href, text, buttonClass } objects |
Link |
component |
Optional router link component (falls back to <a>) |
Footer
A two-column footer with left and right sections.
import { Footer } from '@anephenix/ui';
<Footer
leftSection={<span>© 2026 Acme Ltd.</span>}
rightSection={<a href="/privacy">Privacy</a>}
/>| Prop | Type | Description |
|---|---|---|
leftSection |
node |
Content for the left side of the footer |
rightSection |
node |
Content for the right side of the footer |
Breadcrumb
A navigation trail showing the user's location within a hierarchy. Follows the WAI-ARIA Breadcrumb pattern.
import { Breadcrumb } from '@anephenix/ui';
<Breadcrumb
items={[
{ label: 'Home', href: '/' },
{ label: 'Products', href: '/products' },
{ label: 'Laptops' },
]}
/>| Prop | Type | Description |
|---|---|---|
items |
array |
Array of { label, href? } objects. Omit href on the last item to mark it as the current page |
separator |
string | node |
Separator rendered between items. Defaults to "/" |
className |
string |
Optional additional CSS class on the list |
NavBar
A responsive navigation bar with a hamburger menu for mobile and a desktop menu.
import { NavBar } from '@anephenix/ui';
const links = [
{
id: 'home',
text: 'Home',
url: '/',
hideOnDesktop: false,
hideOptions: ({ loggedIn }) => true,
},
];
<NavBar
logo={<a href="/">My App</a>}
links={links}
loggedIn={false}
Link={Link}
/>| Prop | Type | Description |
|---|---|---|
logo |
node |
Logo element rendered on the left |
links |
array |
Array of link objects (see shape below) |
loggedIn |
bool |
Passed to each link's hideOptions function |
Link |
component |
Router link component used for internal URLs |
className |
string |
Optional additional class for the nav bar element |
midSection |
node |
Optional element rendered between logo and hamburger |
Each link object:
| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier |
text |
string |
Display text |
url |
string |
Href (use onClick to render a button instead) |
onClick |
function |
Click handler (renders a <button>) |
hideOnDesktop |
bool |
Hide this link in the desktop menu |
hideOptions |
function |
({ loggedIn }) => bool — return true to show |
target |
string |
_blank etc. |
rel |
string |
Link rel attribute |
Pagination
Page-number controls with first, previous, next, and last buttons. Returns null when totalPages is less than 2.
import { Pagination } from '@anephenix/ui';
const [page, setPage] = useState(1);
<Pagination
currentPage={page}
totalPages={20}
onPageChange={setPage}
/>| Prop | Type | Description |
|---|---|---|
currentPage |
number |
The currently active page (1-based) |
totalPages |
number |
Total number of pages |
onPageChange |
function |
Called with the new page number when any button is clicked |
siblingCount |
number |
Pages shown on each side of the current page. Defaults to 1 |
showFirstLast |
bool |
Show First and Last buttons. Defaults to true |
className |
string |
Optional additional CSS class on the nav |
Button
A basic button element. Accepts a forwardRef.
import { Button } from '@anephenix/ui';
<Button text="Submit" className="button theme-default primary" onClick={handleClick} />| Prop | Type | Description |
|---|---|---|
text |
string |
Button label |
name |
string |
Name attribute |
className |
string |
CSS class(es) |
onClick |
function |
Click handler |
Button variant classes (combine with button theme-default):
| Class | Alternate |
|---|---|
primary |
primary alternate |
secondary |
secondary alternate |
tertiary |
tertiary alternate |
green-one |
green-one alternate |
green-two |
green-two alternate |
blue-one |
blue-one alternate |
blue-two |
blue-two alternate |
Checkbox
A styled checkbox with a label. Accepts a forwardRef.
import { Checkbox } from '@anephenix/ui';
<Checkbox name="agree" label="I agree to the terms" defaultValue={false} />| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute |
label |
string |
Label text |
className |
string |
CSS class(es) |
defaultValue |
bool |
Whether the checkbox is checked by default |
Switch
A toggle switch for binary on/off settings. Supports controlled and uncontrolled usage. Accepts a forwardRef.
import { Switch } from '@anephenix/ui';
// Uncontrolled
<Switch name="notifications" label="Enable notifications" defaultChecked={false} />
// Controlled
const [enabled, setEnabled] = useState(false);
<Switch
name="darkMode"
label="Dark mode"
checked={enabled}
onChange={(e) => setEnabled(e.target.checked)}
/>| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute |
label |
string |
Optional label text |
checked |
bool |
Checked state in controlled mode |
defaultChecked |
bool |
Initial checked state in uncontrolled mode |
onChange |
function |
Change handler — receives the native input change event |
disabled |
bool |
Disables interaction and dims the switch |
className |
string |
Optional additional CSS class |
Input
A text input element. Accepts a forwardRef.
import { Input } from '@anephenix/ui';
<Input
name="email"
type="email"
placeholder="you@example.com"
onChange={handleChange}
/>| Prop | Type | Default | Description |
|---|---|---|---|
type |
string |
"text" |
Input type |
name |
string |
Name attribute | |
className |
string |
CSS class(es) | |
defaultValue |
string |
Initial value | |
placeholder |
string |
Placeholder text | |
onChange |
function |
Change handler |
RadioButton
A styled radio button with a label. Accepts a forwardRef.
import { RadioButton } from '@anephenix/ui';
const [selected, setSelected] = useState('');
<RadioButton
name="colour"
label="Red"
value="red"
checked={selected === 'red'}
onChange={() => setSelected('red')}
/>| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute (shared across a group) |
label |
string |
Label text |
className |
string |
CSS class(es) |
value |
string |
Value for this option (controlled mode) |
checked |
bool |
Whether this option is selected (controlled mode) |
onChange |
function |
Called when this option is selected |
defaultValue |
string |
Value attribute for uncontrolled usage |
Select
A styled <select> element. Accepts a forwardRef.
import { Select } from '@anephenix/ui';
const options = [
{ value: 'gb', label: 'United Kingdom' },
{ value: 'us', label: 'United States' },
];
<Select name="country" options={options} onChange={handleChange} />| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute |
className |
string |
CSS class(es) |
defaultValue |
string |
Initially selected value |
onChange |
function |
Change handler |
options |
array |
Array of { value, label } objects |
Dropdown
A styled dropdown using a <select> element that supports multiple selection. Accepts a forwardRef. Options use a text field (not label) to distinguish it from Select.
import { Dropdown } from '@anephenix/ui';
const options = [
{ value: 'js', text: 'JavaScript' },
{ value: 'ts', text: 'TypeScript' },
];
<Dropdown name="language" options={options} />| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute |
id |
string |
Element id |
className |
string |
CSS class(es) |
multiple |
bool |
Allow multiple selections |
defaultValue |
string | array |
Initially selected value(s) |
options |
array |
Array of { value, text } objects |
ComboBox
A searchable input that filters a list of options as the user types. Supports keyboard navigation (arrow keys, Enter, Escape), outside-click dismissal, and full ARIA combobox semantics.
import { ComboBox } from '@anephenix/ui';
const options = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
];
const [selected, setSelected] = useState(null);
<ComboBox
options={options}
placeholder="Search fruit..."
onSelect={(option) => setSelected(option)}
/>| Prop | Type | Description |
|---|---|---|
options |
array |
Array of { value, label } objects. Defaults to [] |
value |
string |
Initial text value of the input (uncontrolled seed) |
onChange |
function |
Called with the current input string on every keystroke |
onSelect |
function |
Called with the selected { value, label } object when an option is chosen |
placeholder |
string |
Placeholder text for the input. Defaults to "Search..." |
disabled |
bool |
Disables the input and prevents interaction. Defaults to false |
Textarea
A multi-line text input. Accepts a forwardRef.
import { Textarea } from '@anephenix/ui';
<Textarea name="message" placeholder="Enter your message..." onChange={handleChange} />| Prop | Type | Description |
|---|---|---|
name |
string |
Name attribute |
className |
string |
CSS class(es) |
defaultValue |
string |
Initial value |
placeholder |
string |
Placeholder text |
onChange |
function |
Change handler |
FormField
A wrapper for form inputs that displays a validation error message below the input.
import { FormField, Input } from '@anephenix/ui';
<FormField error="This field is required">
<Input name="email" type="email" />
</FormField>| Prop | Type | Description |
|---|---|---|
children |
node |
The form control(s) to wrap |
error |
string |
Error message to display (omit when valid) |
Accordion
Collapsible sections that expand and collapse to reveal content. Follows the WAI-ARIA Accordion pattern. Panels animate open and closed.
import { Accordion } from '@anephenix/ui';
const items = [
{ id: 'q1', title: 'What is this?', content: <p>An answer.</p> },
{ id: 'q2', title: 'How does it work?', content: <p>Another answer.</p> },
];
<Accordion items={items} defaultOpen="q1" />| Prop | Type | Description |
|---|---|---|
items |
array |
Array of { id, title, content } objects |
allowMultiple |
bool |
Allow more than one panel open at a time. Defaults to false |
defaultOpen |
string | string[] |
Id(s) of item(s) open on first render |
onChange |
function |
Called with an array of currently open ids on change |
className |
string |
Optional additional CSS class |
Alert
An inline status banner for contextual feedback. The role is automatically set to "alert" for error/warning variants and "status" for info/success.
import { Alert } from '@anephenix/ui';
// Persistent
<Alert variant="success" title="Saved">Your changes have been saved.</Alert>
// Dismissible
<Alert variant="warning" onClose={() => setVisible(false)}>
Your session will expire in 5 minutes.
</Alert>| Prop | Type | Description |
|---|---|---|
variant |
"info" | "success" | "warning" | "error" |
Visual style. Defaults to "info" |
title |
string |
Optional bold heading above the body |
children |
node |
The alert content |
onClose |
function |
Optional dismiss callback — renders a close button when provided |
className |
string |
Optional additional CSS class |
Divider
A visual separator between sections of content. Supports horizontal and vertical orientations, three line styles, and an optional centred text label.
import { Divider } from '@anephenix/ui';
// Basic horizontal divider
<Divider />
// With a centred label
<Divider label="or" />
// Dashed variant
<Divider variant="dashed" />
// Vertical (inside a flex container)
<div style={{ display: 'flex', height: '2rem', alignItems: 'center', gap: '1rem' }}>
<span>Left</span>
<Divider orientation="vertical" />
<span>Right</span>
</div>| Prop | Type | Description |
|---|---|---|
orientation |
"horizontal" | "vertical" |
Direction of the divider. Defaults to "horizontal" |
variant |
"solid" | "dashed" | "dotted" |
Line style. Defaults to "solid" |
label |
string |
Optional text centred on the divider line |
className |
string |
Optional additional CSS class on the wrapper |
Avatar
A user profile picture with three automatic states: image, initials (derived from name), and a generic icon fallback. The initials background colour is consistently derived from the name.
import { Avatar } from '@anephenix/ui';
// Image
<Avatar src="/photo.jpg" alt="Alice Brown" />
// Initials fallback
<Avatar name="Alice Brown" size="lg" />
// Icon fallback
<Avatar size="md" shape="rounded" />| Prop | Type | Description |
|---|---|---|
src |
string |
Image URL. Falls back to initials or icon on error |
alt |
string |
Alt text for the image. Defaults to name if provided |
name |
string |
Used to generate initials and a consistent background colour |
size |
"sm" | "md" | "lg" | "xl" |
Avatar diameter. Defaults to "md" |
shape |
"circle" | "rounded" | "square" |
Border radius style. Defaults to "circle" |
className |
string |
Optional additional CSS class |
Badge
A small inline label for counts, status indicators, and tags.
import { Badge } from '@anephenix/ui';
<Badge variant="success">Active</Badge>
<Badge variant="error" size="sm">99+</Badge>
<Badge variant="default">React</Badge>| Prop | Type | Description |
|---|---|---|
children |
node |
The badge content |
variant |
"default" | "primary" | "secondary" | "success" | "warning" | "info" | "error" |
Colour scheme. Defaults to "default" |
size |
"sm" | "md" | "lg" |
Size preset. Defaults to "md" |
className |
string |
Optional additional CSS class |
Card
A flexible content container with optional image, header, body, and footer sections. Width fills the parent.
import { Card } from '@anephenix/ui';
<Card
title="Getting started"
subtitle="Up and running in minutes"
footer={<Button text="View docs" className="button theme-default primary" />}
>
<p>Build consistent UIs with a plain-CSS design system.</p>
</Card>| Prop | Type | Description |
|---|---|---|
title |
string |
Optional heading in the card header |
subtitle |
string |
Optional secondary line below the title |
image |
string |
Optional URL for a cover image at the top |
imageAlt |
string |
Alt text for the image. Defaults to "" |
children |
node |
Main body content |
footer |
node |
Optional footer content |
className |
string |
Optional additional CSS class |
Code
A code block with syntax highlighting, line numbers, a title bar, and a copy button.
import { Code } from '@anephenix/ui';
const snippet = `const greet = (name) => \`Hello, \${name}!\`;`;
<Code title="greet.js" code={snippet} language="javascript" />| Prop | Type | Default | Description |
|---|---|---|---|
title |
string |
Title shown in the bar above the code | |
code |
string |
The code string to display | |
language |
string |
"javascript" |
Syntax highlighting language |
Popover
A click-triggered floating panel for richer interactive content such as forms, switches, or option lists. Closes on Escape, outside click, or the × button.
import { Popover } from '@anephenix/ui';
<Popover
trigger={<Button text="Options" className="button theme-default secondary" />}
title="Display options"
content={<p>Popover body content.</p>}
position="bottom"
/>| Prop | Type | Description |
|---|---|---|
trigger |
element |
The element that toggles the popover |
content |
node |
Content rendered in the popover body |
title |
string |
Optional heading shown in the popover header |
position |
"bottom" | "top" | "left" | "right" |
Placement relative to the trigger. Defaults to "bottom" |
className |
string |
Optional additional CSS class |
ProgressBar
A horizontal bar that communicates the completion state of an operation. Supports four colour variants, three sizes, an optional label and percentage display, and an indeterminate animated state for operations of unknown duration.
import { ProgressBar } from '@anephenix/ui';
<ProgressBar value={75} label="Uploading..." showValue />
<ProgressBar value={100} variant="success" label="Complete" showValue />
<ProgressBar indeterminate label="Processing..." />| Prop | Type | Description |
|---|---|---|
value |
number |
Current progress value. Defaults to 0 |
max |
number |
Maximum value. Defaults to 100 |
variant |
"default" | "success" | "warning" | "error" |
Colour of the fill. Defaults to "default" |
size |
"sm" | "md" | "lg" |
Height of the bar. Defaults to "md" |
label |
string |
Optional label rendered above the bar |
showValue |
bool |
Show the computed percentage to the right of the label. Defaults to false |
indeterminate |
bool |
Animates the bar for operations of unknown duration. Defaults to false |
className |
string |
Optional additional CSS class on the wrapper |
Skeleton
A shimmer placeholder that mimics the shape of content while it loads.
import { Skeleton } from '@anephenix/ui';
// Single text line
<Skeleton width="60%" />
// Paragraph
<Skeleton lines={3} />
// Avatar circle
<Skeleton width={48} height={48} borderRadius="50%" />| Prop | Type | Description |
|---|---|---|
width |
string | number |
Width. Numbers are converted to px. Defaults to "100%" |
height |
string | number |
Height. Numbers are converted to px. Defaults to "1rem" |
borderRadius |
string |
Border radius. Use "50%" for circles. Defaults to "4px" |
lines |
number |
Render multiple stacked lines. The last line is 70% wide. Defaults to 1 |
className |
string |
Optional additional CSS class |
Spinner
An animated loading indicator for indeterminate progress.
import { Spinner } from '@anephenix/ui';
<Spinner size="md" label="Loading..." />| Prop | Type | Description |
|---|---|---|
size |
"sm" | "md" | "lg" |
Controls the diameter and border width. Defaults to "md" |
label |
string |
Accessible aria-label for screen readers. Defaults to "Loading..." |
className |
string |
Optional additional CSS class |
Table
A data table with column definitions and optional custom cell renderers. Horizontally scrollable on small screens.
import { Table } from '@anephenix/ui';
const columns = [
{ key: 'name', header: 'Name' },
{ key: 'role', header: 'Role' },
{
key: 'status',
header: 'Status',
render: (value) => <strong>{value}</strong>,
},
];
const rows = [
{ id: 1, name: 'Alice Chen', role: 'Engineer', status: 'Active' },
{ id: 2, name: 'Bob Smith', role: 'Designer', status: 'Inactive' },
];
<Table columns={columns} rows={rows} caption="Team members" />| Prop | Type | Description |
|---|---|---|
columns |
array |
Array of column definition objects (see shape below) |
rows |
array |
Array of row data objects |
rowKey |
string |
Field name used as the React key for each row. Defaults to "id" |
caption |
string |
Optional accessible caption rendered above the table |
className |
string |
Optional additional CSS class on the <table> element |
Each column object:
| Field | Type | Description |
|---|---|---|
key |
string |
Field name from the row object |
header |
string |
Column header label |
render |
function |
Optional (value, row) => node custom cell renderer |
Tabs
Content-switching tabs with full WAI-ARIA keyboard support (arrow keys, roving tabIndex).
import { Tabs } from '@anephenix/ui';
const tabs = [
{ id: 'overview', label: 'Overview', content: <p>Overview content.</p> },
{ id: 'specs', label: 'Specifications', content: <p>Specs here.</p> },
];
<Tabs tabs={tabs} defaultTab="overview" onChange={(id) => console.log(id)} />| Prop | Type | Description |
|---|---|---|
tabs |
array |
Array of { id, label, content } objects |
defaultTab |
string |
Id of the initially selected tab. Defaults to the first tab |
onChange |
function |
Optional (id) => void called when the active tab changes |
className |
string |
Optional additional CSS class on the wrapper |
Terminal
A terminal-style block for showing shell commands or plain-text output. Includes a copy button.
import { Terminal } from '@anephenix/ui';
<Terminal title="Install" code="npm i @anephenix/ui" />| Prop | Type | Description |
|---|---|---|
title |
string |
Title shown in the bar above the output |
code |
string |
The text to display |
Tooltip
A small floating label that appears on hover and keyboard focus. Shown and hidden entirely via CSS (:hover and :focus-within).
import { Tooltip } from '@anephenix/ui';
<Tooltip content="Save your changes" position="top">
<button type="button">Save</button>
</Tooltip>| Prop | Type | Description |
|---|---|---|
content |
string | node |
The tooltip text or content |
position |
"top" | "bottom" | "left" | "right" |
Placement relative to the child. Defaults to "top" |
children |
node |
The element the tooltip is attached to |
className |
string |
Optional additional CSS class on the tooltip bubble |
Modal
A dialog overlay built on the native <dialog> element. Traps focus, closes on Escape, and supports an optional footer for action buttons.
import { Modal } from '@anephenix/ui';
const [isOpen, setIsOpen] = useState(false);
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Confirm action"
footer={
<>
<Button text="Cancel" className="button theme-default secondary alternate" onClick={() => setIsOpen(false)} />
<Button text="Confirm" className="button theme-default primary" onClick={() => setIsOpen(false)} />
</>
}
>
<p>Are you sure you want to do this?</p>
</Modal>| Prop | Type | Description |
|---|---|---|
isOpen |
bool |
Controls whether the modal is shown |
onClose |
function |
Called when the user dismisses the modal |
title |
string |
Heading shown in the modal header |
children |
node |
Content rendered in the modal body |
footer |
node |
Optional footer content |
Toast
A fixed-position notification with four variants and optional auto-dismiss.
import { Toast } from '@anephenix/ui';
const [isVisible, setIsVisible] = useState(false);
<Toast
isVisible={isVisible}
title="Saved"
message="Your changes have been saved."
variant="success"
duration={4000}
onClose={() => setIsVisible(false)}
/>| Prop | Type | Description |
|---|---|---|
isVisible |
bool |
Controls whether the toast is shown |
message |
string |
The notification text |
title |
string |
Optional bold heading above the message |
variant |
"info" | "success" | "warning" | "error" |
Visual style. Defaults to "info" |
position |
"top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center" |
Corner of the viewport. Defaults to "top-right" |
duration |
number |
Milliseconds before auto-dismissing. Pass 0 to disable. Defaults to 4000 |
onClose |
function |
Called when the close button is clicked or the duration elapses |
handleErrors
A helper that extracts validation errors from an Axios-style API response and normalises them into an array.
import { handleErrors } from '@anephenix/ui';
try {
await api.post('/login', data);
} catch (err) {
handleErrors(setError, err);
}setError receives an array of { name, message } objects corresponding to the server-returned field errors.
npm t
©2026 Anephenix Ltd. UI is licenced under the MIT Licence.