# Blog (/exabase/design/docs/blog)
# Introduction (/exabase/design/docs) ## Overview [#overview] This site provides code snippets for UI components based on the exaBase Design System. Exabase Design System is a comprehensive collection of reusable components, design tokens, and guidelines that help you build consistent and accessible user interfaces. Developers and designers can copy the code snippets and paste them into their own applications to immediately use the sophisticated UI baseline. If product-specific behavior is needed to improve the UX, you can customize the pasted code. ## Features [#features] * 🎨 **Theming Support** - Customize colors, typography, and more * 🌙 **Dark Mode** - Built-in dark mode support * ♿ **Accessible** - WCAG compliant components * 📦 **Tree-shakeable** - Import only what you need * 🎯 **TypeScript** - Full TypeScript support ## Getting Started [#getting-started] To get started with Exabase Design System, choose your installation method: * [Next.js Installation](/docs/getting-started/installation/next) * [Vite Installation](/docs/getting-started/installation/vite) * [Manual Installation](/docs/getting-started/installation/manual) After installation, you can explore: * [Theming](/docs/getting-started/theming) * [Dark Mode](/docs/getting-started/dark-mode) * [Components](/docs/components) * [Icons](/icons) ## Inspirations and Dependencies [#inspirations-and-dependencies] The components and snippets here are based on and compatible with the innovative [shadcn/ui](https://ui.shadcn.com/). [Tailwind CSS](https://tailwindcss.com/) is used for styling, and many components are built on [Base UI](https://base-ui.com/) for accessible, dynamic interaction. [exaBase Design System Icons](https://exawizards.com/exabase/icons) are used for the icons. ## License [#license] Most of the content including snippets in this site were taken from [shadcn/ui](https://ui.shadcn.com/). Our own changes and additions are licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). # Accordion (/exabase/design/docs/components/accordion) ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" export function AccordionDemo() { return ( What are your shipping options? We offer standard (5-7 days), express (2-3 days), and overnight shipping. Free shipping on international orders. What is your return policy? Returns accepted within 30 days. Items must be unused and in original packaging. Refunds processed within 5-7 business days. How can I contact customer support? Reach us via email, live chat, or phone. We respond within 24 hours during business days. ) } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/accordion.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/accordion.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/accordion.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/accordion.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion" import { ChevronDownIcon, ChevronUpIcon, } from "@exawizards/exabase-design-system-icons-react" import { cn } from "@/lib/utils" function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { return ( ) } function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { return ( ) } function AccordionTrigger({ className, children, ...props }: AccordionPrimitive.Trigger.Props) { return ( {children} ) } function AccordionContent({ className, children, ...props }: AccordionPrimitive.Panel.Props) { return (
{children}
) } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } ``` ## Usage [#usage] ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" ``` ```tsx Is it accessible? Yes. It adheres to the WAI-ARIA design pattern. ``` ## Examples [#examples] ### Multiple [#multiple] Use the `multiple` prop to allow multiple items to be open at the same time. ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const items = [ { value: "notifications", trigger: "Notification Settings", content: "Manage how you receive notifications. You can enable email alerts for updates or push notifications for mobile devices.", }, { value: "privacy", trigger: "Privacy & Security", content: "Control your privacy settings and security preferences. Enable two-factor authentication, manage connected devices, review active sessions, and configure data sharing preferences. You can also download your data or delete your account.", }, { value: "billing", trigger: "Billing & Subscription", content: "View your current plan, payment history, and upcoming invoices. Update your payment method, change your subscription tier, or cancel your subscription.", }, ] export function AccordionMultiple() { return ( {items.map((item) => ( {item.trigger} {item.content} ))} ) } ``` ### Disabled [#disabled] Use the `disabled` prop on `AccordionItem` to disable individual items. ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" export function AccordionDisabled() { return ( Can I access my account history? Yes, you can view your complete account history including all transactions, plan changes, and support tickets in the Account History section of your dashboard. Premium feature information This section contains information about premium features. Upgrade your plan to access this content. How do I update my email address? You can update your email address in your account settings. You'll receive a verification email at your new address to confirm the change. ) } ``` ### Borders [#borders] ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const items = [ { value: "billing", trigger: "How does billing work?", content: "We offer monthly and annual subscription plans. Billing is charged at the beginning of each cycle, and you can cancel anytime. All plans include automatic backups, 24/7 support, and unlimited team members.", }, { value: "security", trigger: "Is my data secure?", content: "Yes. We use end-to-end encryption, SOC 2 Type II compliance, and regular third-party security audits. All data is encrypted at rest and in transit using industry-standard protocols.", }, { value: "integration", trigger: "What integrations do you support?", content: "We integrate with 500+ popular tools including Slack, Zapier, Salesforce, HubSpot, and more. You can also build custom integrations using our REST API and webhooks.", }, ] export function AccordionBorders() { return ( {items.map((item) => ( {item.trigger} {item.content} ))} ) } ``` ### Card [#card] Wrap the `Accordion` in a `Card` component. ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" const items = [ { value: "plans", trigger: "What subscription plans do you offer?", content: "We offer three subscription tiers: Starter ($9/month), Professional ($29/month), and Enterprise ($99/month). Each plan includes increasing storage limits, API access, priority support, and team collaboration features.", }, { value: "billing", trigger: "How does billing work?", content: "Billing occurs automatically at the start of each billing cycle. We accept all major credit cards, PayPal, and ACH transfers for enterprise customers. You'll receive an invoice via email after each payment.", }, { value: "cancel", trigger: "How do I cancel my subscription?", content: "You can cancel your subscription anytime from your account settings. There are no cancellation fees or penalties. Your access will continue until the end of your current billing period.", }, ] export function AccordionCard() { return ( Subscription & Billing Common questions about your account, plans, payments and cancellations. {items.map((item) => ( {item.trigger} {item.content} ))} ) } ``` ## API [#api] See the [Base UI](https://base-ui.com/react/components/accordion#api-reference) documentation for more information. # Alert Dialog (/exabase/design/docs/components/alert-dialog) ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" export function AlertDialogDemo() { return ( Show Dialog} /> Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ) } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/alert-dialog.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/alert-dialog.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/alert-dialog.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/alert-dialog.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx "use client" import * as React from "react" import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { ScrollArea, ScrollContent } from "@/components/ui/scroll-area" function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { return } function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { return ( ) } function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { return ( ) } function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) { return ( ) } function AlertDialogContent({ className, ...props }: AlertDialogPrimitive.Popup.Props) { return ( ) } function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AlertDialogFooter({ className, showCloseButton = false, children, ...props }: React.ComponentProps<"div"> & { showCloseButton?: boolean }) { return (
{children} {showCloseButton && ( }> Close )}
) } function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) { return ( ) } function AlertDialogDescription({ className, ...props }: AlertDialogPrimitive.Description.Props) { return ( ) } function AlertDialogAction({ className, ...props }: React.ComponentProps) { return ( } /> Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ) } ``` ### Destructive [#destructive] ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" export function AlertDialogDestructive() { return ( Show Dialog} /> Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ) } ``` ## API [#api] See the [Base UI](https://base-ui.com/react/components/alert-dialog#api-reference) documentation for more information. # Alert (/exabase/design/docs/components/alert) ```tsx import { ExclamationmarkCircleFillIcon, InformationmarkCircleIcon, } from "@exawizards/exabase-design-system-icons-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export function AlertDemo() { return (
Heads up! This is an alert with icon, title and description. This Alert has a title and an icon. No description. Unable to process your payment.

