RG
diff --git a/packages/demo/src/components/demo/checkbox.tsx b/packages/demo/src/components/demo/checkbox.tsx
new file mode 100644
index 00000000..f5c7d918
--- /dev/null
+++ b/packages/demo/src/components/demo/checkbox.tsx
@@ -0,0 +1,6 @@
+import { Checkbox } from "@eqtylab/equality";
+import { Minus } from "lucide-react";
+
+export const CheckboxCustomIconDemo = () => {
+ return
;
+};
diff --git a/packages/demo/src/components/demo/command.tsx b/packages/demo/src/components/demo/command.tsx
index 6a25a4fe..dd278d3e 100644
--- a/packages/demo/src/components/demo/command.tsx
+++ b/packages/demo/src/components/demo/command.tsx
@@ -1,29 +1,33 @@
import {
Command,
CommandInput,
+ CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
} from "@eqtylab/equality";
import { Check } from "lucide-react";
+import { useState } from "react";
+
+const options = ["Option 1", "Option 2", "Option 3"];
export const CommandDemo = () => {
+ const [selected, setSelected] = useState("Option 1");
+
return (
- No repositories found.
-
- {}}>
- Option 1
-
-
- {}}>
- Option 2
-
- {}}>
- Option 3
-
-
+
+ No repositories found.
+
+ {options.map((option) => (
+
+ {option}
+ {selected === option && }
+
+ ))}
+
+
);
};
diff --git a/packages/demo/src/components/demo/drawer.tsx b/packages/demo/src/components/demo/drawer.tsx
deleted file mode 100644
index 631960b1..00000000
--- a/packages/demo/src/components/demo/drawer.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import {
- Button,
- Drawer,
- DrawerContent,
- DrawerHeader,
- DrawerTitle,
- DrawerDescription,
- DrawerFooter,
- DrawerHeaderActions,
- DrawerInnerContent,
-} from "@eqtylab/equality";
-import { useState } from "react";
-import { X } from "lucide-react";
-
-export const DrawerDemo = () => {
- const [open, setOpen] = useState(false);
-
- const onClose = () => {
- setOpen(false);
- };
-
- return (
-
- setOpen(true)}>
- Open Drawer
-
-
-
-
-
- Drawer Title
- Drawer Description
-
-
-
-
-
-
-
- Drawer Content
-
-
-
- Close
-
-
-
-
-
- );
-};
diff --git a/packages/demo/src/components/demo/icon.tsx b/packages/demo/src/components/demo/icon.tsx
index ed091c9b..6245bed3 100644
--- a/packages/demo/src/components/demo/icon.tsx
+++ b/packages/demo/src/components/demo/icon.tsx
@@ -21,7 +21,7 @@ const CustomIcon = ({ className, ...props }: React.SVGProps
) => {
export const IconDemo = () => {
return (
-
+
} background="circle" />
);
diff --git a/packages/demo/src/components/demo/info-card.tsx b/packages/demo/src/components/demo/info-card.tsx
new file mode 100644
index 00000000..d299664a
--- /dev/null
+++ b/packages/demo/src/components/demo/info-card.tsx
@@ -0,0 +1,12 @@
+import { InfoCard } from "@eqtylab/equality";
+
+export const InfoCardClickableDemo = () => {
+ return (
+
alert("Clicked!")}
+ />
+ );
+};
diff --git a/packages/demo/src/components/demo/motion-collapsible-content.tsx b/packages/demo/src/components/demo/motion-collapsible-content.tsx
index 672b8dd3..7c40a3c7 100644
--- a/packages/demo/src/components/demo/motion-collapsible-content.tsx
+++ b/packages/demo/src/components/demo/motion-collapsible-content.tsx
@@ -6,7 +6,9 @@ export const MotionCollapsibleContentDemo = () => {
return (
-
setIsOpen(!isOpen)}>Click me
+
setIsOpen(!isOpen)} className="w-max">
+ Click me
+
Motion Collapsible Content
diff --git a/packages/demo/src/components/demo/pagination.tsx b/packages/demo/src/components/demo/pagination.tsx
index 5850db21..242e9983 100644
--- a/packages/demo/src/components/demo/pagination.tsx
+++ b/packages/demo/src/components/demo/pagination.tsx
@@ -6,7 +6,6 @@ const generateItems = (count: number) => {
return Array.from({ length: count }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
- description: `Description for item ${i + 1}`,
}));
};
@@ -38,12 +37,9 @@ export const PaginationDemo = () => {
{paginatedItems.map((item) => (
{item.name}
-
- {item.description}
-
))}
diff --git a/packages/demo/src/components/demo/popover.tsx b/packages/demo/src/components/demo/popover.tsx
index b058d743..47207964 100644
--- a/packages/demo/src/components/demo/popover.tsx
+++ b/packages/demo/src/components/demo/popover.tsx
@@ -7,17 +7,21 @@ import {
export function PopoverDemo({
align = "center",
+ arrow = false,
}: {
align?: "start" | "end" | "center";
+ arrow?: boolean;
}) {
return (
-
-
- Click me
-
-
- This is a popover
-
-
+
+
+
+ Click me
+
+
+ This is a popover
+
+
+
);
}
diff --git a/packages/demo/src/components/demo/radio-group.tsx b/packages/demo/src/components/demo/radio-group.tsx
index 5c7c8277..7c036fdc 100644
--- a/packages/demo/src/components/demo/radio-group.tsx
+++ b/packages/demo/src/components/demo/radio-group.tsx
@@ -10,15 +10,15 @@ export const RadioGroupDemo = () => {
onValueChange={(value) => setSelectedValue(value)}
>
-
+
No Access
-
+
Read Only
-
+
Read & Write
diff --git a/packages/demo/src/components/demo/select.tsx b/packages/demo/src/components/demo/select.tsx
index d1af1244..732711d2 100644
--- a/packages/demo/src/components/demo/select.tsx
+++ b/packages/demo/src/components/demo/select.tsx
@@ -67,6 +67,22 @@ export function SelectDemo({
Option 12
Option 13
Option 14
+ Option 15
+ Option 16
+ Option 17
+ Option 18
+ Option 19
+ Option 20
+ Option 21
+ Option 22
+ Option 23
+ Option 24
+ Option 25
+ Option 26
+ Option 27
+ Option 28
+ Option 29
+ Option 30
);
diff --git a/packages/demo/src/content/components/alert.mdx b/packages/demo/src/content/components/alert.mdx
index 9ff08a60..aa985abc 100644
--- a/packages/demo/src/content/components/alert.mdx
+++ b/packages/demo/src/content/components/alert.mdx
@@ -1,15 +1,13 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Alert"
-description: "Alert with sizes and variants"
+description: "Message for communicating status, warnings, or errors"
---
import { Alert } from "@eqtylab/equality";
## Overview
----
-
The Alert component displays important messages to users, usually as a result of an action they took. Use alerts to communicate status, warnings, errors, or success states.
- **Primary:** General information.
@@ -20,8 +18,6 @@ The Alert component displays important messages to users, usually as a result of
## Usage
----
-
Import the component:
```tsx
@@ -36,8 +32,6 @@ Basic usage with title and description:
## Variants
----
-
`. It is intended as a decorative backdrop for hero sections, landing pages, and empty states, sitting behind foreground content.
+
+The gradient colour is driven directly by the brand colours in `equality-tokens.json`. The `theme` prop accepts any brand colour key (`primary`, `secondary`, `green`, `red`, `yellow`), and the component reads the corresponding hex value straight from the tokens — so any brand colours added in the future are picked up automatically. An unrecognised theme falls back to black.
+
+The `` is sized to the window and positioned by its container, so place `BgGradient` inside a `relative`, sized wrapper.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { BgGradient } from "@eqtylab/equality";
+```
+
+Basic usage. `theme` is required; `placement` defaults to `"full"`. Wrap it in a positioned, sized container so the canvas has room to fill:
+
+```tsx
+
+
+
+```
## Theme Variants
-### Theme Primary
+Set the `theme` prop to any brand colour key to recolour the gradient.
-
+### Primary
-### Theme Secondary
+
+
+### Secondary
-### Theme Tertiary
+### Green
-
+
-### Theme Red
+### Red
-### Theme Yellow
+### Yellow
+### Usage
+
+```tsx
+
+
+
+
+
+```
+
## Placement Variants
-You can control where the background gradient appears by selecting its placement. Use the `placement` prop to choose between `"full"` (covering the entire container) or `"top"` (applying the gradient only to the top portion). This allows flexible use for backgrounds that need either total coverage or more subtle decoration across the top.
+Use the `placement` prop to control where the blobs are distributed. `"full"` (the default) scatters four blobs around all four corners for total coverage, while `"top"` uses two blobs anchored near the top edge for a subtler decoration that leaves the lower area clear for content.
### Placement Full
@@ -43,3 +75,17 @@ You can control where the background gradient appears by selecting its placement
### Placement Top
+
+### Usage
+
+```tsx
+
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ------- | -------- |
+| `theme` | Brand colour used for the gradient blobs. Read from `equality-tokens.json`; unknown values fall back to black. | `primary`, `secondary`, `green`, `red`, `yellow` | — | ✅ |
+| `placement` | Distribution of the gradient blobs. `full` covers the whole area; `top` anchors blobs near the top edge. | `full`, `top` | `full` | ❌ |
diff --git a/packages/demo/src/content/components/button.mdx b/packages/demo/src/content/components/button.mdx
index 2f531996..8cff569e 100644
--- a/packages/demo/src/content/components/button.mdx
+++ b/packages/demo/src/content/components/button.mdx
@@ -1,7 +1,7 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Button"
-description: "Button with sizes and variants"
+description: "Interactive element for triggering actions"
---
import React from "react";
diff --git a/packages/demo/src/content/components/card-content-header.mdx b/packages/demo/src/content/components/card-content-header.mdx
index 2e75a50b..55ef29ab 100644
--- a/packages/demo/src/content/components/card-content-header.mdx
+++ b/packages/demo/src/content/components/card-content-header.mdx
@@ -1,7 +1,7 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Card Content Header"
-description: "Card Content Header with sizes and variants"
+description: "Header row for card content with icon, heading, and action"
---
import {
@@ -11,6 +11,28 @@ import {
CardDescription,
} from "@eqtylab/equality";
+## Overview
+
+Card Content Header is a header row for the top of a card's content, pairing a circular icon with a heading and an optional "See All" action button. It is designed to sit inside [CardContent](card), giving grouped content a consistent title treatment.
+
+The component renders nothing unless a `heading` or an `onButtonClick` handler is provided. When `onButtonClick` is set, a tertiary "See All" button is shown and the layout adjusts to accommodate it.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { CardContentHeader } from "@eqtylab/equality";
+```
+
+Basic usage with the required `icon` and a heading:
+
+```tsx
+
+```
+
+The `icon` prop accepts either a [Lucide](https://lucide.dev/icons/) icon name or a React element, and is rendered inside a circular, elevated background.
+
## Variants
### Default
@@ -35,3 +57,28 @@ import {
Card with CardContentHeader
+
+### Usage
+
+```tsx
+
+
+ handleSeeAll()}
+ />
+ Card with CardContentHeader
+
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| --------------- | ----------------------------------------------------------------------------------------- | ------------------------ | ------- | -------- |
+| `icon` | Icon shown in the circular background. A Lucide icon name or a React element. | `string`, `ReactElement` | — | ✅ |
+| `heading` | Heading text rendered as an `h3`. | `string` | — | ❌ |
+| `onButtonClick` | Handler for the "See All" button. Providing it renders the button and adjusts the layout. | `() => void` | — | ❌ |
+
+> **Note:** The component renders `null` when neither `heading` nor `onButtonClick` is provided, since there would be nothing meaningful to show.
diff --git a/packages/demo/src/content/components/card.mdx b/packages/demo/src/content/components/card.mdx
index 10b1b4a2..ac4dfda9 100644
--- a/packages/demo/src/content/components/card.mdx
+++ b/packages/demo/src/content/components/card.mdx
@@ -1,7 +1,7 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Card"
-description: "Card with sizes and variants"
+description: "Compound card container with elevations and hover states"
---
import {
@@ -15,7 +15,38 @@ import {
ELEVATION,
} from "@eqtylab/equality";
-## Content only (most used)
+## Overview
+
+Card is a compound component for grouping related content into a surfaced container. It is built from a set of composable parts — `Card` (the wrapper) plus `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, and `CardFooter` — so you only include the sections you need. A content-only card is the most common usage.
+
+Cards sit on the elevation scale via the `elevation` prop (defaulting to `raised`). Passing an `onClick` handler makes the card clickable and adds a hover gradient.
+
+## Usage
+
+Import the parts you need:
+
+```tsx
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardFooter,
+} from "@eqtylab/equality";
+```
+
+A minimal, content-only card:
+
+```tsx
+
+ Card content
+
+```
+
+## Variants
+
+### Content only (most used)
{
@@ -31,7 +62,7 @@ import {
}
-## Header only
+### Header only
{
@@ -43,23 +74,7 @@ import {
}
-## Content only interactive (hover)
-
-{
-
- console.log("Card clicked")}>
-
-
-
Content-only Card
-
- This card only uses CardContent without header or footer.
-
-
-
-
-}
-
-## Basic card
+### Basic card
{
@@ -74,7 +89,7 @@ import {
}
-## With footer
+### With footer
{
@@ -99,7 +114,28 @@ import {
}
-## Interactive (hover)
+### Usage
+
+```tsx
+
+
+ Card with Footer
+ Includes footer actions
+
+
+ This is the main content area of the card.
+
+
+
+ Primary Action
+
+
+
+```
+
+### Interactive (hover)
+
+Passing an `onClick` handler makes the card clickable and adds a hover gradient. This works with any composition, including content-only cards.
{
@@ -114,8 +150,20 @@ import {
}
+#### Usage
+
+```tsx
+ handleSelect()}>
+ This card is clickable.
+
+```
+
+The hover gradient can be restyled with the `hoverGradientClassName` prop.
+
## Elevations
+Use the `elevation` prop with the `ELEVATION` constant to place the card on the elevation scale. `raised` is the default.
+
### Sunken
{
@@ -179,3 +227,36 @@ import {
}
+
+### Usage
+
+```tsx
+import { Card, CardContent, ELEVATION } from "@eqtylab/equality";
+
+
+ Sunken card
+ ;
+```
+
+## Slots
+
+Compose a card from the following parts. All are optional except that content generally lives inside `CardContent`.
+
+| Name | Description |
+| ----------------- | ------------------------------------------------------------------ |
+| `Card` | The outer container. Owns elevation, hover, and click behaviour. |
+| `CardHeader` | Top section, typically wrapping `CardTitle` and `CardDescription`. |
+| `CardTitle` | The card's heading text. |
+| `CardDescription` | Secondary, muted supporting text shown beneath the title. |
+| `CardContent` | The main body area of the card. |
+| `CardFooter` | Bottom section, typically for actions such as buttons. |
+
+## Props
+
+These props apply to the top-level `Card`. The subcomponents accept standard `div` attributes (including `className`).
+
+| Name | Description | Type | Default | Required |
+| ------------------------ | ------------------------------------------------------------------------------------------------------ | ------------------------------------- | -------- | -------- |
+| `elevation` | Position on the elevation scale. Use the `ELEVATION` constant. | `sunken`, `base`, `raised`, `overlay` | `raised` | ❌ |
+| `onClick` | Click handler. Providing it makes the card interactive and enables the hover gradient automatically. | `(e: MouseEvent) => void` | — | ❌ |
+| `hoverGradientClassName` | Additional classes applied to the hover gradient element (only rendered when the card is interactive). | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/checkbox.mdx b/packages/demo/src/content/components/checkbox.mdx
index ee8b04bd..50464cb9 100644
--- a/packages/demo/src/content/components/checkbox.mdx
+++ b/packages/demo/src/content/components/checkbox.mdx
@@ -1,10 +1,39 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Checkbox"
-description: "Checkbox with sizes and variants"
+description: "Toggle control for boolean or multi-select choices"
---
import { Checkbox } from "@eqtylab/equality";
+import { CheckboxCustomIconDemo } from "@demo/components/demo/checkbox";
+
+## Overview
+
+Checkbox is a toggle control for boolean or multi-select choices, such as accepting terms or selecting rows in a table. It is built on [Radix UI Checkbox](https://www.radix-ui.com/primitives/docs/components/checkbox), so it is keyboard accessible and works with assistive technology out of the box.
+
+The checkbox can be controlled or uncontrolled, comes in two sizes, and supports a custom indicator icon. Pair it with a [Label](label) (using a shared `id`/`htmlFor`) to give it an accessible, clickable caption.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Checkbox } from "@eqtylab/equality";
+```
+
+Uncontrolled, with a default state:
+
+```tsx
+
+```
+
+Controlled, driving state yourself:
+
+```tsx
+const [checked, setChecked] = useState(false);
+
+ ;
+```
## States
@@ -26,6 +55,8 @@ import { Checkbox } from "@eqtylab/equality";
## Size Variants
+The checkbox comes in two sizes. `md` is the default; use `sm` in denser layouts.
+
### Medium (default)
@@ -33,3 +64,37 @@ import { Checkbox } from "@eqtylab/equality";
### Small
+
+### Usage
+
+```tsx
+
+
+```
+
+## Custom Icon
+
+Use the `icon` prop to replace the default check mark with any icon component, for example to represent an indeterminate state.
+
+
+
+### Usage
+
+```tsx
+import { Minus } from "lucide-react";
+
+ ;
+```
+
+## Props
+
+Checkbox extends the [Radix Checkbox Root](https://www.radix-ui.com/primitives/docs/components/checkbox#root) props (including `onCheckedChange`, `defaultChecked`, `required`, `name`, and `value`). The most commonly used are listed below.
+
+| Name | Description | Type | Default | Required |
+| ----------------- | ----------------------------------------------------- | ----------------------------------------------- | ------- | -------- |
+| `checked` | The controlled checked state. | `boolean`, `"indeterminate"` | — | ❌ |
+| `onCheckedChange` | Callback fired when the checked state changes. | `(checked: boolean \| "indeterminate") => void` | — | ❌ |
+| `defaultChecked` | The initial checked state when uncontrolled. | `boolean` | — | ❌ |
+| `disabled` | Prevents interaction and visually mutes the checkbox. | `boolean` | `false` | ❌ |
+| `size` | The size of the checkbox. | `sm`, `md` | `md` | ❌ |
+| `icon` | Custom indicator icon component shown when checked. | `React.ElementType` | — | ❌ |
diff --git a/packages/demo/src/content/components/code-block.mdx b/packages/demo/src/content/components/code-block.mdx
index 06b80400..b6a528e4 100644
--- a/packages/demo/src/content/components/code-block.mdx
+++ b/packages/demo/src/content/components/code-block.mdx
@@ -1,15 +1,13 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Code Block"
-description: "Code Block with sizes and variants"
+description: "Syntax-highlighted code display with status variants"
---
import { CodeBlock } from "@eqtylab/equality";
## Overview
----
-
The Code Block component comes with syntax highlighting and four color variants to visually communicate different statuses or contexts:
- **Neutral:** The default style, suitable for general blocks of code.
@@ -20,8 +18,6 @@ The Code Block component comes with syntax highlighting and four color variants
## Usage
----
-
Import the component:
```tsx
@@ -48,8 +44,6 @@ console.log('Hello, world!');
## Code Language Support
----
-
The Code Block component uses refractor and Prism for syntax highlighting. Most languages are supported - those that aren't will fallback to general `text` syntax highlighting.
See [Refractor's readme](https://github.com/wooorm/refractor/blob/main/readme.md#data) for the full list of languages supported by this package (all languages in the list are supported - this package uses `refractor/all`).
@@ -64,8 +58,6 @@ For example:
## Color Variants
----
-
Code blocks support color variants using either the brand primary color or status options. Available variants are `neutral`, `primary` (brand primary color), `success`, `danger`, and `warning`. Use these to visually distinguish different types of code or contexts according to your design needs.
### Primary (default)
@@ -178,8 +170,6 @@ client:only="react"
## Title
----
-
The Code Block component supports arbitrary titles:
Enter move through and select items.
+
+It is a compound component: compose a `Command` from an input, a list, groups, and items. Use it inline (as an embedded searchable list) or wrap it in `CommandDialog` to present it as a modal command palette.
+
+## Usage
+
+Import the parts you need:
+
+```tsx
+import {
+ Command,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+} from "@eqtylab/equality";
+```
+
+A basic inline command list:
+
+```tsx
+
+
+ No repositories found.
+
+ handleSelect("option-1")}>
+ Option 1
+
+ handleSelect("option-2")}>
+ Option 2
+
+
+
+```
+
## Default
+
+## As a Dialog
+
+Wrap the command list in `CommandDialog` to present it as a modal command palette — commonly opened with a keyboard shortcut such as ⌘K . `CommandDialog` accepts the same `open`/`onOpenChange` props as [Dialog](dialog), plus an optional `title` and `description` (visually part of the dialog header and read by screen readers).
+
+```tsx
+import {
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+} from "@eqtylab/equality";
+
+const [open, setOpen] = useState(false);
+
+
+
+
+ No results found.
+
+ runAction()}>Run action
+
+
+ ;
+```
+
+## Slots
+
+Compose a command interface from the following parts:
+
+| Name | Description |
+| ------------------ | ------------------------------------------------------------------------------- |
+| `Command` | The root container that owns filtering and keyboard navigation. |
+| `CommandDialog` | Wraps `Command` in a modal dialog for a command-palette experience. |
+| `CommandInput` | The search input; typing filters the list. Renders a leading search icon. |
+| `CommandList` | Scrollable container for the items. |
+| `CommandEmpty` | Content shown when no items match the current search. |
+| `CommandGroup` | Groups related items under an optional `heading`. |
+| `CommandItem` | A selectable row. Use `value` for filtering and `onSelect` to handle selection. |
+| `CommandSeparator` | A divider line between groups or items. |
+| `CommandShortcut` | Right-aligned text for showing an item's keyboard shortcut. |
+
+## Props
+
+Each part forwards its underlying `cmdk` props (and `className`). The most commonly used are listed below.
+
+| Name | Applies to | Description | Type | Default |
+| -------------- | --------------- | ------------------------------------------------------- | ------------------------- | ---------------------------------- |
+| `value` | `CommandItem` | The value used for filtering and passed to `onSelect`. | `string` | — |
+| `onSelect` | `CommandItem` | Called when the item is selected via click or keyboard. | `(value: string) => void` | — |
+| `disabled` | `CommandItem` | Prevents the item from being selected. | `boolean` | `false` |
+| `heading` | `CommandGroup` | Optional label rendered above the group's items. | `string` | — |
+| `placeholder` | `CommandInput` | Placeholder text for the search input. | `string` | — |
+| `open` | `CommandDialog` | Controls whether the dialog is open. | `boolean` | — |
+| `onOpenChange` | `CommandDialog` | Called when the dialog requests to open or close. | `(open: boolean) => void` | — |
+| `title` | `CommandDialog` | Accessible dialog title shown in the header. | `string` | `"Command Palette"` |
+| `description` | `CommandDialog` | Accessible dialog description shown in the header. | `string` | `"Search for a command to run..."` |
diff --git a/packages/demo/src/content/components/control-status-badge.mdx b/packages/demo/src/content/components/control-status-badge.mdx
index a31a2034..a7c2a083 100644
--- a/packages/demo/src/content/components/control-status-badge.mdx
+++ b/packages/demo/src/content/components/control-status-badge.mdx
@@ -8,14 +8,10 @@ import { ControlStatusBadge, PanelLabel } from "@eqtylab/equality";
## Overview
----
-
`ControlStatusBadge` is a specialized badge wrapper that provides predefined status configurations for controls. It automatically applies appropriate icons, colors, and labels based on the status value.
## Usage
----
-
Import the component from the Equality UI library:
```tsx
@@ -30,8 +26,6 @@ Basic usage requires only a `status` prop:
## Variants
----
-
@@ -76,8 +70,6 @@ Basic usage requires only a `status` prop:
## Display Options
----
-
The component supports three display modes:
### With Icon and Label (Default)
@@ -106,8 +98,6 @@ Show only the text by setting `display` to `"text-only"`:
## Props
----
-
| Prop | Type | Default | Description |
| --------- | ------------------------------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `status` | `string` | **Required** | The status value that determines the badge variant, icon, and label. |
diff --git a/packages/demo/src/content/components/copy-button.mdx b/packages/demo/src/content/components/copy-button.mdx
index 922d10f5..a204a02e 100644
--- a/packages/demo/src/content/components/copy-button.mdx
+++ b/packages/demo/src/content/components/copy-button.mdx
@@ -1,15 +1,35 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Copy Button"
-description: "Copy Button with sizes and variants"
+description: "Button that copies a value to the clipboard"
---
import { CopyButton } from "@eqtylab/equality";
import { CopyButtonDemo } from "@demo/components/demo/copy-button";
+## Overview
+
+Copy Button is an [IconButton](icon-button) that copies a string to the clipboard when clicked. On success it briefly swaps to a check icon for two seconds before reverting, giving users clear confirmation. It uses the modern Clipboard API where available and falls back to a legacy method for non-secure contexts.
+
+Use it wherever a user might want to copy a value — API keys, hashes, IDs, code snippets, or share links.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { CopyButton } from "@eqtylab/equality";
+```
+
+Basic usage with the required `value`:
+
+```tsx
+
+```
+
## Size Variants
-The `CopyButton` component supports three size variants: small (`"sm"`), medium (`"md"`), and large (`"lg"`). By adjusting the `size` prop, you can easily change the button's dimensions to fit a variety of interfaces and design needs. The default size is `"sm"`, making it compact, while `"md"` and `"lg"` provide progressively larger clickable areas.
+The `CopyButton` supports three sizes: `sm` (default), `md`, and `lg`. Adjust the `size` prop to fit a variety of interfaces — `sm` is compact, while `md` and `lg` provide progressively larger clickable areas.
### Small (default)
@@ -34,22 +54,42 @@ The `CopyButton` component supports three size variants: small (`"sm"`), medium
client:only="react"
/>
-## Props
+### Usage
-### `onClick` handler
+```tsx
+
+
+
+```
+
+## Custom Label
+
+By default the button's accessible label (and tooltip) is "Copy to clipboard". Use the `label` prop to describe what is being copied, which is announced to screen readers. On a successful copy the label temporarily becomes "Copied!".
-The `CopyButton` component exposes an `onClick` prop, which allows you to provide a custom function that will be called whenever the button is clicked. This makes the component highly composable, enabling you to trigger additional logic—such as showing a toast notification, logging analytics, or updating application state—alongside the built-in copy-to-clipboard behavior.
+```tsx
+
+```
+
+## onClick Handler
-For example, you can use the `onClick` handler to display a custom message:
+The `onClick` prop runs a custom function **after** the value has been copied, letting you trigger additional logic such as a toast, analytics event, or state update.
-```jsx
+
+
+### Usage
+
+```tsx
alert("Button clicked! Text copied!")}
- client:only="react"
+ onClick={() => showToast("Copied to clipboard")}
/>
```
-The `onClick` function is called after text has been copied to the clipboard.
+## Props
-
+| Name | Description | Type | Default | Required |
+| --------- | -------------------------------------------------------------------------- | ---------------- | --------------------- | -------- |
+| `value` | The string copied to the clipboard when the button is clicked. | `string` | — | ✅ |
+| `label` | Accessible label and tooltip text. Becomes "Copied!" briefly after a copy. | `string` | `"Copy to clipboard"` | ❌ |
+| `size` | The size of the button. | `sm`, `md`, `lg` | `sm` | ❌ |
+| `onClick` | Callback invoked after the value has been copied. | `() => void` | — | ❌ |
diff --git a/packages/demo/src/content/components/date-range-picker.mdx b/packages/demo/src/content/components/date-range-picker.mdx
index d6160f4e..515b12d8 100644
--- a/packages/demo/src/content/components/date-range-picker.mdx
+++ b/packages/demo/src/content/components/date-range-picker.mdx
@@ -1,11 +1,47 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Date Range Picker"
-description: "Date Range Picker with sizes and variants"
+description: "Input for selecting a start and end date"
---
import { DateRangePickerDemo } from "@demo/components/demo/date-range-picker";
+## Overview
+
+Date Range Picker lets users choose a start and end date from a calendar. It renders as a [Button](button) showing the current range, which opens a [Popover](popover) containing a month calendar with navigation.
+
+Selection is two-step: the first click sets the start date, the second sets the end date. If the second date falls before the first, the two are swapped automatically so the range always reads chronologically. Once a range is set, a clear (✕) button appears in the trigger to reset it.
+
+The component is fully controlled — you own the `dateRange` state and update it from the `onSelect` callback.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { DateRangePicker } from "@eqtylab/equality";
+```
+
+Hold the range in state and pass it back through `onSelect`:
+
+```tsx
+import { useState } from "react";
+
+const [dateRange, setDateRange] = useState<{ from?: Date; to?: Date }>({});
+
+ ;
+```
+
+Both `from` and `to` are optional `Date` objects. `from` is normalized to the start of the day (`00:00:00`) and `to` to the end of the day (`23:59:59`), so a completed range covers both selected days in full.
+
## Default
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | ------------------------------------------------------------------------------------- | --------------------------------------------- | ------- | -------- |
+| `dateRange` | The currently selected range. `{ from, to }` — both optional `Date` values. | `{ from?: Date; to?: Date }` | — | ✅ |
+| `onSelect` | Called with the updated range whenever the user picks a date or clears the selection. | `(range: { from?: Date; to?: Date }) => void` | — | ✅ |
+| `className` | Additional CSS classes applied to the trigger button. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/display-field.mdx b/packages/demo/src/content/components/display-field.mdx
index eaaadac1..2096b415 100644
--- a/packages/demo/src/content/components/display-field.mdx
+++ b/packages/demo/src/content/components/display-field.mdx
@@ -1,7 +1,7 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Display Field"
-description: "Display Field with sizes and variants"
+description: "Read-only field showing a value with copy action"
---
import {
@@ -11,52 +11,127 @@ import {
} from "@demo/components/demo/display-field";
import { ELEVATION } from "@eqtylab/equality";
-## Neutral
+## Overview
+
+Display Field presents a read-only value — such as an ID, hash, key, or DID — in a bordered, surfaced container. By default it includes a [CopyButton](copy-button) so users can copy the value in one click. It supports an optional labelled prefix, status variants with matching icons, truncation strategies for long values, custom action buttons, and an expandable slot for supplementary content.
+
+It sits on the elevation scale via the `elevation` prop (defaulting to `base`), so it can be nested inside cards and other surfaces without clashing.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { DisplayField } from "@eqtylab/equality";
+```
+
+The value is passed as `children`:
+
+```tsx
+zQ3shrGxRrYyWixJGrr45jJ1MEY76YQZ4KVbt9CYRsTWZ5MWV
+```
+
+The copy button uses the string children as its value, so copy only works when `children` is a plain string.
+
+## Variants
+
+### Neutral
-## Neutral Truncated
+### Neutral with Prefix
-
+
-## Neutral Middle Truncated
+### Neutral Check with Prefix
-
+`neutralCheck` shows a check icon like `success`, but keeps the neutral colouring.
-## Success with Prefix
+
+
+### Success with Prefix
+
+The `success` and `failure` variants show a status icon alongside the `prefix` (a check and a warning triangle respectively).
-## Failure with Prefix
+### Failure with Prefix
-## Neutral with Prefix
+## Truncation
-
+Long values can be shortened with the `truncate` prop. `true` truncates at the end with an ellipsis, while `"middle"` keeps the start and end visible (useful for hashes and keys where both ends matter). Left as `false`, the value scrolls horizontally.
-## Without Copy Button
+### End Truncated
+
+
+
+
+
+### Middle Truncated
+
+
+
+
+
+### Horizontall Scroll
+
+
+
+
+
+## Actions
+
+The copy button is shown by default. Set `copy={false}` to hide it, and pass `actions` to render your own buttons before the copy button.
+
+### Without Copy Button
-## With Additional Action
+### With Additional Action
-## Copy Disabled with Custom Actions
+### Copy Disabled with Custom Actions
-## With Slot Content
-
-
+### Usage
+
+```tsx
+
+
+
+ >
+ }
+>
+ zQ3shrGxRrYyWixJGrr45jJ1MEY76YQZ4KVbt9CYRsTWZ5MWV
+
+```
+
+## Slot
+
+Use the `slot` prop to render supplementary content in a separate section beneath the value — for example related metadata or an empty-state message.
+
+
## Elevations
+Use the `elevation` prop with the `ELEVATION` constant to place the field on the elevation scale. `base` is the default. These examples are nested in a [Card](card) to show the effect.
+
### Sunken
+
+## Slots
+
+| Name | Description |
+| ---------- | ------------------------------------------------------------------ |
+| `children` | The value displayed in the field. Use a string for copy support. |
+| `actions` | Custom action nodes rendered before the copy button. |
+| `slot` | Supplementary content shown in a separate section below the value. |
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------- | --------- | -------- |
+| `prefix` | Label shown before the value, alongside the variant status icon. | `string` | — | ❌ |
+| `variant` | Status style. `success`/`failure` add an icon and colour; `neutralCheck` adds a plain check. | `neutral`, `neutralCheck`, `success`, `failure` | `neutral` | ❌ |
+| `truncate` | How to shorten long values. `true` truncates the end; `middle` keeps both ends; `false` scrolls. | `true`, `false`, `"middle"` | `false` | ❌ |
+| `copy` | Whether to show the built-in copy button. | `boolean` | `true` | ❌ |
+| `elevation` | Position on the elevation scale. Use the `ELEVATION` constant. | `sunken`, `base`, `raised`, `overlay` | `base` | ❌ |
diff --git a/packages/demo/src/content/components/drawer.mdx b/packages/demo/src/content/components/drawer.mdx
deleted file mode 100644
index aff7ffa8..00000000
--- a/packages/demo/src/content/components/drawer.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: "@demo/layouts/mdx-layout.astro"
-heading: "Drawer"
-description: "Drawer with sizes and variants"
----
-
-import { DrawerDemo } from "@demo/components/demo/drawer";
-
-## Default
-
-
diff --git a/packages/demo/src/content/components/empty-table-state.mdx b/packages/demo/src/content/components/empty-table-state.mdx
index c562003e..4e8264eb 100644
--- a/packages/demo/src/content/components/empty-table-state.mdx
+++ b/packages/demo/src/content/components/empty-table-state.mdx
@@ -9,16 +9,12 @@ import React from "react";
## Overview
----
-
`EmptyTableState` provides a consistent placeholder for tables, lists, or other collections that have no content to show. It pairs an [`Icon`](/components/icon) with a title and optional description, and can surface a "Clear Filters" action when an empty result is caused by active filters.
It is commonly rendered inside a [`Table`](/components/table) empty state cell, but works anywhere an empty collection needs to be communicated.
## Usage
----
-
Import the component:
```tsx
@@ -33,8 +29,6 @@ Basic usage with required properties:
## Variants
----
-
### Default
+
+;
+```
+
+`FormControl` wraps a single form control and forwards the accessibility attributes to it via a [Radix Slot](https://www.radix-ui.com/primitives/docs/utilities/slot), so the child must accept a `ref` and standard input props. `FormMessage` renders the field's validation error automatically — you can also pass custom children, and it renders nothing when there is no message.
+
## Examples
### Default Form
@@ -14,8 +69,75 @@ import { FormDemo } from "@demo/components/demo/form";
### Form with Validation Errors
+Validation rules are declared on `FormField` via `rules`. When validation fails, `FormMessage` displays the error and the label switches to its error style.
+
### Form with Description
+`FormDescription` renders helper text that is linked to the control through `aria-describedby`.
+
+
+## Custom Field Parts
+
+The built-in parts cover most needs, but you can build your own with the exported `useFormField()` hook. Called inside a `FormField`, it returns the current field's linking `id`s and its react-hook-form state (`error`, `isDirty`, `isTouched`, and so on), so a custom part can react to validation without prop-drilling.
+
+For example, a status icon that appears once the field has been edited:
+
+```tsx
+import { useFormField } from "@eqtylab/equality";
+import { Check, TriangleAlert } from "lucide-react";
+
+function FieldStatus() {
+ const { error, isDirty } = useFormField();
+
+ if (!isDirty) return null;
+
+ return error ? (
+
+ ) : (
+
+ );
+}
+```
+
+Drop it inside a `FormItem`, alongside the other parts:
+
+```tsx
+
+ Email
+
+
+
+
+
+
+```
+
+`useFormField()` only works inside a `FormField` (it reads the field via context) and throws otherwise.
+
+## Slots
+
+Compose a form from the following parts:
+
+| Name | Description |
+| ----------------- | ----------------------------------------------------------------------------------------------------- |
+| `Form` | Provider that shares the `useForm` instance with the fields. Spread your `form` object onto it. |
+| `FormField` | Connects a named field to react-hook-form. Wraps a `Controller`; use its `render` prop for the field. |
+| `FormItem` | Groups a single field's parts and generates the linking `id`s. |
+| `FormLabel` | Label for the control. Automatically targets the control and reflects error state. |
+| `FormControl` | Wraps the actual input, forwarding `id`, `aria-describedby`, and `aria-invalid`. |
+| `FormDescription` | Helper text linked to the control via `aria-describedby`. |
+| `FormMessage` | Displays the field's validation error (or custom children); renders nothing when empty. |
+
+## Props
+
+`Form` receives the object returned by `useForm`. `FormField` forwards all [react-hook-form `Controller`](https://react-hook-form.com/docs/usecontroller/controller) props; the most commonly used are listed below. The remaining parts accept standard element attributes plus `className`.
+
+| Name | Applies to | Description | Type | Required |
+| --------- | ----------- | ------------------------------------------------------------------------- | ----------------------------------------- | -------- |
+| `control` | `FormField` | The `control` object from `useForm`. | `Control` | ✅ |
+| `name` | `FormField` | The field name; must match a key in your form values. | `string` | ✅ |
+| `render` | `FormField` | Render function receiving `field` (and `fieldState`) to render the input. | `({ field, fieldState }) => ReactElement` | ✅ |
+| `rules` | `FormField` | Validation rules applied to the field. | `RegisterOptions` | ❌ |
diff --git a/packages/demo/src/content/components/heading.mdx b/packages/demo/src/content/components/heading.mdx
index 99a1a541..007885be 100644
--- a/packages/demo/src/content/components/heading.mdx
+++ b/packages/demo/src/content/components/heading.mdx
@@ -1,17 +1,66 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Heading"
-description: "Heading with sizes and variants"
+description: "Semantic heading with configurable visual level"
---
import { HeadingDemo } from "@demo/components/demo/heading";
-## HTML headings
+## Overview
+
+Heading renders a semantic HTML heading (`h1`–`h6`) with the design system's typographic styling. It separates _meaning_ from _appearance_: the `as` prop sets the actual heading tag (for document structure and accessibility), while the optional `displayAs` prop lets you borrow the visual style of a different level without changing the underlying element.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Heading } from "@eqtylab/equality";
+```
+
+Basic usage — `as` is required and determines both the tag and its style:
+
+```tsx
+Page title
+```
+
+## HTML Headings
+
+Each level has its own type styling. Set `as` to the appropriate level for its place in the document outline.
+### Usage
+
+```tsx
+This is an h1 heading
+This is an h2 heading
+This is an h3 heading
+This is an h4 heading
+This is an h5 heading
+This is an h6 heading
+```
+
## Display As
-Use `displayAs` to render a different semantic level while maintaining visual styling:
+Use `displayAs` to render one semantic level while applying the visual style of another. Here an `h3` is rendered with `h1` styling — the element stays an `
); })()}
-## With Suffix Icon
+### With Suffix Icon
{(() => {
const eyeIcon = React.createElement(Eye);
@@ -101,7 +149,26 @@ return (
type="text"
placeholder="Verified input"
suffix={React.createElement(Check)}
- value="john@example.com"
+ defaultValue="john@example.com"
/>
); })()}
+
+### Usage
+
+```tsx
+import { Search, Eye } from "lucide-react";
+
+ } />
+ } />
+```
+
+## Props
+
+Input accepts all standard ` ` attributes (`type`, `placeholder`, `value`, `defaultValue`, `onChange`, `disabled`, `min`, `max`, etc.) in addition to the props below.
+
+| Name | Description | Type | Default | Required |
+| -------- | --------------------------------------------------- | ----------- | ------- | -------- |
+| `prefix` | Content rendered inside the field, before the text. | `ReactNode` | — | ❌ |
+| `suffix` | Content rendered inside the field, after the text. | `ReactNode` | — | ❌ |
+| `type` | The native input type. | `string` | `text` | ❌ |
diff --git a/packages/demo/src/content/components/label.mdx b/packages/demo/src/content/components/label.mdx
index a69fcfe1..3cab8f46 100644
--- a/packages/demo/src/content/components/label.mdx
+++ b/packages/demo/src/content/components/label.mdx
@@ -1,11 +1,45 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Label"
-description: "Label with sizes and variants"
+description: "Text label for a form control"
---
import { LabelDemo } from "@demo/components/demo/label";
+## Overview
+
+Label provides an accessible caption for a form control. It is built on [Radix UI Label](https://www.radix-ui.com/primitives/docs/components/label), which associates the label with its control so that clicking the label focuses the input and screen readers announce the caption when the control is focused.
+
+Associate a label with a control in one of two ways: give the label an `htmlFor` prop matching the control's `id`, or nest the control inside the label. For fields built with [Form](form), use `FormLabel` instead — it wires up this association automatically.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Label } from "@eqtylab/equality";
+```
+
+Link the label to a control via `htmlFor` and a matching `id`:
+
+```tsx
+import { Label, Input } from "@eqtylab/equality";
+
+
+ Email
+
+
;
+```
+
## Default
+
+## Props
+
+Label accepts all standard `` attributes (including `htmlFor`) plus `className`.
+
+| Name | Description | Type | Default | Required |
+| ---------- | ------------------------------------------------------------ | ----------- | ------- | -------- |
+| `htmlFor` | The `id` of the control this label describes. Links the two. | `string` | — | ❌ |
+| `children` | The label text. | `ReactNode` | — | ✅ |
diff --git a/packages/demo/src/content/components/loading-overlay.mdx b/packages/demo/src/content/components/loading-overlay.mdx
index 812a048e..4c552010 100644
--- a/packages/demo/src/content/components/loading-overlay.mdx
+++ b/packages/demo/src/content/components/loading-overlay.mdx
@@ -1,11 +1,56 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Loading Overlay"
-description: "Loading Overlay with sizes and variants"
+description: "Overlay indicating content is loading"
---
import { LoadingOverlayDemo } from "@demo/components/demo/loading-overlay";
+## Overview
+
+Loading Overlay covers the interface with a dimmed backdrop and a centered [Spinner](spinner) and message, signalling that a blocking operation is in progress. Use it for actions the user should wait on — saving a form, running a process, loading a view — where interacting with the underlying content should be prevented until it completes.
+
+Its visibility is fully controlled through the `isVisible` prop: when `false`, the component renders nothing. For skeleton-style, non-blocking loading of individual elements, use [Skeleton](skeleton) instead.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { LoadingOverlay } from "@eqtylab/equality";
+```
+
+Toggle it with state around an async action:
+
+```tsx
+const [isVisible, setIsVisible] = useState(false);
+
+const handleSave = async () => {
+ setIsVisible(true);
+ await save();
+ setIsVisible(false);
+};
+
+ ;
+```
+
## Default
+Click the button to show the overlay for a few seconds.
+
+
+## Custom Message
+
+The `message` prop replaces the default "Loading..." text.
+
+```tsx
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | ---------------------------------------------------------------- | --------- | -------------- | -------- |
+| `isVisible` | Whether the overlay is shown. When `false`, nothing is rendered. | `boolean` | — | ✅ |
+| `message` | Text displayed beneath the spinner. | `string` | `"Loading..."` | ❌ |
diff --git a/packages/demo/src/content/components/metric-card.mdx b/packages/demo/src/content/components/metric-card.mdx
index 364a3044..6e2364a6 100644
--- a/packages/demo/src/content/components/metric-card.mdx
+++ b/packages/demo/src/content/components/metric-card.mdx
@@ -1,10 +1,10 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Metric Card"
-description: "Metric Card with sizes and variants"
+description: "Card surfacing a numeric metric with label and icon"
---
-import { MetricCard } from "@eqtylab/equality";
+import { MetricCard, ELEVATION } from "@eqtylab/equality";
## Overview
@@ -68,12 +68,65 @@ The `icon` prop is optional. Use it only when a descriptive icon makes sense to
```
+## Elevations
+
+Use the `elevation` prop with the `ELEVATION` constant to place the card on the elevation scale. `raised` is the default.
+
+### Sunken
+
+
+
+### Base
+
+
+
+### Raised (default)
+
+
+
+### Overlay
+
+
+
+### Usage
+
+```tsx
+import { MetricCard, ELEVATION } from "@eqtylab/equality";
+
+ ;
+```
+
## Props
| Name | Description | Type | Default | Required |
| ----------- | ----------------------------------------------------------------------- | ---------------------------------------------------- | --------- | -------- |
-| `value` | The numeric or text value to display prominently on the card. | `string` \| `number` | - | ✅ |
-| `label` | Descriptive label shown beneath the value. | `string` | - | ✅ |
-| `icon` | Icon to display. Can be a Lucide icon name or a custom React element. | `string` \| `ReactElement` | - | ❌ |
+| `value` | The numeric or text value to display prominently on the card. | `string`, `number` | — | ✅ |
+| `label` | Descriptive label shown beneath the value. | `string` | — | ✅ |
+| `icon` | Icon to display. Can be a Lucide icon name or a custom React element. | `string`, `ReactElement` | — | ❌ |
| `variant` | The visual style variant that communicates the metric's status or tone. | `default`, `primary`, `danger`, `success`, `warning` | `default` | ❌ |
-| `elevation` | Elevation level of the card surface. | `sunken`, `base`, `raised`, `overlay` | `raised` | ❌ |
+| `elevation` | Position on the elevation scale. Use the `ELEVATION` constant. | `sunken`, `base`, `raised`, `overlay` | `raised` | ❌ |
diff --git a/packages/demo/src/content/components/motion-collapsible-content.mdx b/packages/demo/src/content/components/motion-collapsible-content.mdx
index c3947262..4f6d50be 100644
--- a/packages/demo/src/content/components/motion-collapsible-content.mdx
+++ b/packages/demo/src/content/components/motion-collapsible-content.mdx
@@ -1,10 +1,44 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Motion Collapsible Content"
+description: "Animated container that expands and collapses its content"
---
import { MotionCollapsibleContentDemo } from "@demo/components/demo/motion-collapsible-content";
+## Overview
+
+Motion Collapsible Content smoothly expands and collapses a region of content, animating both its height and opacity. It measures its children (and re-measures them as they change) so the open animation always transitions to the correct height, even for dynamic content.
+
+Its open state is fully controlled through the `isOpen` prop — pair it with your own trigger (such as a [Button](button)) that toggles the state. Use it for show/more panels, expandable rows, and inline disclosure sections. It is built on [Motion](https://motion.dev/), so the animation respects the transition timing defined by the component.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { MotionCollapsibleContent } from "@eqtylab/equality";
+```
+
+Drive `isOpen` from state and toggle it with a trigger:
+
+```tsx
+const [isOpen, setIsOpen] = useState(false);
+
+ setIsOpen((open) => !open)}>Toggle
+
+ Content revealed when open.
+ ;
+```
+
## Default
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | ----------------------------------------------------------------- | ----------- | ------- | -------- |
+| `isOpen` | Whether the content is expanded. Toggling it animates the change. | `boolean` | — | ✅ |
+| `children` | The content to reveal when open. | `ReactNode` | — | ✅ |
+| `className` | Additional CSS classes applied to the animated container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/page-not-found.mdx b/packages/demo/src/content/components/page-not-found.mdx
index e812ddc8..39bed4c2 100644
--- a/packages/demo/src/content/components/page-not-found.mdx
+++ b/packages/demo/src/content/components/page-not-found.mdx
@@ -1,10 +1,63 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Page Not Found"
+description: "Full-page 404 state with a return-home action"
---
import { NotFound } from "@eqtylab/equality";
+## Overview
+
+Page Not Found is a ready-made 404 state for routes and resources that don't exist. It presents an alert icon, a heading, a description, and — by default — a "Return Home" button, so you can drop it into a catch-all route with no configuration.
+
+Every piece of copy and the action are customisable: override the `title` and `description` if needed, and provide an `onHomeClick` handler to integrate with your router instead of the default full-page navigation to `/`.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { NotFound } from "@eqtylab/equality";
+```
+
+Basic usage — renders with sensible defaults:
+
+```tsx
+
+```
+
+By default the home button navigates to `/` with a full page load. Pass `onHomeClick` to hook into client-side routing instead:
+
+```tsx
+import { useNavigate } from "react-router";
+
+const navigate = useNavigate();
+
+ navigate("/")} />;
+```
+
## Default
+
+## Custom Content
+
+Override the `title` and `description`, and hide the button with `showHomeButton={false}` when a return action isn't appropriate.
+
+```tsx
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ---------------- | ----------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------- | -------- |
+| `title` | The heading text. | `string` | `"Oops! Page Not Found"` | ❌ |
+| `description` | The supporting text shown beneath the title. | `string` | `"The page or resource you're looking for doesn't exist or has been moved."` | ❌ |
+| `showHomeButton` | Whether to render the "Return Home" button. | `boolean` | `true` | ❌ |
+| `onHomeClick` | Handler for the home button. Defaults to navigating to `/`. | `() => void` | navigates to `/` | ❌ |
+| `className` | Additional CSS classes applied to the container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/pagination.mdx b/packages/demo/src/content/components/pagination.mdx
index db0932ba..12b0e859 100644
--- a/packages/demo/src/content/components/pagination.mdx
+++ b/packages/demo/src/content/components/pagination.mdx
@@ -1,10 +1,82 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Pagination"
+description: "Page navigation with items-per-page and result info"
---
import { PaginationDemo } from "@demo/components/demo/pagination";
+## Overview
+
+Pagination provides controls for moving through a paginated list: previous/next buttons, a windowed set of numbered page buttons, an optional "showing X to Y of Z" info line, and an optional items-per-page [Select](select). It is a controlled component — you own the current page and page size, and it calls back when the user changes them.
+
+It does **not** slice your data; it only renders the controls. Compute the current page's items yourself from `currentPage` and `itemsPerPage`, and pass the counts in. To keep the UI uncluttered when pagination isn't needed, the component renders nothing when `totalItems` is at or below the smallest items-per-page option.
+
+### totalItems vs. filteredItems
+
+Two counts are required because they serve different purposes:
+
+- **`totalItems`** — the full, unfiltered size of the dataset. Used only to decide whether to render the controls at all.
+- **`filteredItems`** — the number of items after any active filtering. Drives the page count, the info text, and the numbered buttons.
+
+When no filtering is applied, pass the same value for both.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Pagination } from "@eqtylab/equality";
+```
+
+Hold the page and page size in state, slice your data from them, and wire the callbacks:
+
+```tsx
+const [currentPage, setCurrentPage] = useState(1);
+const [itemsPerPage, setItemsPerPage] = useState(10);
+
+const pageItems = allItems.slice(
+ (currentPage - 1) * itemsPerPage,
+ currentPage * itemsPerPage,
+);
+
+ ;
+```
+
+Changing the page size resets to page 1 automatically.
+
## Default
+
+## Configuration
+
+- **Items-per-page dropdown** — only shown when you pass `onItemsPerPageChange`. Without it, the info line shows a static end count instead. Customise the choices with `itemsPerPageOptions`.
+- **Info line** — set `showInfo={false}` to hide the "showing X to Y of Z" text. Use `type` to append a noun (e.g. `type="items"` → "of 100 items").
+- **Windowing** — `maxVisiblePages` controls how many numbered buttons appear at once; the window slides to keep the current page centered.
+- **Scroll on change** — pass `scrollTargetRef` (a ref or an element `id` string) to smooth-scroll that element into view whenever the page changes.
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ---------------------- | ----------------------------------------------------------------------------------- | ---------------------------------- | ------------------- | -------- |
+| `currentPage` | The active page (1-based). | `number` | — | ✅ |
+| `totalItems` | Full dataset size. Controls whether the component renders. | `number` | — | ✅ |
+| `filteredItems` | Item count after filtering. Drives page count and info text. | `number` | — | ✅ |
+| `itemsPerPage` | Number of items shown per page. | `number` | — | ✅ |
+| `onPageChange` | Called with the new page when the user navigates. | `(page: number) => void` | — | ✅ |
+| `onItemsPerPageChange` | Called with the new page size. When provided, the items-per-page dropdown is shown. | `(itemsPerPage: number) => void` | — | ❌ |
+| `itemsPerPageOptions` | Choices offered in the items-per-page dropdown. | `number[]` | `[10, 25, 50, 100]` | ❌ |
+| `showInfo` | Whether to show the "showing X to Y of Z" info line. | `boolean` | `true` | ❌ |
+| `maxVisiblePages` | Maximum number of numbered page buttons shown at once. | `number` | `5` | ❌ |
+| `type` | Noun appended to the info line (e.g. `"items"`). | `string` | `""` | ❌ |
+| `scrollTargetRef` | Element (ref or `id` string) scrolled into view on page change. | `RefObject`, `string` | — | ❌ |
+| `className` | Additional CSS classes applied to the container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/panel-label.mdx b/packages/demo/src/content/components/panel-label.mdx
index 624e13b4..12505c34 100644
--- a/packages/demo/src/content/components/panel-label.mdx
+++ b/packages/demo/src/content/components/panel-label.mdx
@@ -1,11 +1,36 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Panel Label"
-description: "Panel Label with sizes and variants"
+description: "Small heading label for a panel section"
---
import { PanelLabel } from "@eqtylab/equality";
+## Overview
+
+Panel Label is a small, understated heading for labelling a section of a panel, sidebar, or column of controls. It gives grouped content a consistent caption without the weight of a full [Heading](heading) — use it for column titles, filter group labels, and other minor section markers.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { PanelLabel } from "@eqtylab/equality";
+```
+
+Basic usage — `label` is required:
+
+```tsx
+
+```
+
## Default
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ----------- | -------------------------------------------- | -------- | ------- | -------- |
+| `label` | The text shown as the label. | `string` | — | ✅ |
+| `className` | Additional CSS classes applied to the label. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/popover.mdx b/packages/demo/src/content/components/popover.mdx
index c073bf06..4936de1b 100644
--- a/packages/demo/src/content/components/popover.mdx
+++ b/packages/demo/src/content/components/popover.mdx
@@ -1,19 +1,93 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Popover"
-description: "Popover with sizes and variants"
+description: "Floating content anchored to a trigger element"
---
import { PopoverDemo } from "@demo/components/demo/popover";
+## Overview
+
+Popover displays floating content anchored to a trigger, opening on click and closing on outside click or Esc . It's built on [Radix UI Popover](https://www.radix-ui.com/primitives/docs/components/popover), so it handles positioning, focus management, and keyboard interaction, and manages its own open state (it can also be controlled).
+
+Use it for rich, interactive overlays — filters, pickers, small forms, or menus of actions. For simple hover hints use a [Tooltip](tooltip); for a modal use a [Dialog](dialog). The content is rendered in a portal, so it escapes overflow clipping from ancestors.
+
+## Usage
+
+Import the parts:
+
+```tsx
+import { Popover, PopoverTrigger, PopoverContent } from "@eqtylab/equality";
+```
+
+Wrap a trigger and content in `Popover`. Use `asChild` on the trigger to render your own element (such as a [Button](button)) as the trigger:
+
+```tsx
+
+
+ Click me
+
+
+ This is a popover
+
+
+```
+
## Default
-## Align: start
+## Alignment
+
+Use the `align` prop on `PopoverContent` to align it against the trigger. `center` is the default; `start` and `end` align to the trigger's edges. Fine-tune the gap with `sideOffset`.
+
+### Align: start
-## Align: end
+### Align: center (default)
+
+
+
+### Align: end
+
+### Usage
+
+```tsx
+…
+…
+…
+```
+
+## Arrow
+
+Set `arrow` on `PopoverContent` to render a small pointer connecting the content to its trigger.
+
+
+
+```tsx
+
+ Popover with an arrow
+
+```
+
+## Slots
+
+| Name | Description |
+| ---------------- | ---------------------------------------------------------------------------- |
+| `Popover` | The root that manages open state. Wraps the trigger and content. |
+| `PopoverTrigger` | The element that toggles the popover. Use `asChild` to use your own element. |
+| `PopoverContent` | The floating panel. Owns alignment, offset, and the optional arrow. |
+
+## Props
+
+`Popover` and `PopoverTrigger` forward their [Radix](https://www.radix-ui.com/primitives/docs/components/popover) props (e.g. `open`, `onOpenChange`, `defaultOpen` on the root). The props most commonly set on `PopoverContent` are below.
+
+| Name | Applies to | Description | Type | Default |
+| ------------ | ---------------- | -------------------------------------------------------------- | ------------------------ | -------- |
+| `align` | `PopoverContent` | Alignment against the trigger. | `start`, `center`, `end` | `center` |
+| `sideOffset` | `PopoverContent` | Distance in pixels between the content and the trigger. | `number` | `4` |
+| `arrow` | `PopoverContent` | Renders a pointer arrow connecting the content to the trigger. | `boolean` | `false` |
+| `asChild` | `PopoverTrigger` | Render the trigger as the child element instead of a `button`. | `boolean` | `false` |
diff --git a/packages/demo/src/content/components/progress.mdx b/packages/demo/src/content/components/progress.mdx
index fdb70ec3..06bf1b51 100644
--- a/packages/demo/src/content/components/progress.mdx
+++ b/packages/demo/src/content/components/progress.mdx
@@ -1,28 +1,48 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Progress"
-description: "Progress with sizes and variants"
+description: "Horizontal progress bar with color variants"
---
import { Progress } from "@eqtylab/equality";
+## Overview
+
+Progress is a horizontal bar that communicates how far along a determinate task is — an upload, a multi-step flow, or a quota. It is built on [Radix UI Progress](https://www.radix-ui.com/primitives/docs/components/progress), so it exposes the correct progress roles and values to assistive technology.
+
+The fill is driven by the `value` prop (a percentage from 0 to 100), and its colour maps to a brand colour via the `color` prop. For an indeterminate or spinning state, use a [Spinner](spinner); for circular progress, see [Radial Graph](radial-graph).
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Progress } from "@eqtylab/equality";
+```
+
+Basic usage — pass a `value` between 0 and 100:
+
+```tsx
+
+```
+
## Percentage Examples
-### Value: 0
+### 0%
-### Value: 75%
+### 75%
-### Value: 100%
+### 100%
## Color Variants
-This component supports several color variants that correspond with the brand colors. You can use these variants — such as `primary`, `secondary`, `green`, `red`, and `gold` — to visually distinguish different types of progress bars or their context according to your design needs. Each color maps directly to a brand color in the Equality design system, allowing for consistent and meaningful styling in your application.
+The `color` prop maps the bar to a brand colour — `primary` (default), `secondary`, `green`, `red`, and `yellow` — so you can distinguish progress by context.
### Primary (default)
@@ -43,3 +63,22 @@ This component supports several color variants that correspond with the brand co
### Yellow
+
+### Usage
+
+```tsx
+
+
+
+
+
+```
+
+## Props
+
+Progress also accepts the underlying [Radix Progress Root](https://www.radix-ui.com/primitives/docs/components/progress) props (such as `max`) and `className`.
+
+| Name | Description | Type | Default | Required |
+| ------- | ----------------------------------------- | ------------------------------------------------ | --------- | -------- |
+| `value` | The completion percentage, from 0 to 100. | `number` | — | ❌ |
+| `color` | Brand colour of the progress fill. | `primary`, `secondary`, `green`, `red`, `yellow` | `primary` | ❌ |
diff --git a/packages/demo/src/content/components/radial-graph.mdx b/packages/demo/src/content/components/radial-graph.mdx
index 5f751cff..86369225 100644
--- a/packages/demo/src/content/components/radial-graph.mdx
+++ b/packages/demo/src/content/components/radial-graph.mdx
@@ -1,23 +1,64 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Radial Graph"
-description: "Radial Graph with sizes and variants"
+description: "Circular graph for progress or percentages"
---
import { RadialGraph } from "@eqtylab/equality";
-## Text variants
+## Overview
+
+Radial Graph shows a single percentage as a circular gauge, with a centered label and optional sub-label. It's built from radial bars that fill and animate to represent the value, making it well suited to compliance scores, completion rates, and other at-a-glance metrics.
+
+By default the label displays the rounded percentage, but you can override it with any text. The `variant` prop ties the colour to a status (success, danger, warning), so the value's meaning reads at a glance. For a simple linear bar, use [Progress](progress) instead.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { RadialGraph } from "@eqtylab/equality";
+```
+
+Basic usage — `percentage` is required:
+
+```tsx
+
+```
+
+## Labels
+
+The center label defaults to the rounded `percentage`. Add a `subLabel` for context, or set `displayLabel` to replace the percentage text entirely.
### Default
-### With sub label
+### With Sub Label
+### Custom Display Label
+
+
+
+### Usage
+
+```tsx
+
+
+
+```
+
## Graph Sizes
+Three sizes are available; `md` is the default.
+
### Small
@@ -30,18 +71,17 @@ import { RadialGraph } from "@eqtylab/equality";
-## Custom display label
+### Usage
-
+```tsx
+
+
+
+```
## Variants
-This component supports color variants using either the brand primary color or status options. Available variants are `primary` (brand primary color), `success`, `danger`, and `warning`. Use these to visually distinguish different types of radial graphs or their context according to your design needs.
+The `variant` prop sets the fill colour. Use `primary` (default) for a plain metric, or `success`/`danger`/`warning` to give the value meaning.
### Primary (default)
@@ -58,3 +98,22 @@ This component supports color variants using either the brand primary color or s
### Warning
+
+### Usage
+
+```tsx
+
+
+
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| -------------- | ----------------------------------------------------------------------- | ----------------------------------------- | ----------------- | -------- |
+| `percentage` | The value to display, from 0 to 100. Drives the fill and default label. | `number` | — | ✅ |
+| `displayLabel` | Text shown in the center, replacing the percentage. | `string` | `"{percentage}%"` | ❌ |
+| `subLabel` | Secondary text shown beneath the label. | `string` | — | ❌ |
+| `graphSize` | The size of the graph. | `sm`, `md`, `lg` | `md` | ❌ |
+| `variant` | The fill colour, communicating status or tone. | `primary`, `success`, `danger`, `warning` | `primary` | ❌ |
diff --git a/packages/demo/src/content/components/radio-dropdown.mdx b/packages/demo/src/content/components/radio-dropdown.mdx
index c220006f..81884491 100644
--- a/packages/demo/src/content/components/radio-dropdown.mdx
+++ b/packages/demo/src/content/components/radio-dropdown.mdx
@@ -1,11 +1,53 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Radio Dropdown"
-description: "Radio Dropdown with sizes and variants"
+description: "Dropdown for single-choice filtering with counts"
---
import { RadioDropdownDemo } from "@demo/components/demo/radio-dropdown";
+## Overview
+
+Radio Dropdown is a compact control for picking a single option from a list — typically a filter such as a status or category. It renders as a [Button](button) showing the current selection, opening a [Dropdown Menu](dropdown-menu) of radio options. Each option can carry an optional `count`, shown as a [Badge](badge) on the trigger and inline in the menu, which is handy for surfacing how many items match each filter.
+
+It is a controlled component: you provide the `options` and the `selectedValue`, and it calls `onSelect` when the user chooses a different one. Options with an empty `value` or `label` are filtered out automatically.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { RadioDropdown } from "@eqtylab/equality";
+```
+
+Hold the selected value in state and update it from `onSelect`:
+
+```tsx
+const [status, setStatus] = useState("active");
+
+ ;
+```
+
+The `label` is shown on the trigger before anything is selected, and as the heading inside the menu. Once an option is selected, its label replaces the trigger text.
+
## Default
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| --------------- | ------------------------------------------------------------------- | ---------------------------------------------------- | ------- | -------- |
+| `label` | Trigger text before selection, and the menu heading. | `string` | — | ✅ |
+| `options` | The selectable options. Each is `{ value, label, count? }`. | `{ value: string; label: string; count?: number }[]` | — | ✅ |
+| `selectedValue` | The `value` of the currently selected option. | `string` | — | ✅ |
+| `onSelect` | Called with the chosen option's `value` when the selection changes. | `(value: string) => void` | — | ✅ |
+| `className` | Additional CSS classes applied to the trigger button. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/radio-group.mdx b/packages/demo/src/content/components/radio-group.mdx
index c6f8d757..4d66f6f2 100644
--- a/packages/demo/src/content/components/radio-group.mdx
+++ b/packages/demo/src/content/components/radio-group.mdx
@@ -1,11 +1,65 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Radio Group"
-description: "Radio Group with sizes and variants"
+description: "Group of radio buttons for single selection"
---
import { RadioGroupDemo } from "@demo/components/demo/radio-group";
+## Overview
+
+Radio Group lets users pick exactly one option from a small, visible set — permission levels, plans, or any mutually exclusive choice. It's built on [Radix UI Radio Group](https://www.radix-ui.com/primitives/docs/components/radio-group), so arrow keys move between options, only one can be selected at a time, and the group is exposed correctly to assistive technology.
+
+It is a compound component: a `RadioGroup` wraps a set of `RadioGroupItem`s. Pair each item with a [Label](label) so the choice has a visible, clickable caption. Use it for a handful of always-visible options; when space is tight or the list is long, prefer a [Select](select) or [Radio Dropdown](radio-dropdown).
+
+## Usage
+
+Import the parts:
+
+```tsx
+import { RadioGroup, RadioGroupItem, Label } from "@eqtylab/equality";
+```
+
+Give each `RadioGroupItem` an `id` and point its `Label` at it with `htmlFor`, so clicking the label selects the option:
+
+```tsx
+const [value, setValue] = useState("read");
+
+
+
+
+ Read Only
+
+
+
+ Read & Write
+
+ ;
+```
+
+Radio Group can be controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
+
## Default
+
+## Slots
+
+| Name | Description |
+| ---------------- | ------------------------------------------------------------------------ |
+| `RadioGroup` | The container that manages the selection and keyboard navigation. |
+| `RadioGroupItem` | A single selectable option. Pair each with a `Label` via `id`/`htmlFor`. |
+
+## Props
+
+Both parts forward their [Radix](https://www.radix-ui.com/primitives/docs/components/radio-group) props (and `className`). The most commonly used are below.
+
+| Name | Applies to | Description | Type | Required |
+| --------------- | ---------------- | ----------------------------------------------------- | ------------------------- | -------- |
+| `value` | `RadioGroup` | The selected value (controlled). | `string` | ❌ |
+| `defaultValue` | `RadioGroup` | The initial selected value (uncontrolled). | `string` | ❌ |
+| `onValueChange` | `RadioGroup` | Called with the new value when the selection changes. | `(value: string) => void` | ❌ |
+| `disabled` | `RadioGroup` | Disables the entire group. | `boolean` | ❌ |
+| `value` | `RadioGroupItem` | The value this item represents. Required per item. | `string` | ✅ |
+| `id` | `RadioGroupItem` | Links the item to its `Label` via `htmlFor`. | `string` | ❌ |
+| `disabled` | `RadioGroupItem` | Disables just this option. | `boolean` | ❌ |
diff --git a/packages/demo/src/content/components/scroll-area.mdx b/packages/demo/src/content/components/scroll-area.mdx
index d3123b09..d8964292 100644
--- a/packages/demo/src/content/components/scroll-area.mdx
+++ b/packages/demo/src/content/components/scroll-area.mdx
@@ -1,15 +1,38 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Scroll Area"
-description: "Scroll Area with sizes and variants"
+description: "Scrollable container with custom scrollbars"
---
import { ScrollArea } from "@eqtylab/equality";
-## Default
+## Overview
-
-
+Scroll Area is a container that adds a styled, cross-browser scrollbar to overflowing content, replacing the inconsistent native one. It's built on [Radix UI Scroll Area](https://www.radix-ui.com/primitives/docs/components/scroll-area), so the scrollbar is themable and unobtrusive while normal wheel, touch, and keyboard scrolling continue to work.
+
+Give the Scroll Area a constrained height (via `className` or `style`); when its content is taller, a vertical scrollbar appears. Use it for long lists, panels, and menus where you want a consistent scrollbar treatment.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { ScrollArea } from "@eqtylab/equality";
+```
+
+Set a height on the Scroll Area and place your content inside:
+
+```tsx
+
+ {/* long content */}
+
+```
+
+## Example
+
+
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
@@ -17,49 +40,51 @@ import { ScrollArea } from "@eqtylab/equality";
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
-
+
+
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo
inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut
fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem
sequi nesciunt.
-
+
+
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
consectetur, adipisci velit, sed quia non numquam eius modi tempora
incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad
minima veniam, quis nostrum exercitationem ullam corporis suscipit
laboriosam, nisi ut aliquid ex ea commodi consequatur.
-
+
+
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam
nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas
nulla pariatur. At vero eos et accusamus et iusto odio dignissimos ducimus
qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores
et quas molestias excepturi sint occaecati cupiditate non provident.
-
+
+
Similique sunt in culpa qui officia deserunt mollitia animi, id est
laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita
distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque
nihil impedit quo minus id quod maxime placeat facere possimus, omnis
voluptas assumenda est, omnis dolor repellendus.
+
+
+
- Temporibus autem quibusdam et aut officiis debitis aut rerum
- necessitatibus saepe eveniet ut et voluptates repudiandae sint et
- molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente
- delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut
- perferendis doloribus asperiores repellat.
+## Slots
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
- tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
- veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
- commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
- velit esse cillum dolore eu fugiat nulla pariatur.
+| Name | Description |
+| ------------ | ------------------------------------------------------------------------------------- |
+| `ScrollArea` | The scrollable container. Wraps your content and renders a styled vertical scrollbar. |
+| `ScrollBar` | The scrollbar element, exported for advanced composition (e.g. a horizontal bar). |
- Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
- deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste
- natus error sit voluptatem accusantium doloremque laudantium, totam rem
- aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto
- beatae vitae dicta sunt explicabo.
+## Props
-
-
+Scroll Area forwards the underlying [Radix Scroll Area Root](https://www.radix-ui.com/primitives/docs/components/scroll-area#root) props (such as `type` and `scrollHideDelay`) in addition to the props below.
+
+| Name | Description | Type | Default | Required |
+| ----------- | ----------------------------------------------------- | ----------- | ------- | -------- |
+| `children` | The content to render inside the scrollable viewport. | `ReactNode` | — | ✅ |
+| `className` | Additional CSS classes applied to the container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/search-bar.mdx b/packages/demo/src/content/components/search-bar.mdx
index 240428c1..268ae9c4 100644
--- a/packages/demo/src/content/components/search-bar.mdx
+++ b/packages/demo/src/content/components/search-bar.mdx
@@ -1,11 +1,62 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Search Bar"
-description: "Search Bar with sizes and variants"
+description: "Text input for searching or filtering"
---
import { SearchBarDemo } from "@demo/components/demo/search-bar";
+## Overview
+
+Search Bar is a purpose-built [Input](input) for searching and filtering. It shows a leading search icon and, once text is entered, a trailing clear button that empties the field. It renders inside a semantic `` landmark, so assistive technology can identify it as the page's search region.
+
+It is a controlled component: you pass the query string with `searchQuery`, updating it from `setSearchQuery` on every change. This makes it easy to drive live filtering or debounce the value before querying.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { SearchBar } from "@eqtylab/equality";
+```
+
+Hold the query in state and pass it back through `setSearchQuery`:
+
+```tsx
+const [searchQuery, setSearchQuery] = useState("");
+
+ ;
+```
+
+Then filter your data from `searchQuery`:
+
+```tsx
+const results = items.filter((item) =>
+ item.name.toLowerCase().includes(searchQuery.toLowerCase()),
+);
+```
+
## Default
+
+## Custom Placeholder
+
+The placeholder defaults to "Search...". Override it with the `placeholder` prop to hint at what's being searched.
+
+```tsx
+
+```
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| ---------------- | ---------------------------------------------------------------- | ------------------------- | ------------- | -------- |
+| `searchQuery` | The current search value (controlled). | `string` | — | ✅ |
+| `setSearchQuery` | Called with the new value on change, and with `""` when cleared. | `(query: string) => void` | — | ✅ |
+| `placeholder` | Placeholder text for the input. | `string` | `"Search..."` | ❌ |
+| `className` | Additional CSS classes applied to the container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/section-heading.mdx b/packages/demo/src/content/components/section-heading.mdx
index c209e696..8f3a279a 100644
--- a/packages/demo/src/content/components/section-heading.mdx
+++ b/packages/demo/src/content/components/section-heading.mdx
@@ -1,27 +1,74 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Section Heading"
-description: "Section Heading with sizes and variants"
+description: "Section title with optional description and actions"
---
import { SectionHeading, Button } from "@eqtylab/equality";
import { UserPlus } from "lucide-react";
import { SectionHeadingDemo } from "@demo/components/demo/section-heading";
+## Overview
+
+Section Heading introduces a section of a page or panel with a title, an optional supporting description, and optional actions aligned to the right. Use it above lists, tables, cards, and settings groups to give each section a consistent header with room for controls like a "Clear Filters" link or an "Add" button.
+
+The title renders as an `h3`. When right content is provided, the heading switches to a spread layout with the title/description on the left and the actions on the right.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { SectionHeading } from "@eqtylab/equality";
+```
+
+Basic usage — `heading` is required:
+
+```tsx
+
+```
+
+Add actions with `renderRightContent`, a function that returns the nodes to place on the right:
+
+```tsx
+ (
+
+
+ Add Member
+
+ )}
+/>
+```
+
## Variants
### Default
-
+
### With Description
-
+
-### With right content
+### With Right Content
-### With description and right content
+### With Description and Right Content
+
+## Props
+
+| Name | Description | Type | Default | Required |
+| -------------------- | --------------------------------------------------------------------------- | ----------------- | ------- | -------- |
+| `heading` | The section title, rendered as an `h3`. | `string` | — | ✅ |
+| `description` | Optional supporting text shown beneath the title. | `string` | — | ❌ |
+| `renderRightContent` | Function returning nodes (e.g. buttons) placed on the right of the heading. | `() => ReactNode` | — | ❌ |
+| `className` | Additional CSS classes applied to the container. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/select.mdx b/packages/demo/src/content/components/select.mdx
index 98f60214..bf2537fd 100644
--- a/packages/demo/src/content/components/select.mdx
+++ b/packages/demo/src/content/components/select.mdx
@@ -1,33 +1,78 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Select"
-description: "Select with sizes and variants"
+description: "Dropdown for choosing one option from a list"
---
import { SelectDemo } from "@demo/components/demo/select";
import { ELEVATION } from "@eqtylab/equality";
+## Overview
+
+Select lets users choose a single option from a dropdown list. It's built on [Radix UI Select](https://www.radix-ui.com/primitives/docs/components/select), so it's fully keyboard accessible (type-ahead, arrow keys, Esc ), manages focus, and exposes the right roles to assistive technology. Long lists scroll within the popover.
+
+It is a compound component: a `Select` root wraps a `SelectTrigger` (showing the current `SelectValue`) and a `SelectContent` holding the `SelectItem`s. Use it for choosing from a moderate set of options; for free-text entry use an [Input](input), and for a lightweight filter with counts use a [Radio Dropdown](radio-dropdown).
+
+## Usage
+
+Import the parts you need:
+
+```tsx
+import {
+ Select,
+ SelectTrigger,
+ SelectValue,
+ SelectContent,
+ SelectItem,
+} from "@eqtylab/equality";
+```
+
+Compose a controlled select. `SelectValue`'s `placeholder` shows before a choice is made:
+
+```tsx
+const [value, setValue] = useState("");
+
+
+
+
+
+
+ Apple
+ Orange
+ Pear
+
+ ;
+```
+
+Select can be controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`). Pair the trigger with a [Label](label) via `id`/`htmlFor` for an accessible caption.
+
## Variants
-### Default Select
+### Default
-### Disabled Select
+### Disabled
+
+Set `disabled` on the `Select` root to disable the whole control.
-### Pre-selected Select
+### Pre-selected
+
+Use `defaultValue` on the root to start with an option chosen. Long lists scroll within the popover.
## Elevations
+Set the `elevation` prop on `SelectContent` to place the dropdown on the elevation scale. `overlay` is the default.
+
### Sunken
-### Base (default)
+### Base
@@ -35,6 +80,42 @@ import { ELEVATION } from "@eqtylab/equality";
-### Overlay
+### Overlay (default)
+
+### Usage
+
+```tsx
+import { ELEVATION } from "@eqtylab/equality";
+
+… ;
+```
+
+## Slots
+
+| Name | Description |
+| ----------------- | ---------------------------------------------------------------------- |
+| `Select` | The root that manages selection state. Owns `value`/`onValueChange`. |
+| `SelectTrigger` | The button that opens the dropdown and displays the value. |
+| `SelectValue` | Renders the selected value, or a `placeholder` when nothing is chosen. |
+| `SelectContent` | The dropdown popover holding the options. Owns `elevation`. |
+| `SelectItem` | A selectable option. Requires a `value`. |
+| `SelectGroup` | Groups related items. |
+| `SelectLabel` | A heading for a group of items. |
+| `SelectSeparator` | A divider between items or groups. |
+
+## Props
+
+The parts forward their [Radix Select](https://www.radix-ui.com/primitives/docs/components/select) props (and `className`). The most commonly used are below.
+
+| Name | Applies to | Description | Type | Default |
+| --------------- | --------------- | ----------------------------------------------------- | ------------------------------------- | --------- |
+| `value` | `Select` | The selected value (controlled). | `string` | — |
+| `defaultValue` | `Select` | The initial selected value (uncontrolled). | `string` | — |
+| `onValueChange` | `Select` | Called with the new value when the selection changes. | `(value: string) => void` | — |
+| `disabled` | `Select` | Disables the entire control. | `boolean` | `false` |
+| `placeholder` | `SelectValue` | Text shown before a value is selected. | `string` | — |
+| `value` | `SelectItem` | The value this item represents. Required per item. | `string` | — |
+| `disabled` | `SelectItem` | Disables just this option. | `boolean` | `false` |
+| `elevation` | `SelectContent` | Position of the dropdown on the elevation scale. | `sunken`, `base`, `raised`, `overlay` | `overlay` |
diff --git a/packages/demo/src/content/components/separator.mdx b/packages/demo/src/content/components/separator.mdx
index 0326281d..b823c3dc 100644
--- a/packages/demo/src/content/components/separator.mdx
+++ b/packages/demo/src/content/components/separator.mdx
@@ -1,19 +1,66 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Separator"
-description: "Separator with sizes and variants"
+description: "Visual divider line, horizontal or vertical"
---
import { Separator } from "@eqtylab/equality";
+## Overview
+
+Separator draws a thin dividing line to visually separate content or groups of controls. It's built on [Radix UI Separator](https://www.radix-ui.com/primitives/docs/components/separator) and can run horizontally (the default) or vertically.
+
+By default the separator is `decorative`, meaning it's purely visual and hidden from assistive technology. If the line represents a meaningful boundary between sections of content, set `decorative={false}` so it's exposed as a semantic separator to screen readers.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Separator } from "@eqtylab/equality";
+```
+
+Basic usage:
+
+```tsx
+
+```
+
+A vertical separator needs a parent with height to be visible:
+
+```tsx
+
+ Left
+
+ Right
+
+```
+
## Variants
-### Orientation: horizontal
+### Horizontal orientation (default)
-### Orientation: vertical
+### Vertical orientation
+
+### Usage
+
+```tsx
+
+
+```
+
+## Props
+
+Separator also accepts standard element attributes and `className`.
+
+| Name | Description | Type | Default | Required |
+| ------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------ | ------------ | -------- |
+| `orientation` | The direction of the line. | `horizontal`, `vertical` | `horizontal` | ❌ |
+| `decorative` | When `true`, the separator is purely visual and hidden from assistive tech. Set `false` for a meaningful divider. | `boolean` | `true` | ❌ |
+| `className` | Additional CSS classes applied to the separator. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/skeleton.mdx b/packages/demo/src/content/components/skeleton.mdx
index 1939c682..55c03e19 100644
--- a/packages/demo/src/content/components/skeleton.mdx
+++ b/packages/demo/src/content/components/skeleton.mdx
@@ -1,11 +1,87 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Skeleton"
-description: "Skeleton components"
+description: "Animated placeholder shown while content loads"
---
import { Skeleton } from "@eqtylab/equality";
-## Skeleton
+## Overview
+
+Skeleton is a low-fidelity, animated placeholder that stands in for content while it loads. Showing the rough shape of the incoming UI keeps layout stable and feels faster than a spinner, because the page doesn't jump when data arrives.
+
+It has no built-in dimensions — you shape each placeholder with `className` (width, height, border radius) to mirror the element it replaces. Compose several skeletons to mock up a card, list row, or avatar-and-text block. For a full-screen blocking indicator instead, use a [Loading Overlay](loading-overlay).
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Skeleton } from "@eqtylab/equality";
+```
+
+Size it with utility classes to match the content:
+
+```tsx
+
+```
+
+Show a skeleton while data is loading, then swap in the real content:
+
+```tsx
+{
+ isLoading ? : {user.name}
;
+}
+```
+
+## Examples
+
+### Single Line
+
+### Shapes
+
+Use border-radius and sizing utilities to match different elements — a circle for an avatar, short bars for text.
+
+
+
+### Card Placeholder
+
+Compose several skeletons to stand in for a whole block of content.
+
+
+
+
+
+
+
+
+### Usage
+
+```tsx
+{
+ /* Avatar + text */
+}
+;
+```
+
+## Props
+
+Skeleton accepts all standard `div` attributes. Size and shape it with `className`.
+
+| Name | Description | Type | Default | Required |
+| ----------- | -------------------------------------------------------------------- | -------- | ------- | -------- |
+| `className` | Utility classes that set the placeholder's size, shape, and spacing. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/sort-selector.mdx b/packages/demo/src/content/components/sort-selector.mdx
index 49b5cc74..d79218f9 100644
--- a/packages/demo/src/content/components/sort-selector.mdx
+++ b/packages/demo/src/content/components/sort-selector.mdx
@@ -1,39 +1,81 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Sort Selector"
+description: "Dropdown for choosing a list's sort field and order"
---
import { SortSelectorDemo } from "@demo/components/demo/sort-selector";
+## Overview
+
+Sort Selector is a dropdown for choosing how a list is sorted. It presents a curated set of field-and-direction options — Name (A–Z / Z–A) plus date options — and reflects the current choice on its trigger. It's built on a [Dropdown Menu](dropdown-menu) with a [Button](button) trigger.
+
+It is a controlled component: you hold the current `sortField` and `sortOrder` and update them from the `setSortField`/`setSortOrder` callbacks. When the current sort differs from the configured defaults, a **Reset** link appears in the menu to return to them.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { SortSelector } from "@eqtylab/equality";
+```
+
+Hold the field and order in state, then apply them to your data:
+
+```tsx
+const [sortField, setSortField] = useState("name");
+const [sortOrder, setSortOrder] = useState("asc");
+
+ ;
+```
+
+The component only selects the sort preference — apply it to your list yourself, for example:
+
+```tsx
+const sorted = [...items].sort(compareBy(sortField, sortOrder));
+```
+
## Default
Shows the Sort Selector with default sorting (by Name, ascending):
-## Props
+## Sort Mode
-The Sort Selector component accepts several props to customize its behavior:
+The `sortMode` prop determines which date options are offered. It accepts `"created"` (default) or `"updated"`:
-- **`sortField`** (`"name"`, `"type"`, `"createdAt"`, `"updatedAt"`): The field by which to sort.
-- **`sortOrder`** (`"asc"` or `"desc"`): The sort direction (ascending/descending).
-- **`sortMode`** (`"created"` or `"updated"`, default: `"created"`): Determines which date-related sorting options are offered.
-- **`showDateOptions`** (`true` or `false`, default: `true`): Controls whether date sorting options (created/updated) are displayed.
-- **`setSortField`** and **`setSortOrder`**: Handler functions to update the current sort field and order.
+- `sortMode="created"` → "Recently Created" / "Oldest Created"
+- `sortMode="updated"` → "Recently Updated" / "Oldest Updated"
-You can combine these props to control and respond to sorting preference changes in your UI.
+
-### Sort mode options
+## Hide Date Options
-The `sortMode` prop determines which set of date-related sorting options are shown in the Sort Selector. It accepts either `"created"` (default) or `"updated"`.
+Set `showDateOptions={false}` to offer only the Name options:
-- When `sortMode="created"`, you will see options to sort by "Recently Created" or "Oldest Created".
-- When `sortMode="updated"`, you will see options to sort by "Recently Updated" or "Oldest Updated".
+
-
+## Reset
-### Hide date options
+When the current sort no longer matches `defaultSortField`/`defaultSortOrder` (both `name`/`asc` by default), a **Reset** link appears in the dropdown that restores the defaults. Open the menu and pick a different option to see it.
-Set `showDateOptions={false}` to hide sorting options for creation date:
+## Props
-
+| Name | Description | Type | Default | Required |
+| ------------------ | ------------------------------------------------------------- | ---------------------------------------- | --------- | -------- |
+| `sortField` | The currently selected sort field (controlled). | `name`, `type`, `createdAt`, `updatedAt` | — | ✅ |
+| `sortOrder` | The current sort direction (controlled). | `asc`, `desc` | — | ✅ |
+| `setSortField` | Called with the new field when the selection changes. | `(field: SortField) => void` | — | ✅ |
+| `setSortOrder` | Called with the new order when the selection changes. | `(order: SortOrder) => void` | — | ✅ |
+| `sortMode` | Which date options are offered. | `created`, `updated` | `created` | ❌ |
+| `showDateOptions` | Whether the date options are shown. | `boolean` | `true` | ❌ |
+| `defaultSortField` | The field the Reset link restores to. | `name`, `type`, `createdAt`, `updatedAt` | `name` | ❌ |
+| `defaultSortOrder` | The order the Reset link restores to. | `asc`, `desc` | `asc` | ❌ |
+| `setCurrentPage` | Optional. Called with `1` on any change, to reset pagination. | `(page: number) => void` | — | ❌ |
+| `className` | Additional CSS classes applied to the trigger button. | `string` | — | ❌ |
diff --git a/packages/demo/src/content/components/spinner.mdx b/packages/demo/src/content/components/spinner.mdx
index 0b1a8519..981e03ba 100644
--- a/packages/demo/src/content/components/spinner.mdx
+++ b/packages/demo/src/content/components/spinner.mdx
@@ -1,13 +1,14 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Spinner"
+description: "Indeterminate loading indicator"
---
import { Spinner } from "@eqtylab/equality";
## Overview
-The Spinner component provides a visual loading indicator to communicate that content or an action is in progress. Use it when fetching data, submitting forms, or during any asynchronous operation where the user should wait.
+The Spinner component provides a visual loading indicator to communicate that content or an action is in progress. Use it when fetching data, submitting forms, or during any asynchronous operation where the user should wait. For a full-screen blocking indicator, use [Loading Overlay](loading-overlay).
## Usage
diff --git a/packages/demo/src/content/components/switch.mdx b/packages/demo/src/content/components/switch.mdx
index 28fcb26a..4bb0b872 100644
--- a/packages/demo/src/content/components/switch.mdx
+++ b/packages/demo/src/content/components/switch.mdx
@@ -1,18 +1,48 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Switch"
-description: "Switch with sizes and variants"
+description: "Toggle switch for on/off states"
---
import { SwitchDemo } from "@demo/components/demo/switch";
-## Size Variants
+## Overview
+
+Switch is a toggle for a single on/off setting that takes effect immediately — like enabling a feature or notification. It's built on [Radix UI Switch](https://www.radix-ui.com/primitives/docs/components/switch), so it's keyboard accessible and exposes the correct toggle role and state to assistive technology.
+
+It comes in three sizes and a `danger` variant, and can show an icon on its thumb. Use a Switch for instant-effect settings; when a choice only applies after submitting a form, prefer a [Checkbox](checkbox). Pair it with a [Label](label) for an accessible caption.
+
+## Usage
+
+Import the component:
+
+```tsx
+import { Switch } from "@eqtylab/equality";
+```
+
+Controlled, driving the state yourself:
+
+```tsx
+const [enabled, setEnabled] = useState(false);
+
+ ;
+```
+
+Uncontrolled, with an initial state:
+
+```tsx
+
+```
+
+## Sizes
+
+Three sizes are available; `md` is the default.
### Small
-### Medium (Default)
+### Medium (default)
@@ -20,22 +50,21 @@ import { SwitchDemo } from "@demo/components/demo/switch";
-## With Thumb Icon
+### Usage
-
+```tsx
+
+
+
+```
## States
-### Default Off
+### Off
-### Default On
+### On
@@ -47,12 +76,54 @@ import { SwitchDemo } from "@demo/components/demo/switch";
-## Variants
+## Color Variants
-### Default
+Use the `danger` variant for toggles with a destructive or high-risk consequence.
-
+
-### Danger
+### Usage
-
+```tsx
+
+
+```
+
+## Thumb Icon
+
+Set `thumbIcon` to render an icon on the switch thumb — a [Lucide](https://lucide.dev/icons/) icon name or a React element. The switch shows a single icon; to display a different icon per state, swap `thumbIcon` yourself based on `checked`.
+
+
+
+### Usage
+
+```tsx
+// Single icon
+
+
+// Different icon per state
+
+```
+
+## Props
+
+Switch also accepts the underlying [Radix Switch Root](https://www.radix-ui.com/primitives/docs/components/switch) props (`checked`, `onCheckedChange`, `defaultChecked`, `required`, `name`, `value`) and `className`.
+
+| Name | Description | Type | Default | Required |
+| ----------------- | ---------------------------------------------------------- | ---------------------------- | --------- | -------- |
+| `checked` | The controlled on/off state. | `boolean` | — | ❌ |
+| `onCheckedChange` | Called with the new state when toggled. | `(checked: boolean) => void` | — | ❌ |
+| `defaultChecked` | The initial state when uncontrolled. | `boolean` | — | ❌ |
+| `disabled` | Prevents interaction and visually mutes the switch. | `boolean` | `false` | ❌ |
+| `size` | The size of the switch. | `sm`, `md`, `lg` | `md` | ❌ |
+| `variant` | The visual style, communicating intent. | `default`, `danger` | `default` | ❌ |
+| `thumbIcon` | Icon shown on the thumb. A Lucide name or a React element. | `string`, `ReactElement` | — | ❌ |
diff --git a/packages/demo/src/content/components/table.mdx b/packages/demo/src/content/components/table.mdx
index dc7ded99..7156dd30 100644
--- a/packages/demo/src/content/components/table.mdx
+++ b/packages/demo/src/content/components/table.mdx
@@ -9,8 +9,6 @@ import { ELEVATION } from "@eqtylab/equality";
## Overview
----
-
Tables are built from compositional primitives that map directly to [HTML table elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/table). Each primitive is a styled wrapper that accepts all native HTML attributes, giving you full control over layout, sizing, and responsiveness.
Rows use CSS Grid with [subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid) for column sizing. Define columns once via the `columns` prop on `` — all rows share the same column tracks.
@@ -23,8 +21,6 @@ Rows use CSS Grid with [subgrid](https://developer.mozilla.org/en-US/docs/Web/CS
## Usage
----
-
Import the components:
```tsx
@@ -61,8 +57,6 @@ Basic usage:
## Variants
----
-
### Default
@@ -192,8 +186,6 @@ Use the `sticky` prop on `` to keep column headers visible while sc
## Column Sizing
----
-
The `columns` prop accepts a CSS [`grid-template-columns`](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns) value. All rows share the same column tracks via CSS subgrid.
### Fixed and Flexible Columns
@@ -228,8 +220,6 @@ Use `auto` for columns that should shrink to fit their content. This is useful f
## Truncation
----
-
Use Tailwind's `truncate min-w-0` classes directly on `` and `` to clip overflowing text with an ellipsis. For truncation to work, the column must use `minmax(0, *)` in the `columns` definition so cells can shrink below their content size.
@@ -263,8 +253,6 @@ Use Tailwind's `truncate min-w-0` classes directly on `` and `
@@ -300,8 +288,6 @@ Use [container queries](https://tailwindcss.com/docs/responsive-design#what-are-
## Elevations
----
-
### Sunken
@@ -320,8 +306,6 @@ Use [container queries](https://tailwindcss.com/docs/responsive-design#what-are-
## Props
----
-
### TableContainer
| Name | Description | Type | Default | Required |
diff --git a/packages/demo/src/content/components/tabs.mdx b/packages/demo/src/content/components/tabs.mdx
index 930e7861..5c720d7a 100644
--- a/packages/demo/src/content/components/tabs.mdx
+++ b/packages/demo/src/content/components/tabs.mdx
@@ -16,8 +16,6 @@ The component automatically manages the active tab state and provides a smooth a
## Usage
----
-
Import the component from the Equality UI library:
```tsx
@@ -46,8 +44,6 @@ Basic usage requires an `id` and an array of `items`:
## Variants
----
-
### Default
The default variant uses a transparent background with an animated underline indicator:
@@ -207,8 +203,6 @@ Use the `suffix` property to render any React component after a tab's label. Thi
## Props
----
-
| Name | Description | Type | Default | Required |
| -------------------- | ------------------------------------------------------------------------ | ------------------------- | ------------- | -------- |
| `id` | Unique identifier for the tabs instance, used for the animated indicator | `string` | - | ✅ |
diff --git a/packages/demo/src/content/components/textarea.mdx b/packages/demo/src/content/components/textarea.mdx
index 57c34337..ae0e3e32 100644
--- a/packages/demo/src/content/components/textarea.mdx
+++ b/packages/demo/src/content/components/textarea.mdx
@@ -1,21 +1,44 @@
---
layout: "@demo/layouts/mdx-layout.astro"
heading: "Textarea"
-description: "Textarea with sizes and variants"
+description: "Multi-line text input field"
---
import { Textarea } from "@eqtylab/equality";
+## Overview
+
+Textarea is a multi-line text field for longer free-form input such as comments, descriptions, or messages. It wraps a native `