textarea]:h-auto",
// Variants based on alignment.
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
// Focus state.
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50",
// Error state.
"has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
className
)}
{...props}
/>
)
}
```
**Example:**
```tsx
import { InputGroup, InputGroupInput, InputGroupAddon } from '@saasflare/ui';
$
```
---
## Input OTP
**Description:** One-time-code input with per-digit slots, paste handling, and auto-advance.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/input-otp.json
```
**Example:**
```tsx
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '@saasflare/ui';
```
---
## Item
**Description:** Generic list/row primitive with leading media, title, description, and trailing action slots.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/item.json
```
**Props:**
```ts
interface ItemProps
extends Omit
, keyof SaasflareComponentProps>,
Omit, "size">,
SaasflareComponentProps {
/** Render as child element (Radix Slot pattern). */
asChild?: boolean
/** Row density. (`"default"` is a deprecated alias for `"md"`.) */
size?: VariantProps["size"] | "default"
}
```
**Example:**
```tsx
import { ItemGroup, Item, ItemContent, ItemTitle } from '@saasflare/ui';
-
Settings
```
---
## Kbd
**Description:** Inline keyboard-key styling for documenting shortcuts (⌘K, Ctrl+C).
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/kbd.json
```
**Props:**
```ts
interface KbdProps extends Omit, keyof SaasflareComponentProps>, SaasflareComponentProps {}
/**
* Renders a single keyboard key as a styled `` element. Use it in
* tooltips, menus, and docs to surface keyboard shortcuts.
*
* @component
* @layer core
*/
function Kbd({ className, surface, radius, animated, iconWeight, ...props }: KbdProps) {
const sf = useSaasflareProps({ surface, radius, animated, iconWeight })
return (
)
}
```
**Example:**
```tsx
import { Kbd, KbdGroup } from '@saasflare/ui';
CtrlS
```
---
## Label
**Description:** Accessible form label wired to its control, with required and disabled styling.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/label.json
```
**Props:**
```ts
interface LabelProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {}
/**
* Accessible form label built on the Radix Label primitive. Dims itself when
* the associated peer control or `data-disabled` group is disabled; associate
* with a control via `htmlFor`.
*
* @component
* @layer core
*
* @example
*
*/
function Label({
className,
surface,
radius,
animated,
iconWeight,
...props
}
```
**Example:**
```tsx
import { Label } from '@saasflare/ui';
```
---
## Marquee
**Description:** Infinite-scrolling row of content (logos, testimonials) — pure CSS, no animation frames.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/marquee.json
```
**Props:**
```ts
export interface MarqueeProps
extends Omit, keyof SaasflareComponentProps | "children">,
SaasflareComponentProps {
/** Items to scroll infinitely. */
children: ReactNode
/** Reverse scroll direction. Default: `false` */
reverse?: boolean
/** Scroll duration in seconds for one full cycle. Default: `40` */
speed?: number
/** Pause scrolling on hover. Default: `true` */
pauseOnHover?: boolean
/** Gap between items in pixels. Default: `48` */
gap?: number
/** Number of times to duplicate the content strip. Default: `2` */
repeat?: number
/** Enable vertical scrolling instead of horizontal. Default: `false` */
vertical?: boolean
/** Additional class names for the container. */
className?: string
}
```
**Example:**
```tsx
import { Marquee } from '@saasflare/ui';
```
---
## Menubar
**Description:** Desktop-style horizontal menu bar (File / Edit / View) with dropdowns and keyboard navigation.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/menubar.json
```
**Props:**
```ts
interface MenubarProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {}
/**
* Root horizontal menu bar. Owns the Saasflare surface/radius/animated context for
* its menus and emits the corresponding data attributes.
*/
function Menubar({
className,
surface,
radius,
animated,
iconWeight,
...props
}
```
**Example:**
```tsx
import { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem } from '@saasflare/ui';
File
New
Open
```
---
## Metric Card
**Description:** Dashboard metric card with sparkline support and trend indicator.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/metric-card.json
```
**Props:**
```ts
interface MetricCardProps extends Omit, keyof SaasflareComponentProps>, SaasflareComponentProps {
/** Metric label (e.g. "Revenue", "Active Users") */
label: string
/** Formatted metric value (e.g. "$12,345", "1,234") */
value: string
/** Optional trend indicator */
trend?: MetricTrend
/** Optional icon element */
icon?: React.ReactNode
}
```
**Example:**
```tsx
import { MetricCard } from "@saasflare/ui";
import { UsersIcon } from "./phosphor";
}
/>
```
---
## Multi Select
**Description:** Searchable multi-select with chips, select-all, a max limit, and async option loading — built on Popover + cmdk, with a plain string[] value.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/multi-select.json
```
**Props:**
```ts
export interface MultiSelectProps
extends Omit, MultiSelectDomConflicts | keyof SaasflareComponentProps>,
SaasflareComponentProps {
/** Options to choose from. Update this array (+ `loading`) for async loading. */
options: MultiSelectOption[]
/** Controlled selected values (array of `option.value`). */
value?: string[]
/** Uncontrolled initial selection. @default [] */
defaultValue?: string[]
/** Fires on every selection change with the next value array. */
onValueChange?: (value: string[]) => void
/** Trigger placeholder when nothing is selected. @default "Select…" */
placeholder?: string
/** Search input placeholder. @default "Search…" */
searchPlaceholder?: string
/** Node shown when the query matches no option. @default "No results." */
emptyMessage?: React.ReactNode
/** Max number of selectable values. Over-limit options become non-interactive (data-disabled) and a "Max N selected" hint shows. */
max?: number
/** Show a clear-all (×) affordance on the trigger when selection is non-empty. @default true */
clearable?: boolean
/** Show a "Select all / Clear" header row (operates on the CURRENTLY FILTERED, non-disabled options). @default false */
selectAll?: boolean
/** Label for the select-all row. @default "Select all" */
selectAllLabel?: string
/** Close the popover after each pick. @default false (multi-pick stays open) */
closeOnSelect?: boolean
/** Max chip ROWS shown collapsed on the trigger before overflowing to "+N more" (HeroUI isMultiline analog). @default 1 */
maxRows?: number
/** Async: render a spinner row + aria-busy; pair with `onSearchChange` for server filtering. @default false */
loading?: boolean
/** Controlled search query (optional). */
searchValue?: string
/** Fires on query change. PRESENCE of this prop flips cmdk to `shouldFilter={false}` (server-side filtering); omit it for built-in client fuzzy search. */
onSearchChange?: (query: string) => void
/** Disable the whole control. */
disabled?: boolean
/** Forwarded to the cmdk list for ARIA. */
"aria-label"?: string
/** className on the trigger button (root data-axes div wraps it). */
className?: string
/** className on the popover content. */
contentClassName?: string
}
```
**Example:**
```tsx
import { MultiSelect, type MultiSelectOption } from "@saasflare/ui"
const options: MultiSelectOption[] = [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "svelte", label: "Svelte" },
]
const [value, setValue] = React.useState([])
```
---
## Mouse Gradient Blob
**Description:** Large blurred gradient blob that trails the cursor with spring physics. Ambient hero background.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/mouse-gradient-blob.json
```
**Props:**
```ts
export interface MouseGradientBlobProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {
/** Diameter of the blob in pixels. Default: `500` */
size?: number
/** Gradient color stops. Default: primary + chart-2 tokens */
colors?: [string, string]
/** Opacity of the blob (0–1). Default: `0.15` */
opacity?: number
/** Blur radius in pixels. Default: `80` */
blur?: number
/** Additional class names for the container */
className?: string
}
```
**Example:**
```tsx
import { MouseGradientBlob } from '@saasflare/ui';
Your content here
```
---
## Moving Border
**Description:** Continuously rotating conic-gradient border around an element. Eye-catching CTA wrapper.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/moving-border.json
```
**Props:**
```ts
export interface MovingBorderProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {
/** Content inside the border. */
children: ReactNode
/** Gradient colors for the rotating border. */
colors?: string[]
/** Rotation cycle duration in seconds. Default: `6` */
duration?: number
/** Border width in pixels. Default: `1.5` */
borderWidth?: number
/** Border radius (CSS value). Default: `"0.75rem"` */
borderRadius?: string
/** Additional class names for the outer wrapper. */
className?: string
}
```
**Example:**
```tsx
import { MovingBorder } from '@saasflare/ui';
```
---
## Native Select
**Description:** Lightweight native select with a chevron overlay — zero-JS dropdown for simple option lists.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/native-select.json
```
**Props:**
```ts
interface NativeSelectProps
extends Omit, "size" | keyof SaasflareComponentProps>,
SaasflareComponentProps {
/** Control height. `"sm"` renders a more compact trigger; `"md"` is the standard height. (`"default"` is a deprecated alias for `"md"`.) */
size?: "sm" | "md" | "default"
}
```
**Example:**
```tsx
import { NativeSelect, NativeSelectOption } from '@saasflare/ui';
Option A
Option B
```
---
## Navigation Menu
**Description:** Top-level site navigation with animated mega-menu dropdowns and active-link states.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/navigation-menu.json
```
**Props:**
```ts
interface NavigationMenuProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {
/**
* Render the shared viewport panel for dropdown content.
*
* @default true
*/
viewport?: boolean
}
```
**Example:**
```tsx
import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink } from '@saasflare/ui';
Products
Documentation
```
---
## Notification Center
**Description:** Bell-icon trigger + dropdown inbox with per-item read/dismiss controls. Composes Button + Popover + Badge.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/notification-center.json
```
**Props:**
```ts
export interface NotificationCenterProps extends SaasflareComponentProps {
/** Notifications to display. */
items: NotificationItem[]
/** Header title in the popover. Default: `"Notifications"`. */
title?: string
/** Called when the user clicks a row (also passes the item). */
onItemClick?: (item: NotificationItem) => void
/** Called when "Mark as read" is tapped on a row. */
onMarkRead?: (id: string) => void
/** Called when "Mark all as read" is tapped. Hide the action by omitting this. */
onMarkAllRead?: () => void
/** Custom empty-state content. */
emptyState?: ReactNode
/** Maximum items rendered (older ones truncated). */
limit?: number
/** Additional class names on the trigger button. */
triggerClassName?: string
/** Additional class names on the popover content. */
contentClassName?: string
}
```
**Example:**
```tsx
api.read(id)}
onMarkAllRead={() => api.readAll()}
/>
```
---
## Number Input
**Description:** Numeric input with ± stepper buttons, min/max clamping, configurable step + precision.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/number-input.json
```
**Props:**
```ts
export interface NumberInputProps extends SaasflareComponentProps {
/** Controlled value. */
value?: number
/** Uncontrolled initial value. */
defaultValue?: number
/** Called when the value changes (after clamp). */
onChange?: (value: number) => void
/** Minimum allowed value. */
min?: number
/** Maximum allowed value. */
max?: number
/** Step for stepper buttons + arrow keys. Default: `1`. */
step?: number
/** Decimal precision for display. Default: derived from `step`. */
precision?: number
/** Placeholder shown when empty. */
placeholder?: string
/** Disable the input. */
disabled?: boolean
/** Read-only input. */
readOnly?: boolean
/** Hide the stepper buttons. */
hideSteppers?: boolean
/** Additional class names on the outer wrapper. */
className?: string
/** Name attribute (for form submission). */
name?: string
/** Accessible label. */
"aria-label"?: string
}
```
**Example:**
```tsx
const [n, setN] = useState(1);
```
---
## Page Header
**Description:** Page header block with breadcrumbs, title, description, and an action slot for primary buttons.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/page-header.json
```
**Props:**
```ts
interface PageHeaderProps extends Omit, keyof SaasflareComponentProps>, SaasflareComponentProps {
/** Page title */
title: string
/** Optional description below the title */
description?: string
/** Optional breadcrumb element rendered above the title */
breadcrumbs?: React.ReactNode
/** Action slot (buttons, etc.) aligned to the right */
actions?: React.ReactNode
}
```
**Example:**
```tsx
import { PageHeader } from "@saasflare/ui";
Invite Member}
/>
```
---
## Page Transition
**Description:** Wraps route content with enter/exit animations for smooth client-side page transitions.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/page-transition.json
```
**Props:**
```ts
export interface PageTransitionProps
extends Omit<
React.ComponentProps<"div">,
MotionConflicts | "children" | keyof SaasflareComponentProps
>,
SaasflareComponentProps {
/** Child content to animate. */
children: ReactNode
/** Animation preset. Default: `"fade"` */
variant?: keyof typeof VARIANTS
/** Transition duration in seconds. Default: `0.3` */
duration?: number
/**
* Unique key for AnimatePresence to trigger re-animation on route changes.
* Default: the current pathname (Next.js App Router).
*/
transitionKey?: string
}
```
**Example:**
```tsx
import { PageTransition } from '@saasflare/ui';
// In your layout.tsx — the current pathname is used as the animation key.
export default function Layout({ children }: { children: React.ReactNode }) {
return {children};
}
```
---
## Pagination
**Description:** Page navigation with previous/next, page numbers, and ellipsis truncation for long ranges.
**Install:**
```bash
npx shadcn add https://ui.saasflare.io/r/pagination.json
```
**Props:**
```ts
interface PaginationProps
extends Omit, keyof SaasflareComponentProps>,
SaasflareComponentProps {}
/**
* Pagination root — semantic `