Please verify your billing information and try again.

  • Check your card details
  • Ensure sufficient funds
  • Verify billing address
) } ```
## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/alert.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/alert.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/alert.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/alert.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const alertVariants = cva( "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-3 py-2.5 text-sm text-foreground has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", { variants: { variant: { default: "bg-muted/50", info: "border-info-text/50 bg-info-muted *:[svg]:text-info", success: "border-success-text/50 bg-success-muted *:[svg]:text-success", warning: "border-warning-text/50 bg-warning-muted *:[svg]:text-warning", error: "border-destructive-text/50 bg-destructive-muted *:[svg]:text-destructive", }, }, defaultVariants: { variant: "default", }, } ) function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps) { return (
) } function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AlertDescription({ className, ...props }: React.ComponentProps<"div">) { return (
) } export { Alert, AlertTitle, AlertDescription } ``` ## Usage [#usage] ```tsx import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; ``` ```tsx Heads up! You can add components and dependencies to your app using the cli. ``` ## Examples [#examples] ### Variant [#variant] ```tsx import { CheckmarkCircleFillIcon, ExclamationmarkCircleFillIcon, ExclamationmarkTriangleFillIcon, InformationmarkCircleFillIcon, InformationmarkCircleIcon, } from "@exawizards/exabase-design-system-icons-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export function AlertVariant() { return (
Default This is a default alert. Success Your changes have been saved. Info New features are now available. Warning This API will be deprecated soon. Error Something went wrong. Please try again.
) } ```
### Elements [#elements] ```tsx import { InformationmarkCircleIcon } from "@exawizards/exabase-design-system-icons-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export function AlertElements() { return (
Title Title Description Title Title Description
) } ```
### Action [#action] shadcn/ui provides an `AlertAction` component, but we do not provide it due to its lack of layout flexibility. Instead, place actions at any position you like. ```tsx import { InformationmarkCircleIcon } from "@exawizards/exabase-design-system-icons-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" export function AlertWithAction() { return (
Dark mode is now available

Enable it under your profile settings to get started.

Dark mode is now available

Enable it under your profile settings to get started.

) } ```
## API [#api] ### Alert [#alert] `` renders as a `
` element. | Prop | Type | Default | Description | | --------- | ---------------------------------------------------------- | --------- | ------------------------- | | `variant` | `"default" \| "success" \| "info" \| "warning" \| "error"` | `default` | The variant of the alert. | # Aspect Ratio (/exabase/design/docs/components/aspect-ratio) ```tsx import Image from "next/image" import { AspectRatio } from "@/components/ui/aspect-ratio" export function AspectRatioDemo() { return ( Photo by Drew Beamer ) } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/aspect-ratio.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/aspect-ratio.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/aspect-ratio.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/aspect-ratio.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx import { cn } from "@/lib/utils" function AspectRatio({ ratio, className, ...props }: React.ComponentProps<"div"> & { ratio: number }) { return (
) } export { AspectRatio } ``` ## Usage [#usage] ```tsx import Image from "next/image" import { AspectRatio } from "@/components/ui/aspect-ratio" ``` ```tsx
Image
``` ## API [#api] ### AspectRatio [#aspectratio] The `AspectRatio` component displays content within a desired ratio. | Prop | Type | Default | Description | | ------- | -------- | ------- | --------------------------------------------- | | `ratio` | `number` | - | The aspect ratio of the content. **Required** | # Autocomplete (/exabase/design/docs/components/autocomplete) ```tsx import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteDemo() { return ( No items found. {(item) => ( {item.label} )} ) } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/autocomplete.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/autocomplete.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/autocomplete.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/autocomplete.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx // Based on coss ui (https://github.com/cosscom/coss/blob/main/apps/ui/registry/default/ui/autocomplete.tsx) // Copyright (c) coss.com - MIT License "use client" import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete" import { XmarkLargeIcon } from "@exawizards/exabase-design-system-icons-react" import { cn } from "@/lib/utils" import { Input } from "@/components/ui/input" import { ScrollArea } from "@/components/ui/scroll-area" const Autocomplete = AutocompletePrimitive.Root function AutocompleteInput({ className, size, render, ...props }: Omit & React.ComponentProps) { return ( } {...props} /> ) } function AutocompleteContent({ className, children, side = "bottom", sideOffset = 4, alignOffset, align = "start", anchor, ...props }: AutocompletePrimitive.Popup.Props & { align?: AutocompletePrimitive.Positioner.Props["align"] sideOffset?: AutocompletePrimitive.Positioner.Props["sideOffset"] alignOffset?: AutocompletePrimitive.Positioner.Props["alignOffset"] side?: AutocompletePrimitive.Positioner.Props["side"] anchor?: AutocompletePrimitive.Positioner.Props["anchor"] }) { return ( {children} ) } function AutocompleteItem({ className, children, ...props }: AutocompletePrimitive.Item.Props) { return ( {children} ) } function AutocompleteSeparator({ className, ...props }: AutocompletePrimitive.Separator.Props) { return ( ) } function AutocompleteGroup({ ...props }: AutocompletePrimitive.Group.Props) { return ( ) } function AutocompleteLabel({ className, ...props }: AutocompletePrimitive.GroupLabel.Props) { return ( ) } function AutocompleteEmpty({ className, ...props }: AutocompletePrimitive.Empty.Props) { return ( ) } function AutocompleteRow({ ...props }: AutocompletePrimitive.Row.Props) { return } function AutocompleteValue({ ...props }: AutocompletePrimitive.Value.Props) { return ( ) } function AutocompleteList({ className, ...props }: AutocompletePrimitive.List.Props) { return ( ) } function AutocompleteClear({ className, ...props }: AutocompletePrimitive.Clear.Props) { return ( ) } function AutocompleteStatus({ className, ...props }: AutocompletePrimitive.Status.Props) { return ( ) } function AutocompleteCollection({ ...props }: AutocompletePrimitive.Collection.Props) { return ( ) } function AutocompleteTrigger({ className, children, ...props }: AutocompletePrimitive.Trigger.Props) { return ( {children} ) } const useAutocompleteFilter = AutocompletePrimitive.useFilter export { Autocomplete, AutocompleteInput, AutocompleteTrigger, AutocompleteContent, AutocompleteItem, AutocompleteSeparator, AutocompleteGroup, AutocompleteLabel, AutocompleteEmpty, AutocompleteValue, AutocompleteList, AutocompleteClear, AutocompleteStatus, AutocompleteRow, AutocompleteCollection, useAutocompleteFilter, } ``` ## Usage [#usage] ```tsx import { Autocomplete, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteContent, } from "@/components/ui/autocomplete" ``` ```tsx const items = [ { value: "apple", label: "Apple" }, { value: "banana", label: "Banana" }, { value: "orange", label: "Orange" }, { value: "grape", label: "Grape" }, ] No results found. {(item) => {item.label}} ``` ## Examples [#examples] ### Size [#size] ```tsx import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteSize() { return (
No items found. {(item) => ( {item.label} )} No items found. {(item) => ( {item.label} )}
) } ```
### Grouped [#grouped] ```tsx import * as React from "react" import { Autocomplete, AutocompleteCollection, AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteInput, AutocompleteItem, AutocompleteLabel, AutocompleteList, AutocompleteSeparator, AutocompleteValue, } from "@/components/ui/autocomplete" type Group = { value: string items: Item[] } type Item = { id: string value: string category: string } const items: Item[] = [ { id: "react", value: "React", category: "Frontend Frameworks" }, { id: "vue", value: "Vue.js", category: "Frontend Frameworks" }, { id: "angular", value: "Angular", category: "Frontend Frameworks" }, { id: "svelte", value: "Svelte", category: "Frontend Frameworks" }, { id: "nodejs", value: "Node.js", category: "Backend Runtime" }, { id: "deno", value: "Deno", category: "Backend Runtime" }, { id: "bun", value: "Bun", category: "Backend Runtime" }, { id: "express", value: "Express.js", category: "Backend Frameworks" }, { id: "fastify", value: "Fastify", category: "Backend Frameworks" }, { id: "nestjs", value: "NestJS", category: "Backend Frameworks" }, { id: "nextjs", value: "Next.js", category: "Meta Frameworks" }, { id: "nuxt", value: "Nuxt.js", category: "Meta Frameworks" }, { id: "remix", value: "Remix", category: "Meta Frameworks" }, ] const order = [ "Frontend Frameworks", "Backend Runtime", "Backend Frameworks", "Meta Frameworks", ] export function AutocompleteGrouped() { const groupedItems = React.useMemo(() => { const groups: { [key: string]: Item[] } = items.reduce( (acc, item) => { acc[item.category] = acc[item.category] || [] acc[item.category].push(item) return acc }, {} as { [key: string]: Item[] } ) return order.map((value) => ({ value, items: groups[value] ?? [] })) }, []) return ( No items found for " " {(group: Group, index: number) => ( {index > 0 && } {group.value} {(item: Item) => ( {item.value} )} )} ) } ``` ### Disabled [#disabled] ```tsx import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteDisabled() { return ( No items found. {(item) => ( {item.label} )} ) } ``` ### Inline Autocomplete [#inline-autocomplete] Autofill the input with the highlighted item while navigating with arrow keys using the `mode` prop. Accepts `aria-autocomplete` values `list`, `both`, `inline`, or `none`. ```tsx import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteInline() { return ( No items found. {(item) => ( {item.label} )} ) } ``` ### Auto Highlight [#auto-highlight] Automatically highlight the first matching item. ```tsx import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteAutoHighlight() { return ( No items found. {(item) => ( {item.label} )} ) } ``` ### Input Group [#input-group] ```tsx import React from "react" import { MagnifyingglassIcon } from "@exawizards/exabase-design-system-icons-react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, } from "@/components/ui/autocomplete" import { InputGroup, InputGroupAddon, InputGroupInput, } from "@/components/ui/input-group" const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, { label: "Orange", value: "orange" }, { label: "Grape", value: "grape" }, { label: "Strawberry", value: "strawberry" }, { label: "Mango", value: "mango" }, { label: "Pineapple", value: "pineapple" }, { label: "Kiwi", value: "kiwi" }, { label: "Peach", value: "peach" }, { label: "Pear", value: "pear" }, ] export function AutocompleteInputGroup() { const anchorRef = React.useRef(null) return ( } /> No items found. {(item) => ( {item.label} )} ) } ``` ### Limit Results [#limit-results] ```tsx "use client" import React, { useState } from "react" import { Autocomplete, AutocompleteContent, AutocompleteEmpty, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompleteStatus, useAutocompleteFilter, } from "@/components/ui/autocomplete" const limit = 8 type Tag = { id: string; value: string } const tags: Tag[] = [ { id: "t1", value: "feature" }, { id: "t2", value: "fix" }, { id: "t3", value: "bug" }, { id: "t4", value: "docs" }, { id: "t5", value: "internal" }, { id: "t6", value: "mobile" }, { id: "t7", value: "frontend" }, { id: "t8", value: "backend" }, { id: "t9", value: "performance" }, { id: "t10", value: "accessibility" }, { id: "t11", value: "design" }, { id: "t12", value: "research" }, { id: "t13", value: "testing" }, { id: "t14", value: "infrastructure" }, { id: "t15", value: "documentation" }, { id: "c-accordion", value: "component: accordion" }, { id: "c-alert-dialog", value: "component: alert dialog" }, { id: "c-autocomplete", value: "component: autocomplete" }, { id: "c-avatar", value: "component: avatar" }, { id: "c-checkbox", value: "component: checkbox" }, { id: "c-checkbox-group", value: "component: checkbox group" }, { id: "c-collapsible", value: "component: collapsible" }, { id: "c-combobox", value: "component: combobox" }, { id: "c-context-menu", value: "component: context menu" }, { id: "c-dialog", value: "component: dialog" }, { id: "c-field", value: "component: field" }, { id: "c-fieldset", value: "component: fieldset" }, { id: "c-filterable-menu", value: "component: filterable menu" }, { id: "c-form", value: "component: form" }, { id: "c-input", value: "component: input" }, { id: "c-menu", value: "component: menu" }, { id: "c-menubar", value: "component: menubar" }, { id: "c-meter", value: "component: meter" }, { id: "c-navigation-menu", value: "component: navigation menu" }, { id: "c-number-field", value: "component: number field" }, { id: "c-popover", value: "component: popover" }, { id: "c-preview-card", value: "component: preview card" }, { id: "c-progress", value: "component: progress" }, { id: "c-radio", value: "component: radio" }, { id: "c-scroll-area", value: "component: scroll area" }, { id: "c-select", value: "component: select" }, { id: "c-separator", value: "component: separator" }, { id: "c-slider", value: "component: slider" }, { id: "c-switch", value: "component: switch" }, { id: "c-tabs", value: "component: tabs" }, { id: "c-toast", value: "component: toast" }, { id: "c-toggle", value: "component: toggle" }, { id: "c-toggle-group", value: "component: toggle group" }, { id: "c-toolbar", value: "component: toolbar" }, { id: "c-tooltip", value: "component: tooltip" }, ] export function AutocompleteLimit() { const [value, setValue] = useState("") const { contains } = useAutocompleteFilter({ sensitivity: "base" }) const totalMatches = React.useMemo(() => { const trimmed = value.trim() if (!trimmed) { return tags.length } return tags.filter((t) => contains(t.value, trimmed)).length }, [value, contains]) const moreCount = Math.max(0, totalMatches - limit) return ( No tags found. {(tag: Tag) => ( {tag.value} )} {moreCount > 0 && ( +{moreCount} more )} ) } ``` ## API [#api] See the [Base UI](https://base-ui.com/react/components/autocomplete#api-reference) documentation for more information. ### AutocompleteInput [#autocompleteinput] | Prop | Type | Default | Description | | ------ | ------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `size` | `"xs" \| "sm" \| "default" \| number` | `"default"` | When `xs`, `sm`, or `default` is specified, the size variant is applied. When a number is specified, it is reflected in the native input's `size` attribute. | # Avatar (/exabase/design/docs/components/avatar) ```tsx import { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, } from "@/components/ui/avatar" export function AvatarDemo() { return (
JM JM JM JM DC EP +3
) } ```
## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/avatar.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/avatar.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/avatar.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/avatar.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx "use client" import * as React from "react" import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar" import { mergeProps } from "@base-ui/react/merge-props" import { useRender } from "@base-ui/react/use-render" import { cn } from "@/lib/utils" function Avatar({ className, size = "default", ...props }: AvatarPrimitive.Root.Props & { size?: "default" | "sm" | "lg" }) { return ( ) } function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) { return ( ) } function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) { return ( ) } function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { return ( svg]:hidden", "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", className )} {...props} /> ) } function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AvatarGroupCount({ className, render, ...props }: useRender.ComponentProps<"div">) { return useRender({ defaultTagName: "div", props: mergeProps<"div">( { className: cn( "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", className ), }, props ), render, state: { slot: "avatar-group-count", }, }) } export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge, } ``` ## Usage [#usage] ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" ``` ```tsx EW ``` ## Examples [#examples] ### Image [#image] An avatar component with an image and a fallback. ```tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export function AvatarImageExample() { return ( JM ) } ``` ### Text or Icon [#text-or-icon] If you don't use an image, you can place text or an icon directly as a child of the Avatar. ```tsx import { PersonCircleFillIcon } from "@exawizards/exabase-design-system-icons-react" import { Avatar } from "@/components/ui/avatar" export function AvatarText() { return (
JM
) } ```
### Badge [#badge] Use the `AvatarBadge` component to add a badge to the avatar. The badge is positioned at the bottom right of the avatar. ```tsx import { PlusIcon } from "@exawizards/exabase-design-system-icons-react" import { Avatar, AvatarBadge, AvatarFallback, AvatarImage, } from "@/components/ui/avatar" export function AvatarBadgeExample() { return (
JM JM JM JM
) } ```
### Avatar Group [#avatar-group] Use the `AvatarGroup` component to add a group of avatars. ```tsx import { Avatar, AvatarFallback, AvatarGroup, AvatarImage, } from "@/components/ui/avatar" export function AvatarGroupExample() { return ( JM DC EP ) } ``` ### Avatar Group Count [#avatar-group-count] Use `` to add an extra content to the group. ```tsx import { PlusIcon } from "@exawizards/exabase-design-system-icons-react" import { Avatar, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" export function AvatarGroupCountExample() { return (
JM DC EP +3 JM DC EP }>
) } ```
### Size [#size] Use the `size` prop to change the size of the avatar. ```tsx import { Avatar, AvatarFallback, AvatarGroup, AvatarImage, } from "@/components/ui/avatar" export function AvatarSizeExample() { return (
JM JM JM JM DC EP
) } ```
### Dropdown [#dropdown] You can use the `Avatar` component as a trigger for a dropdown menu. ```tsx "use client" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" export function AvatarDropdown() { return ( JM } /> Profile Billing Settings Log out ) } ``` ### Customize [#customize] ```tsx import { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarImage, } from "@/components/ui/avatar" export function AvatarCustomize() { return (
JM JM JM JM DC EP
) } ```
## API [#api] ### Avatar [#avatar] The `Avatar` component is the root component that wraps the avatar image and fallback. | Prop | Type | Default | | ------ | --------------------------- | ----------- | | `size` | `"default" \| "sm" \| "lg"` | `"default"` | See the [Base UI](https://base-ui.com/react/components/avatar#api-reference) documentation for more information. # Badge (/exabase/design/docs/components/badge) ```tsx import { Badge } from "@/components/ui/badge" export function BadgeDemo() { return Badge } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/badge.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/badge.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/badge.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/badge.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx import { mergeProps } from "@base-ui/react/merge-props" import { useRender } from "@base-ui/react/use-render" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const badgeVariants = cva( "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:pointer-events-none [&>svg]:size-3!", { variants: { variant: { default: "bg-primary text-primary-foreground", secondary: "bg-secondary text-secondary-foreground", info: "bg-info text-info-foreground", success: "bg-success text-success-foreground", warning: "bg-warning text-warning-foreground", error: "bg-destructive text-destructive-foreground", outline: "border-border bg-background text-foreground", }, }, defaultVariants: { variant: "default", }, } ) function Badge({ className, variant = "default", render, ...props }: useRender.ComponentProps<"span"> & VariantProps) { return useRender({ defaultTagName: "span", props: mergeProps<"span">( { className: cn(badgeVariants({ variant }), className), }, props ), render, state: { slot: "badge", variant, }, }) } export { Badge, badgeVariants } ``` ## Usage [#usage] ```tsx import { Badge } from "@/components/ui/badge" ``` ```tsx Badge ``` ### Link [#link] Use the `render` prop to render a link as a badge. ```tsx import Link from "next/link" import { Badge } from "@/components/ui/badge" export function BadgeLink() { return (
Default} /> Outline} variant="outline" /> Secondary} variant="secondary" /> Info} variant="info" /> Success} variant="success" /> Warning} variant="warning" /> Error} variant="error" /> Custom} className="bg-purple-500" />
) } ```
## Examples [#examples] ### Variant [#variant] ```tsx import { Badge } from "@/components/ui/badge" export function BadgeVariant() { return (
Default Outline Secondary Info Success Warning Error Custom
) } ```
### Count [#count] ```tsx import { BellIcon, MailIcon, } from "@exawizards/exabase-design-system-icons-react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" export function BadgeCount() { return (
) } ```
### Dot [#dot] ```tsx import { BellIcon } from "@exawizards/exabase-design-system-icons-react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" export function BadgeDot() { return (
) } ```
### With Icon [#with-icon] You can render an icon inside the badge. Use `data-icon="inline-start"` to render the icon on the left and `data-icon="inline-end"` to render the icon on the right. ```tsx import { CheckmarkIcon, RectangleArrowUpRightIcon, } from "@exawizards/exabase-design-system-icons-react" import { Badge } from "@/components/ui/badge" import { Spinner } from "@/components/ui/spinner" export function BadgeWithIcon() { return (
Verified Document Generating
) } ```
## API [#api] ### Badge [#badge] | Prop | Type | Default | Description | | --------- | -------------------------------------------------------------------------------------- | --------- | ------------------------- | | `variant` | `"default" \| "outline" \| "secondary" \| "info" \| "success" \| "warning" \| "error"` | `default` | The variant of the badge. | # Breadcrumb (/exabase/design/docs/components/breadcrumb) ```tsx import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb" export function BreadcrumbDemo() { return ( Home Components Breadcrumb ) } ``` ## Installation [#installation] ### CLI [#cli] npm pnpm yarn bun ```bash npx shadcn@latest add https://exawizards.com/exabase/design/registry/breadcrumb.json ``` ```bash pnpm dlx shadcn@latest add https://exawizards.com/exabase/design/registry/breadcrumb.json ``` ```bash yarn dlx shadcn@latest add https://exawizards.com/exabase/design/registry/breadcrumb.json ``` ```bash bun x shadcn@latest add https://exawizards.com/exabase/design/registry/breadcrumb.json ``` ### Manual [#manual] Copy and paste the following code into your project. ```tsx import * as React from "react" import { mergeProps } from "@base-ui/react/merge-props" import { useRender } from "@base-ui/react/use-render" import { ChevronRightIcon, DotsHorizontalIcon, } from "@exawizards/exabase-design-system-icons-react" import { cn } from "@/lib/utils" function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) { return (