# All Components **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/index.mdx > Explore the full list of components available in the library. More are on the way. ## Buttons ## Collections ## Colors ## Controls ## Data Display ## Date and Time ## Feedback ## Forms ## Layout ## Media ## Navigation ## Overlays ## Pickers ## Typography ## Utilities # Introduction **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/getting-started **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/getting-started/index.mdx > An open-source UI component library for building beautiful and accessible user interfaces. DarkCode UI is a React component library built on [Tailwind CSS v4](https://tailwindcss.com/) and [React Aria Components](https://react-spectrum.adobe.com/react-aria/index.html). Every component comes with smooth animations, polished details, and built-in accessibility—ready to use, fully customizable. ## Why DarkCode UI? **Beautiful by default** — Professional look out of the box, no extra styling needed. **Accessible** — Built on [React Aria Components](https://react-spectrum.adobe.com/react-aria/components.html) with focus management, keyboard navigation, and screen reader support. **Flexible** — Each component is made of customizable parts. Change what you need, leave the rest. **Developer-friendly** — Fully typed APIs, predictable patterns, and excellent autocompletion. **Maintained** — We handle updates, bug fixes, and new features. Just update the package. **Lightweight** — Tree-shaken. Only what you use goes into your app. **Future-proof** — Built for [React 19](https://react.dev/blog/2024/12/05/react-19) and [Tailwind v4](https://tailwindcss.com/blog/tailwindcss-v4), designed for AI-assisted development. ## A Living Library, Not Copy-Paste Copy-paste code works until it breaks. You're left maintaining outdated dependencies that stop evolving. DarkCode UI is different. It's a living library that grows with you: * Automatic updates and fixes * New features without extra work * Components stay current with React, Tailwind, and browsers * Deep customization, not shallow theme tweaks * AI-friendly APIs for code generation DarkCode UI is not a snapshot—it's a garden that keeps growing. 🌱 ## DarkCode UI Ecosystem * **🌐 DarkCode UI** (web) — You're here! React components with Tailwind CSS v4 * **🤖 [DarkCode UI Chat](https://darkcode-ui.chat?ref=darkcode-ui)** (text-to-app) — Create apps with natural language * **🧠 UI for LLMs** — New platform & MCPs coming soon **Why React Aria?** We chose React Aria for accessibility at scale. DarkCode UI keeps familiar API conventions like `isDisabled` and `onPress`. Thanks to [Devon Govett](https://x.com/devongovett) and the Adobe team. ## FAQ **Is DarkCode UI free?** Yes, completely free and open source under the Apache License 2.0. **Is it production-ready?** Yes. DarkCode UI is stable and ready for production use. **Can I customize the components?** Yes! Use Tailwind utilities, CSS variables, [BEM](https://getbem.com/) modifiers, or compose component parts differently. Every slot is customizable. **Does it work with TypeScript?** Fully typed with excellent IDE support and autocompletion. **What about accessibility?** Built on React Aria Components for WCAG compliance. Keyboard navigation, focus management, and screen reader support included. **Can I use the styles without React?** Yes, the CSS can be applied to plain HTML. See our [Tailwind Play example](https://play.tailwindcss.com/vMYXzKPyUx). ## Get Involved Join the community, share feedback, or contribute: * [GitHub Discussions](https://github.com/DarkCode-Developers/darkcode-ui/discussions) * [Contributing Guidelines](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/CONTRIBUTING.md) DarkCode UI is released under the [Apache License 2.0](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/LICENSE). # All Releases **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/releases **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/releases/index.mdx > All updates and changes to DarkCode UI, including new features, fixes, and breaking changes. **Using AI assistants?** Simply prompt "Hey Cursor, update DarkCode UI to the latest version" and your AI assistant will automatically compare versions and apply the necessary changes. Learn more about the [DarkCode UI MCP Server](/docs/react/getting-started/mcp-server). ## Latest Release ### v1.0.0 **June 2026** The first stable release of DarkCode UI — a modern React component library built on React Aria and Tailwind CSS v4 with compound components, oklch design tokens, and AI-ready documentation. [Read full release notes →](/docs/react/releases/v1-0-0) # Introducing DarkCode UI v1.0.0 **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/releases/v1-0-0 **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/releases/v1-0-0.mdx > The first stable release of DarkCode UI — beautiful, accessible React components built on React Aria and Tailwind CSS v4. ## DarkCode UI is here DarkCode UI v1.0.0 is the first public release of our React component library. It ships a complete design system, compound components for maximum flexibility, and first-class support for light and dark themes. ## Highlights * **Compound components** — compose every internal piece independently (`Card.Header`, `Modal.Content`, and more) * **React Aria foundation** — accessible by default with keyboard, screen reader, and focus management built in * **Tailwind CSS v4** — CSS-based theming with oklch design tokens and BEM component classes * **No provider required** — import components and start building * **AI-ready docs** — MCP server, agent skills, and `llms.txt` for coding assistants ## Packages Install the core packages: ```bash npm install @darkcode-ui/react @darkcode-ui/styles ``` ## Resources * [Quick start](/docs/react/getting-started/quick-start) * [Documentation](https://ui.darkcode.dev) * [Storybook](https://storybook.ui.darkcode.dev) * [GitHub](https://github.com/DarkCode-Developers/darkcode-ui) ## What's next Future releases follow semantic versioning with regular minor and patch updates. Track changes on the [releases page](/docs/react/releases). # ButtonGroup **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/button-group **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(buttons)/button-group.mdx > Group related buttons together with consistent styling and spacing ## Import ```tsx import { ButtonGroup, Button } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {Button, ButtonGroup, Chip, Description, Dropdown, Label} from "@darkcode-ui/react"; import { ChevronDown, ChevronLeft, ChevronRight, CodeFork, Ellipsis, Picture, Pin, QrCode, Star, TextAlignCenter, TextAlignJustify, TextAlignLeft, TextAlignRight, ThumbsDown, ThumbsUp, Video, } from "@gravity-ui/icons"; export function Basic() { return (
{/* Single button with dropdown */}
All commits from this branch will be added to the base branch The 14 commits from this branch will be combined into one commit in the base branch The 14 commits from this branch will be rebased and added to the base branch
{/* Individual buttons */}
{/* Previous/Next Button Group */}
{/* Content Selection Button Group */}
{/* Text Alignment Button Group */}
{/* Icon-Only Alignment Button Group */}
); } ``` ### Anatomy Import the ButtonGroup component and access all parts using dot notation. ```tsx import { ButtonGroup, Button } from '@darkcode-ui/react'; export default () => ( ); ``` > **ButtonGroup** wraps multiple Button components together, applying consistent styling, spacing, and automatic border radius handling. It uses React Context to pass `size`, `variant`, and `isDisabled` props to all child buttons. ### Variants ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; export function Variants() { return (

Primary

Secondary

Tertiary

Outline

Ghost

Danger

); } ``` ### Sizes ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; export function Sizes() { return (

Small

Medium (default)

Large

); } ``` ### Orientation Use the `orientation` prop to arrange buttons horizontally or vertically. ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; import {TextAlignCenter, TextAlignJustify, TextAlignLeft, TextAlignRight} from "@gravity-ui/icons"; export function Orientation() { return (
Horizontal
Vertical
); } ``` ### With Icons ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; import {Globe, Plus, TrashBin} from "@gravity-ui/icons"; export function WithIcons() { return (

With icons

Icon only buttons

); } ``` ### Full Width ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; import {TextAlignCenter, TextAlignLeft, TextAlignRight} from "@gravity-ui/icons"; export function FullWidth() { return (
); } ``` ### Disabled State ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; export function Disabled() { return (

All buttons disabled

Group disabled, but one button overrides

); } ``` ### Without Separator Simply omit the `` component from your buttons. ```tsx import {Button, ButtonGroup} from "@darkcode-ui/react"; export function WithoutSeparator() { return ( ); } ``` ## Related Components * **Button**: Allows a user to perform an action * **Dropdown**: Context menu with actions and options * **Chip**: Compact elements for tags and filters ## Styling ### Passing Tailwind CSS classes ```tsx import { ButtonGroup, Button } from '@darkcode-ui/react'; function CustomButtonGroup() { return ( ); } ``` ### Customizing the component classes To customize the ButtonGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .button-group { @apply gap-2 rounded-lg; } .button-group__separator { @apply opacity-25; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ButtonGroup component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/button-group.css)): #### Base Classes * `.button-group` - Base button group container * `.button-group--full-width` - Full width modifier * `.button-group__separator` - Separator element between buttons The ButtonGroup component automatically applies border radius to buttons: * First button gets rounded left/start edge * Last button gets rounded right/end edge * Middle buttons have no border radius * Single button gets full border radius on all edges Add `` inside each Button (except the first) to show dividers between buttons. ## API Reference ### ButtonGroup Props | Prop | Type | Default | Description | | ------------- | --------------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------- | | `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'ghost' \| 'danger'` | - | Visual style variant applied to all buttons in the group | | `size` | `'sm' \| 'md' \| 'lg'` | - | Size applied to all buttons in the group | | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | The orientation of the button group | | `fullWidth` | `boolean` | `false` | Whether the button group should take full width of its container | | `isDisabled` | `boolean` | `false` | Whether all buttons in the group are disabled (can be overridden on individual buttons) | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Button components to group together | ### ButtonGroup.Separator Props | Prop | Type | Default | Description | | ----------- | -------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | ### Notes * ButtonGroup uses React Context to pass `size`, `variant`, and `isDisabled` props to all child Button components * **Only direct child buttons receive the ButtonGroup props** - Buttons nested inside other components (like Modal, Dropdown, etc.) will not inherit the group's props even if they are descendants of the ButtonGroup * Individual Button components can override the group's `isDisabled` prop by setting `isDisabled={false}` * The component automatically handles border radius between buttons * Add `` inside each Button (except the first) to show dividers between buttons * Buttons in a group have their active/pressed scale transform removed for a more cohesive appearance
# Button **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/button **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(buttons)/button.mdx > A clickable button component with multiple variants and states ## Import ```tsx import { Button } from '@darkcode-ui/react'; ``` ### Usage ```tsx "use client"; import {Button} from "@darkcode-ui/react"; export function Basic() { return ; } ``` ### Variants ```tsx import {Button} from "@darkcode-ui/react"; export function Variants() { return (
); } ``` ### With Icons ```tsx import {Button} from "@darkcode-ui/react"; import {Envelope, Globe, Plus, TrashBin} from "@gravity-ui/icons"; export function WithIcons() { return (
); } ``` ### Icon Only ```tsx import {Button} from "@darkcode-ui/react"; import {Ellipsis, Gear, TrashBin} from "@gravity-ui/icons"; export function IconOnly() { return (
); } ``` ### Loading ```tsx "use client"; import {Button, Spinner} from "@darkcode-ui/react"; import React from "react"; export function Loading() { return ( ); } ``` ### Loading State ```tsx "use client"; import {Button, Spinner} from "@darkcode-ui/react"; import {Paperclip} from "@gravity-ui/icons"; import React, {useState} from "react"; export function LoadingState() { const [isLoading, setLoading] = useState(false); const handlePress = () => { setLoading(true); setTimeout(() => setLoading(false), 2000); }; return ( ); } ``` ### Sizes ```tsx import {Button} from "@darkcode-ui/react"; export function Sizes() { return (
); } ``` ### Full Width ```tsx import {Button} from "@darkcode-ui/react"; import {Plus} from "@gravity-ui/icons"; export function FullWidth() { return (
); } ``` ### Disabled State ```tsx import {Button} from "@darkcode-ui/react"; export function Disabled() { return (
); } ``` ### Social Buttons ```tsx import {Button} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; export function Social() { return (
); } ``` ### Custom Render Function ```tsx "use client"; import {Button} from "@darkcode-ui/react"; export function CustomRenderFunction() { return ( ); } ``` ## Related Components * **Popover**: Displays content in context with a trigger * **Tooltip**: Contextual information on hover or focus * **Form**: Form validation and submission handling ## Styling ### Passing Tailwind CSS classes ```tsx import { Button } from '@darkcode-ui/react'; function CustomButton() { return ( ); } ``` ### Customizing the component classes To customize the Button component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .button { @apply bg-purple-500 text-white hover:bg-purple-600; } .button--icon-only { @apply rounded-lg bg-blue-500; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### Adding custom variants You can extend DarkCode UI components by wrapping them and adding your own custom variants. ```tsx import type {ButtonProps} from "@darkcode-ui/react"; import type {VariantProps} from "tailwind-variants"; import {Button, buttonVariants} from "@darkcode-ui/react"; import {tv} from "tailwind-variants"; const myButtonVariants = tv({ base: "text-md font-semibold shadow-md text-shadow-lg data-[pending=true]:opacity-40", defaultVariants: { radius: "full", variant: "primary", }, extend: buttonVariants, variants: { radius: { full: "rounded-full", lg: "rounded-lg", md: "rounded-md", sm: "rounded-sm", }, size: { lg: "h-12 px-8", md: "h-11 px-6", sm: "h-10 px-4", xl: "h-13 px-10", }, variant: { primary: "text-white dark:bg-white/10 dark:text-white dark:hover:bg-white/15", }, }, }); type MyButtonVariants = VariantProps; export type MyButtonProps = Omit & MyButtonVariants & {className?: string}; function CustomButton({className, radius, variant, ...props}: MyButtonProps) { return ); } ``` ### CSS Classes The Button component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/button.css)): #### Base & Size Classes * `.button` - Base button styles * `.button--sm` - Small size variant * `.button--md` - Medium size variant * `.button--lg` - Large size variant #### Variant Classes * `.button--primary` * `.button--secondary` * `.button--tertiary` * `.button--outline` * `.button--ghost` * `.button--danger` #### Modifier Classes * `.button--icon-only` * `.button--icon-only.button--sm` * `.button--icon-only.button--lg` ### Interactive States The button supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` * **Active/Pressed**: `:active` or `[data-pressed="true"]` (includes scale transform) * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring) * **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events) * **Pending**: `[data-pending]` (no pointer events during loading) ## API Reference ### Button Props | Prop | Type | Default | Description | | ------------ | ---------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | | `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'outline' \| 'ghost' \| 'danger'` | `'primary'` | Visual style variant | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the button | | `fullWidth` | `boolean` | `false` | Whether the button should take full width of its container | | `isDisabled` | `boolean` | `false` | Whether the button is disabled | | `isPending` | `boolean` | `false` | Whether the button is in a loading state | | `isIconOnly` | `boolean` | `false` | Whether the button contains only an icon | | `onPress` | `(e: PressEvent) => void` | - | Handler called when the button is pressed | | `children` | `React.ReactNode \| (values: ButtonRenderProps) => React.ReactNode` | - | Button content or render prop | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ButtonRenderProps When using the render prop pattern, these values are provided: | Prop | Type | Description | | ---------------- | --------- | ---------------------------------------------- | | `isPending` | `boolean` | Whether the button is in a loading state | | `isPressed` | `boolean` | Whether the button is currently pressed | | `isHovered` | `boolean` | Whether the button is hovered | | `isFocused` | `boolean` | Whether the button is focused | | `isFocusVisible` | `boolean` | Whether the button should show focus indicator | | `isDisabled` | `boolean` | Whether the button is disabled |
# CloseButton **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/close-button **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(buttons)/close-button.mdx > Button component for closing dialogs, modals, or dismissing content ## Import ```tsx import { CloseButton } from "@darkcode-ui/react"; ``` ### Usage ```tsx import {CloseButton} from "@darkcode-ui/react"; export function Default() { return ; } ``` ### With Custom Icon ```tsx import {CloseButton} from "@darkcode-ui/react"; import {CircleXmark, Xmark} from "@gravity-ui/icons"; export function WithCustomIcon() { return (
Custom Icon
Alternative Icon
); } ``` ### Interactive ```tsx "use client"; import {CloseButton} from "@darkcode-ui/react"; import {useState} from "react"; export function Interactive() { const [count, setCount] = useState(0); return (
setCount(count + 1)} /> Clicked: {count} times
); } ``` ## Related Components * **Alert**: Display important messages and notifications * **AlertDialog**: Critical confirmations requiring user attention * **Chip**: Compact elements for tags and filters ## Styling ### Passing Tailwind CSS classes ```tsx import {CloseButton} from "@darkcode-ui/react"; function CustomCloseButton() { return Close; } ``` ### Customizing the component classes To customize the CloseButton component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .close-button { @apply bg-red-100 text-red-800 hover:bg-red-200; } .close-button--custom { @apply rounded-full border-2 border-red-300; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The CloseButton component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/close-button.css)): #### Base Classes * `.close-button` - Base component styles #### Variant Classes * `.close-button--default` - Default variant ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` * **Active/Pressed**: `:active` or `[data-pressed="true"]` * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` * **Disabled**: `:disabled` or `[aria-disabled="true"]` ## API Reference ### CloseButton Props | Prop | Type | Default | Description | | ------------ | ----------------------- | --------------- | ------------------------------------------- | | `variant` | `"default"` | `"default"` | Visual variant of the button | | `children` | `ReactNode \| function` | `` | Content to display (defaults to close icon) | | `onPress` | `() => void` | - | Handler called when the button is pressed | | `isDisabled` | `boolean` | `false` | Whether the button is disabled | ### React Aria Button Props CloseButton extends all React Aria Button props. Common props include: | Prop | Type | Description | | ------------------ | -------- | --------------------------------------- | | `aria-label` | `string` | Accessible label for screen readers | | `aria-labelledby` | `string` | ID of element that labels the button | | `aria-describedby` | `string` | ID of element that describes the button | ### RenderProps When using the render prop pattern, these values are provided: | Prop | Type | Description | | ------------ | --------- | ------------------------------ | | `isHovered` | `boolean` | Whether the button is hovered | | `isPressed` | `boolean` | Whether the button is pressed | | `isFocused` | `boolean` | Whether the button is focused | | `isDisabled` | `boolean` | Whether the button is disabled |
# ToggleButtonGroup **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/toggle-button-group **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(buttons)/toggle-button-group.mdx > Groups multiple ToggleButtons into a unified control, allowing users to select one or multiple options. ## Import ```tsx import { ToggleButtonGroup, ToggleButton } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons"; export function Basic() { return ( ); } ``` ### Anatomy Import the ToggleButtonGroup component and access all parts using dot notation. ```tsx import { ToggleButtonGroup, ToggleButton } from '@darkcode-ui/react'; export default () => ( First Second Third ); ``` ### Sizes ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons"; export function Sizes() { return (
Small
Medium (default)
Large
); } ``` ### Orientation ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Underline} from "@gravity-ui/icons"; export function Orientation() { return (
Horizontal
Vertical
); } ``` ### Detached Use `isDetached` to separate buttons with gaps instead of connecting them. ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons"; export function Attached() { return (
Attached (default)
Detached
); } ``` ### Full Width ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import { Bold, Italic, Strikethrough, TextAlignCenter, TextAlignLeft, TextAlignRight, Underline, } from "@gravity-ui/icons"; export function FullWidth() { return (
Left Center Right
); } ``` ### Selection Mode Use `selectionMode="single"` for mutually exclusive choices or `selectionMode="multiple"` for independent toggles. ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import { Bold, Italic, Strikethrough, TextAlignCenter, TextAlignLeft, TextAlignRight, Underline, } from "@gravity-ui/icons"; export function SelectionMode() { return (
Single selection Left Center Right
Multiple selection
); } ``` ### Controlled ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons"; import {useState} from "react"; export function Controlled() { const [selectedKeys, setSelectedKeys] = useState(new Set(["bold"])); return (

Selected:{" "} {selectedKeys.size > 0 ? [...selectedKeys].join(", ") : "None"}

); } ``` ### Disabled ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Underline} from "@gravity-ui/icons"; export function Disabled() { return (
All buttons disabled
Individual button disabled
); } ``` ### Without Separator Simply omit the `` component from your buttons. ```tsx import {ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Bold, Italic, Strikethrough, Underline} from "@gravity-ui/icons"; export function WithoutSeparator() { return ( ); } ``` ## Related Components * **ToggleButton**: Interactive toggle control for on/off states * **ButtonGroup**: Group related buttons together * **Button**: Allows a user to perform an action ## Styling ### Passing Tailwind CSS classes ```tsx import { ToggleButtonGroup, ToggleButton } from '@darkcode-ui/react'; function CustomToggleButtonGroup() { return ( Option A Option B ); } ``` ### Customizing the component classes To customize the ToggleButtonGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .toggle-button-group { @apply rounded-lg; } .toggle-button-group__separator { @apply opacity-25; } .toggle-button-group--full-width { @apply w-full; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ToggleButtonGroup component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/toggle-button-group.css)): #### Base & Layout Classes * `.toggle-button-group` - Base container styles * `.toggle-button-group--horizontal` - Horizontal orientation * `.toggle-button-group--vertical` - Vertical orientation * `.toggle-button-group--full-width` - Full width modifier * `.toggle-button-group__separator` - Separator element between buttons #### Modifier Classes * `.toggle-button-group--detached` - Detached mode (separated buttons with gaps) ## API Reference ### ToggleButtonGroup Props Inherits from [React Aria ToggleButtonGroup](https://react-aria.adobe.com/ToggleButtonGroup). | Prop | Type | Default | Description | | ------------------------ | ---------------------------- | -------------- | ------------------------------------------------ | | `selectionMode` | `"single" \| "multiple"` | `"single"` | Whether one or multiple buttons can be selected | | `selectedKeys` | `Iterable` | - | Controlled selection state | | `defaultSelectedKeys` | `Iterable` | - | Default selected keys (uncontrolled) | | `onSelectionChange` | `(keys: Set) => void` | - | Called when selection changes | | `disallowEmptySelection` | `boolean` | `false` | Prevents clearing all selections | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size propagated to child ToggleButtons | | `isDetached` | `boolean` | `false` | Whether buttons are visually separated with gaps | | `fullWidth` | `boolean` | `false` | Whether the group fills available width | | `isDisabled` | `boolean` | `false` | Disables all buttons in the group | | `className` | `string` | - | Additional CSS classes | ### ToggleButtonGroup.Separator Props | Prop | Type | Default | Description | | ----------- | -------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | ### Notes * ToggleButtonGroup uses React Context to pass `size` to all child ToggleButton components * Each ToggleButton must have a unique `id` prop that corresponds to the keys used in `selectedKeys` / `defaultSelectedKeys` * The `isDisabled` prop is handled natively by React Aria and disables all child ToggleButtons — individual buttons can override this by setting `isDisabled={false}` * The component automatically handles border radius between buttons * Add `` inside each ToggleButton (except the first) to show dividers between buttons * Use `disallowEmptySelection` with `selectionMode="single"` to ensure one option is always selected
# ToggleButton **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/toggle-button **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(buttons)/toggle-button.mdx > An interactive toggle control for on/off or selected/unselected states ## Import ```tsx import { ToggleButton } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ToggleButton} from "@darkcode-ui/react"; import {Heart} from "@gravity-ui/icons"; export function Basic() { return ( Like ); } ``` ### Variants ```tsx import {ToggleButton} from "@darkcode-ui/react"; import {Heart} from "@gravity-ui/icons"; export function Variants() { return (
Default Ghost
); } ``` ### Icon Only ```tsx import {ToggleButton} from "@darkcode-ui/react"; import {Bookmark, Heart} from "@gravity-ui/icons"; export function IconOnly() { return (
); } ``` ### Sizes ```tsx import {ToggleButton} from "@darkcode-ui/react"; import {Heart} from "@gravity-ui/icons"; export function Sizes() { return (
Small Medium Large
); } ``` ### Controlled ```tsx "use client"; import {ToggleButton} from "@darkcode-ui/react"; import {Heart, HeartFill} from "@gravity-ui/icons"; import {useState} from "react"; export function Controlled() { const [isSelected, setIsSelected] = useState(false); return (
{({isSelected: selected}) => ( <> {selected ? : } {selected ? "Liked" : "Like"} )}

Status: {isSelected ? "Selected" : "Not selected"}

); } ``` ### Disabled ```tsx import {ToggleButton} from "@darkcode-ui/react"; import {Heart, HeartFill} from "@gravity-ui/icons"; export function Disabled() { return (
Like Like
); } ``` ## Related Components * **Button**: Allows a user to perform an action * **Switch**: Toggle between two states * **Checkbox**: Binary choice input control ## Styling ### Passing Tailwind CSS classes ```tsx import { ToggleButton } from '@darkcode-ui/react'; function CustomToggleButton() { return ( Toggle ); } ``` ### Customizing the component classes To customize the ToggleButton component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .toggle-button { @apply bg-purple-500 text-white; } .toggle-button--icon-only { @apply rounded-lg; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ToggleButton component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/toggle-button.css)): #### Base & Size Classes * `.toggle-button` - Base toggle button styles * `.toggle-button--sm` - Small size variant * `.toggle-button--md` - Medium size variant (default) * `.toggle-button--lg` - Large size variant #### Variant Classes * `.toggle-button--default` - Default variant with filled background * `.toggle-button--ghost` - Ghost variant with transparent background #### Modifier Classes * `.toggle-button--icon-only` - Icon-only toggle button * `.toggle-button--icon-only.toggle-button--sm` - Small icon-only * `.toggle-button--icon-only.toggle-button--lg` - Large icon-only ### Interactive States The toggle button supports both CSS pseudo-classes and data attributes for flexibility: * **Selected**: `[data-selected="true"]` (accent background and foreground) * **Hover**: `:hover` or `[data-hovered="true"]` * **Active/Pressed**: `:active` or `[data-pressed="true"]` (includes scale transform) * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring) * **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events) ## API Reference ### ToggleButton Props Inherits from [React Aria ToggleButton](https://react-spectrum.adobe.com/react-aria/ToggleButton.html). | Prop | Type | Default | Description | | ----------------- | ------------------------------------------------------------------------- | ----------- | ----------------------------------------- | | `variant` | `'default' \| 'ghost'` | `'default'` | Visual style variant | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the toggle button | | `isIconOnly` | `boolean` | `false` | Whether the button contains only an icon | | `isSelected` | `boolean` | - | Controlled selected state | | `defaultSelected` | `boolean` | `false` | Default selected state (uncontrolled) | | `isDisabled` | `boolean` | `false` | Whether the toggle button is disabled | | `onChange` | `(isSelected: boolean) => void` | - | Handler called when selection changes | | `onPress` | `(e: PressEvent) => void` | - | Handler called when the button is pressed | | `children` | `React.ReactNode \| (values: ToggleButtonRenderProps) => React.ReactNode` | - | Button content or render prop | ### ToggleButtonRenderProps When using the render prop pattern, these values are provided: | Prop | Type | Description | | ---------------- | --------- | ---------------------------------------------- | | `isSelected` | `boolean` | Whether the button is currently selected | | `isPressed` | `boolean` | Whether the button is currently pressed | | `isHovered` | `boolean` | Whether the button is hovered | | `isFocused` | `boolean` | Whether the button is focused | | `isFocusVisible` | `boolean` | Whether the button should show focus indicator | | `isDisabled` | `boolean` | Whether the button is disabled |
# Context Menu **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/context-menu **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(collections)/context-menu.mdx > A right-click context menu with nested submenus, selection state, and long-press support for touch ## Import ```tsx import { ContextMenu } from '@darkcode-ui/react'; ``` ### Usage ```tsx "use client"; import {ContextMenu, Label} from "@darkcode-ui/react"; export function Default() { return ( Right click here console.log(`Selected: ${key}`)}> ); } ``` ### Anatomy Import the ContextMenu component and access all parts using dot notation. ```tsx import { ContextMenu, Label, Header } from '@darkcode-ui/react'; export default () => (
) ``` ### Controlled ```tsx "use client"; import {ContextMenu, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function Controlled() { const [open, setOpen] = useState(false); return (

Menu is: {open ? "open" : "closed"}

Right click here console.log(`Selected: ${key}`)}>
); } ``` ### Disabled ```tsx "use client"; import {ContextMenu, Label} from "@darkcode-ui/react"; export function Disabled() { return ( Right click disabled ); } ``` ### Long Press ```tsx "use client"; import {ContextMenu, Label} from "@darkcode-ui/react"; export function LongPress() { return ( Long press (touch) or right click console.log(`Selected: ${key}`)}> ); } ``` ### With Sections ```tsx "use client"; import {ContextMenu, Header, Kbd, Label} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; export function WithSections() { return ( Right click here console.log(`Selected: ${key}`)}>
Actions
N
Danger zone
); } ``` ### With Selection ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {ContextMenu, Header, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function WithSelection() { const [selected, setSelected] = useState(new Set(["bold"])); return ( Right click here
Text style
); } ``` ### With Submenus ```tsx "use client"; import {ContextMenu, Label} from "@darkcode-ui/react"; export function WithSubmenus() { return ( Right click here console.log(`Selected: ${key}`)}> ); } ``` ## Related Components * **Dropdown**: Context menu with actions and options * **Popover**: Displays content in context with a trigger * **Separator**: Visual divider between content ## Styling ### Passing Tailwind CSS classes ```tsx import { ContextMenu, Label } from '@darkcode-ui/react'; function CustomContextMenu() { return ( Right click here ); } ``` ### Customizing the component classes To customize the Context Menu component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .context-menu__trigger { @apply relative inline-block; } .context-menu__popover { @apply rounded-lg border border-border bg-overlay p-2; } .context-menu__menu { @apply flex flex-col gap-0.5; } .context-menu__separator { @apply bg-separator; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Context Menu component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/context-menu.css)): #### Element Classes * `.context-menu__trigger` — The right-click target area. Relatively positioned `inline-block` with `-webkit-touch-callout: none` for long-press support. * `.context-menu__popover` — The floating menu panel positioned at the cursor. Has `bg-overlay`, `shadow-overlay`, rounded corners, and enter/exit animations. * `.context-menu__menu` — The list of menu items inside the popover. * `.context-menu__separator` — Horizontal divider between menu groups (`bg-separator`). #### Interactive States * **Entering**: `[data-entering="true"]` on `.context-menu__popover` — `fade-in` + `zoom-in-90` with placement-aware slide (150ms). * **Exiting**: `[data-exiting="true"]` on `.context-menu__popover` — `fade-out` + `zoom-out-95` (100ms). * **Placement slide**: `[data-placement="top"]` slides from bottom, `[data-placement="bottom"]` from top, `[data-placement="left"]` from right, `[data-placement="right"]` from left. * **Focus visible**: `.context-menu__popover[data-focus-visible="true"]` — outline suppressed. * **Reduced motion**: `motion-reduce:animate-none` on entering/exiting states. The Context Menu reuses the Menu, MenuItem, and MenuSection base components for its items, so their classes (`.menu-item`, `.menu-item__indicator`, `.menu-section`, …) are also available for customization. See the [Dropdown](/docs/en/react/components/dropdown) documentation for the full list. ## API Reference ### ContextMenu The root provider that manages open state and cursor positioning. | Prop | Type | Default | Description | | -------------- | ------------------------- | ------- | ------------------------------------- | | `open` | `boolean` | — | Controlled open state. | | `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled). | | `onOpenChange` | `(open: boolean) => void` | — | Callback when open state changes. | | `isDisabled` | `boolean` | `false` | Whether the context menu is disabled. | | `children` | `ReactNode` | — | Context menu content. | ### ContextMenu.Trigger The right-click (or long-press) target area. Renders a `
` by default. | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ---------------------------------- | | `children` | `ReactNode` | — | Content that can be right-clicked. | | `className` | `string` | — | Additional CSS classes. | ### ContextMenu.Popover The floating panel positioned at the cursor coordinates. | Prop | Type | Default | Description | | ----------- | ----------- | ---------------- | ------------------------------------------- | | `offset` | `number` | `2` | Distance from the anchor point in pixels. | | `placement` | `Placement` | `"bottom start"` | Preferred placement relative to the cursor. | | `className` | `string` | — | Additional CSS classes. | | `children` | `ReactNode` | — | Popover content. | Also supports all React Aria [Popover](https://react-spectrum.adobe.com/react-aria/Popover.html) props (except `isOpen`, `triggerRef`, and `onOpenChange`). ### ContextMenu.Menu The list of menu items. Automatically closes the context menu when an item is selected. | Prop | Type | Default | Description | | ------------------- | ---------------------------------- | -------- | ----------------------------------------------------------- | | `onClose` | `() => void` | — | Custom close handler. Defaults to closing the context menu. | | `selectionMode` | `"single" \| "multiple" \| "none"` | `"none"` | Whether single or multiple selection is enabled. | | `selectedKeys` | `Iterable` | — | The currently selected keys (controlled). | | `onSelectionChange` | `(keys: Selection) => void` | — | Handler called when the selection changes. | | `disabledKeys` | `Iterable` | — | Keys of disabled items. | | `onAction` | `(key: Key) => void` | — | Handler called when an item is activated. | Also supports all React Aria [Menu](https://react-spectrum.adobe.com/react-aria/Menu.html) props. ### ContextMenu.Item An individual menu item. Re-exports the DarkCode UI Dropdown item. | Prop | Type | Default | Description | | ----------- | ----------------------- | ----------- | --------------------------------------- | | `id` | `Key` | — | Unique identifier for the item. | | `textValue` | `string` | — | Text content of the item for typeahead. | | `variant` | `"default" \| "danger"` | `"default"` | Visual variant of the item. | | `className` | `string` | — | Additional CSS classes. | Also supports all [Dropdown.Item](/docs/en/react/components/dropdown) props. ### ContextMenu.ItemIndicator Selection indicator for checkbox/radio menu items. | Prop | Type | Default | Description | | ------ | ---------------------- | ------------- | ----------------------------- | | `type` | `"checkmark" \| "dot"` | `"checkmark"` | Type of indicator to display. | ### ContextMenu.Section Groups related menu items. Re-exports the DarkCode UI Dropdown section. Also supports all [Dropdown.Section](/docs/en/react/components/dropdown) props. ### ContextMenu.SubmenuTrigger Wraps a menu item that opens a nested submenu on hover. Also supports all [Dropdown.SubmenuTrigger](/docs/en/react/components/dropdown) props. ### ContextMenu.SubmenuIndicator Chevron icon indicating a submenu. | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ------------------------- | | `className` | `string` | — | Additional CSS classes. | | `children` | `ReactNode` | — | Custom indicator content. | ### ContextMenu.Separator A horizontal divider between menu groups. Also supports all React Aria [Separator](https://react-spectrum.adobe.com/react-aria/Separator.html) props. ## Examples ### Basic Usage ```tsx import { ContextMenu, Label } from '@darkcode-ui/react'; Right click here alert(`Selected: ${key}`)}> ``` ### Controlled Open State ```tsx import { ContextMenu, Label } from '@darkcode-ui/react'; import { useState } from 'react'; function ControlledContextMenu() { const [open, setOpen] = useState(false); return ( Right click here ); } ``` ## Accessibility The Context Menu component implements the ARIA menu pattern and provides: * Right-click (`contextmenu`) and touch long-press activation * Cursor-anchored positioning with automatic flipping near viewport edges * Full keyboard navigation support (arrow keys, home/end, typeahead) * Screen reader announcements for actions and selection changes * Proper focus management and `Escape` / outside-press dismissal * Submenu navigation For more information, see the [React Aria Menu documentation](https://react-spectrum.adobe.com/react-aria/Menu.html#menu). # Dropdown **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/dropdown **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(collections)/dropdown.mdx > A dropdown displays a list of actions or options that a user can choose ## Import ```tsx import { Dropdown } from '@darkcode-ui/react'; ``` ### Usage ```tsx "use client"; import {Button, Dropdown, Label} from "@darkcode-ui/react"; export function Default() { return ( console.log(`Selected: ${key}`)}> ); } ``` ### Anatomy Import the Dropdown component and access all parts using dot notation. ```tsx import { Dropdown, Button, Label, Description, Header, Kbd, Separator } from '@darkcode-ui/react'; export default () => (
Select a fruit
); } ``` ### Single With Custom Indicator ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Button, Dropdown, Header, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function SingleWithCustomIndicator() { const [selected, setSelected] = useState(new Set(["apple"])); const CustomCheckmarkIcon = ( ); return (
Select a fruit
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)} {({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)} {({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
{({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)} {({isSelected}) => (isSelected ? CustomCheckmarkIcon : null)}
); } ``` ### With Multiple Selection ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Button, Dropdown, Header, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function WithMultipleSelection() { const [selected, setSelected] = useState(new Set(["apple"])); return (
Select a fruit
); } ``` ### With Section Level Selection ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Button, Dropdown, Header, Kbd, Label, Separator} from "@darkcode-ui/react"; import {useState} from "react"; export function WithSectionLevelSelection() { const [textStyles, setTextStyles] = useState(new Set(["bold", "italic"])); const [textAlignment, setTextAlignment] = useState(new Set(["left"])); return (
Actions
X C U
Text Style
B I U
Text Alignment
A H D
); } ``` ### With Keyboard Shortcuts ```tsx "use client"; import {Button, Dropdown, Kbd, Label} from "@darkcode-ui/react"; export function WithKeyboardShortcuts() { return ( console.log(`Selected: ${key}`)}> N O S D ); } ``` ### With Icons ```tsx "use client"; import {Button, Dropdown, Kbd, Label} from "@darkcode-ui/react"; import {FloppyDisk, FolderOpen, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithIcons() { return ( console.log(`Selected: ${key}`)}> N O S D ); } ``` ### Long Press Trigger ```tsx import {Button, Dropdown, Label} from "@darkcode-ui/react"; export function LongPressTrigger() { return ( ); } ``` ### With Descriptions ```tsx "use client"; import {Button, Description, Dropdown, Kbd, Label} from "@darkcode-ui/react"; import {FloppyDisk, FolderOpen, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithDescriptions() { return ( console.log(`Selected: ${key}`)}>
Create a new file
N
Open an existing file
O
Save the current file
S
Move to trash
D
); } ``` ### With Sections ```tsx "use client"; import {Button, Description, Dropdown, Header, Kbd, Label, Separator} from "@darkcode-ui/react"; import {EllipsisVertical, Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithSections() { return ( console.log(`Selected: ${key}`)}>
Actions
Create a new file
N
Make changes
E
Danger zone
Move to trash
D
); } ``` ### With Disabled Items ```tsx "use client"; import {Button, Description, Dropdown, Header, Kbd, Label, Separator} from "@darkcode-ui/react"; import {Bars, Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithDisabledItems() { return ( console.log(`Selected: ${key}`)} >
Actions
Create a new file
N
Make changes
E
Danger zone
Move to trash
D
); } ``` ### With Submenus ```tsx "use client"; import {Button, Dropdown, Label} from "@darkcode-ui/react"; export function WithSubmenus() { return ( console.log(`Selected: ${key}`)}> ); } ``` ### With Custom Submenu Indicator ```tsx "use client"; import {Button, Dropdown, Label} from "@darkcode-ui/react"; import {ArrowRight} from "@gravity-ui/icons"; export function WithCustomSubmenuIndicator() { return ( console.log(`Selected: ${key}`)}> ); } ``` ### Controlled ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Button, Dropdown, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function Controlled() { const [selected, setSelected] = useState(new Set(["bold"])); const selectedItems = Array.from(selected); return (

Selected: {selectedItems.length > 0 ? selectedItems.join(", ") : "None"}

); } ``` ### Controlled Open State ```tsx "use client"; import {Button, Dropdown, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function ControlledOpenState() { const [open, setOpen] = useState(false); return (

Dropdown is: {open ? "open" : "closed"}

); } ``` ### Custom Trigger ```tsx import {Avatar, Dropdown, Label} from "@darkcode-ui/react"; import {ArrowRightFromSquare, Gear, Persons} from "@gravity-ui/icons"; export function CustomTrigger() { return ( JD
JD

Jane Doe

jane@example.com

); } ``` ## Related Components * **Button**: Allows a user to perform an action * **Popover**: Displays content in context with a trigger * **Separator**: Visual divider between content ## Styling ### Passing Tailwind CSS classes ```tsx import { Dropdown, Button } from '@darkcode-ui/react'; function CustomDropdown() { return ( Item 1 ); } ``` ### Customizing the component classes To customize the Dropdown component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .dropdown { @apply flex flex-col gap-1; } .dropdown__trigger { @apply outline-none; } .dropdown__popover { @apply rounded-lg border border-border bg-overlay p-2; } .dropdown__menu { @apply flex flex-col gap-1; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Dropdown component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/dropdown.css)): #### Base Classes * `.dropdown` - Base dropdown container * `.dropdown__trigger` - The button or element that triggers the dropdown * `.dropdown__popover` - The popover container * `.dropdown__menu` - The menu container inside the popover #### State Classes * `.dropdown__trigger[data-focus-visible="true"]` - Focused trigger state * `.dropdown__trigger[data-disabled="true"]` - Disabled trigger state * `.dropdown__trigger[data-pressed="true"]` - Pressed trigger state * `.dropdown__popover[data-entering]` - Entering animation state * `.dropdown__popover[data-exiting]` - Exiting animation state * `.dropdown__menu[data-selection-mode="single"]` - Single selection mode * `.dropdown__menu[data-selection-mode="multiple"]` - Multiple selection mode ### Menu Component Classes The Dropdown component uses Menu, MenuItem, and MenuSection as base components. These classes are also available for customization: #### Menu Classes * `.menu` - Base menu container ([menu.css](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/menu.css)) * `[data-slot="separator"]` - Separator elements within the menu #### MenuItem Classes * `.menu-item` - Base menu item container ([menu-item.css](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/menu-item.css)) * `.menu-item__indicator` - Selection indicator (checkmark or dot) * `[data-slot="menu-item-indicator--checkmark"]` - Checkmark indicator SVG * `[data-slot="menu-item-indicator--dot"]` - Dot indicator SVG * `.menu-item__indicator--submenu` - Submenu indicator (chevron) * `.menu-item--default` - Default variant styling * `.menu-item--danger` - Danger variant styling #### MenuItem State Classes * `.menu-item[data-focus-visible="true"]` - Focused item state (keyboard focus) * `.menu-item[data-focus="true"]` - Focused item state * `.menu-item[data-pressed]` - Pressed item state * `.menu-item[data-hovered]` - Hovered item state * `.menu-item[data-selected="true"]` - Selected item state * `.menu-item[data-disabled]` - Disabled item state * `.menu-item[data-has-submenu="true"]` - Item with submenu * `.menu-item[data-selection-mode="single"]` - Single selection mode * `.menu-item[data-selection-mode="multiple"]` - Multiple selection mode * `.menu-item[aria-checked="true"]` - Checked item (ARIA) * `.menu-item[aria-selected="true"]` - Selected item (ARIA) #### MenuSection Classes * `.menu-section` - Base menu section container ([menu-section.css](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/menu-section.css)) ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` on trigger and items * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on trigger and items * **Disabled**: `:disabled` or `[data-disabled="true"]` on trigger and items * **Pressed**: `:active` or `[data-pressed="true"]` on trigger and items * **Selected**: `[data-selected="true"]` or `[aria-selected="true"]` on items ## API Reference ### Dropdown Props | Prop | Type | Default | Description | | -------------- | --------------------------- | --------- | ------------------------------------------------------ | | `isOpen` | `boolean` | - | Sets the open state of the menu (controlled) | | `defaultOpen` | `boolean` | - | Sets the default open state of the menu (uncontrolled) | | `onOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the open state changes | | `trigger` | `"press" \| "longPress"` | `"press"` | The type of interaction that triggers the menu | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Dropdown content | ### Dropdown.Trigger Props | Prop | Type | Default | Description | | ----------- | ----------------------------- | ------- | ---------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Trigger content or render function | All [Button](https://react-spectrum.adobe.com/react-aria/Button.html) props are also supported when using a Button as the trigger. ### Dropdown.Popover Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------------------------------ | | `placement` | `"bottom" \| "bottom left" \| "bottom right" \| "bottom start" \| "bottom end" \| "top" \| "top left" \| "top right" \| "top start" \| "top end" \| "left" \| "left top" \| "left bottom" \| "start" \| "start top" \| "start bottom" \| "right" \| "right top" \| "right bottom" \| "end" \| "end top" \| "end bottom"` | `"bottom"` | Placement of the popover relative to the trigger | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Content children | All [Popover](https://react-spectrum.adobe.com/react-aria/Popover.html) props are also supported. ### Dropdown.Menu Props | Prop | Type | Default | Description | | --------------------- | ---------------------------------- | -------- | ----------------------------------------------- | | `selectionMode` | `"single" \| "multiple" \| "none"` | `"none"` | Whether single or multiple selection is enabled | | `selectedKeys` | `Iterable` | - | The currently selected keys (controlled) | | `defaultSelectedKeys` | `Iterable` | - | The initial selected keys (uncontrolled) | | `onSelectionChange` | `(keys: Selection) => void` | - | Handler called when the selection changes | | `disabledKeys` | `Iterable` | - | Keys of disabled items | | `onAction` | `(key: Key) => void` | - | Handler called when an item is activated | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Menu content | All [Menu](https://react-spectrum.adobe.com/react-aria/Menu.html#menu) props are also supported. ### Dropdown.Section Props | Prop | Type | Default | Description | | --------------------- | --------------------------- | ------- | -------------------------------------------- | | `selectionMode` | `"single" \| "multiple"` | - | Selection mode for items within this section | | `selectedKeys` | `Iterable` | - | The currently selected keys (controlled) | | `defaultSelectedKeys` | `Iterable` | - | The initial selected keys (uncontrolled) | | `onSelectionChange` | `(keys: Selection) => void` | - | Handler called when the selection changes | | `disabledKeys` | `Iterable` | - | Keys of disabled items | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Section content | All [MenuSection](https://react-spectrum.adobe.com/react-aria/Menu.html#menusection) props are also supported. ### Dropdown.Item Props | Prop | Type | Default | Description | | ----------- | ----------------------------- | ----------- | -------------------------------------- | | `id` | `Key` | - | Unique identifier for the item | | `textValue` | `string` | - | Text content of the item for typeahead | | `variant` | `"default" \| "danger"` | `"default"` | Visual variant of the item | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Item content or render function | All [MenuItem](https://react-spectrum.adobe.com/react-aria/Menu.html#menuitem) props are also supported. ### Dropdown.ItemIndicator Props | Prop | Type | Default | Description | | ----------- | ----------------------------- | ------------- | ------------------------------------------- | | `type` | `"checkmark" \| "dot"` | `"checkmark"` | Type of indicator to display | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Custom indicator content or render function | When using a render function, these values are provided: | Prop | Type | Description | | ----------------- | --------- | --------------------------------------------- | | `isSelected` | `boolean` | Whether the item is selected | | `isIndeterminate` | `boolean` | Whether the item is in an indeterminate state | ### Dropdown.SubmenuIndicator Props | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ------------------------ | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Custom indicator content | ### Dropdown.SubmenuTrigger Props | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ----------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Submenu trigger content | All [SubmenuTrigger](https://react-spectrum.adobe.com/react-aria/Menu.html#submenutrigger) props are also supported. ### RenderProps When using render functions with Dropdown.Item, these values are provided: | Prop | Type | Description | | ------------ | --------- | --------------------------------- | | `isSelected` | `boolean` | Whether the item is selected | | `isFocused` | `boolean` | Whether the item is focused | | `isDisabled` | `boolean` | Whether the item is disabled | | `isPressed` | `boolean` | Whether the item is being pressed | ## Examples ### Basic Usage ```tsx import { Dropdown, Button, Label } from '@darkcode-ui/react'; alert(`Selected: ${key}`)}> ``` ### With Sections ```tsx import { Dropdown, Button, Label, Header, Separator } from '@darkcode-ui/react'; alert(`Selected: ${key}`)}>
Actions
Danger zone
``` ### Controlled Selection ```tsx import type { Selection } from '@darkcode-ui/react'; import { Dropdown, Button, Label } from '@darkcode-ui/react'; import { useState } from 'react'; function ControlledDropdown() { const [selected, setSelected] = useState(new Set(['bold'])); return ( ); } ``` ### With Submenus ```tsx import { Dropdown, Button, Label } from '@darkcode-ui/react'; alert(`Selected: ${key}`)}> ``` ## Accessibility The Dropdown component implements the ARIA menu pattern and provides: * Full keyboard navigation support (arrow keys, home/end, typeahead) * Screen reader announcements for actions and selection changes * Proper focus management * Support for disabled states * Long press interaction support * Submenu navigation For more information, see the [React Aria Menu documentation](https://react-spectrum.adobe.com/react-aria/Menu.html#menu).
# ListBox **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/list-box **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(collections)/list-box.mdx > A listbox displays a list of options and allows a user to select one or more of them ## Import ```tsx import { ListBox } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {Avatar, Description, Label, ListBox} from "@darkcode-ui/react"; export function Default() { return ( B
bob@ui.darkcode.dev
F
fred@ui.darkcode.dev
M
martha@ui.darkcode.dev
); } ``` ### Anatomy Import the ListBox component and access all parts using dot notation. ```tsx import { ListBox, Label, Description, Header } from '@darkcode-ui/react'; export default () => (
) ``` ### With Sections ```tsx "use client"; import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@darkcode-ui/react"; import {Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithSections() { return ( alert(`Selected item: ${key}`)} >
Actions
Create a new file
N
Make changes
E
Danger zone
Move to trash
D
); } ``` ### Multi Select ```tsx import {Avatar, Description, Label, ListBox, Surface} from "@darkcode-ui/react"; export function MultiSelect() { return ( B
bob@ui.darkcode.dev
F
fred@ui.darkcode.dev
M
martha@ui.darkcode.dev
); } ``` ### With Disabled Items ```tsx "use client"; import {Description, Header, Kbd, Label, ListBox, Separator, Surface} from "@darkcode-ui/react"; import {Pencil, SquarePlus, TrashBin} from "@gravity-ui/icons"; export function WithDisabledItems() { return ( alert(`Selected item: ${key}`)} >
Actions
Create a new file
N
Make changes
E
Danger zone
Move to trash
D
); } ``` ### Custom Check Icon ```tsx "use client"; import {Avatar, Description, Label, ListBox, Surface} from "@darkcode-ui/react"; import {Check} from "@gravity-ui/icons"; export function CustomCheckIcon() { return ( B
bob@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }
F
fred@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }
M
martha@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }
); } ``` ### Controlled ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Avatar, Description, Label, ListBox, Surface} from "@darkcode-ui/react"; import {Check} from "@gravity-ui/icons"; import {useState} from "react"; export function Controlled() { const [selected, setSelected] = useState(new Set(["1"])); const selectedItems = Array.from(selected); return (
B
bob@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }
F
fred@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }
M
martha@ui.darkcode.dev
{({isSelected}) => isSelected ? : null }

Selected: {selectedItems.length > 0 ? selectedItems.join(", ") : "None"}

); } ``` ### Custom Render Function ```tsx "use client"; import {Avatar, Description, Label, ListBox} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
} selectionMode="single" > } textValue="Bob" > B
bob@ui.darkcode.dev
} textValue="Fred" > F
fred@ui.darkcode.dev
} textValue="Martha" > M
martha@ui.darkcode.dev
); } ``` ### Virtualization ListBox supports virtualization through [Virtualizer](https://react-aria.adobe.com/Virtualizer), enabling efficient rendering of large datasets by displaying only the rows visible within the viewport. ```tsx "use client"; import {Description, Label, ListBox, ListLayout, Virtualizer} from "@darkcode-ui/react"; interface User { id: number; name: string; email: string; } export function Virtualization() { const firstNames = [ "Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "Oliver", "Isabella", "Lucas", "Mia", "Ethan", "Charlotte", "Mason", "Amelia", "Logan", "Harper", "Alexander", "Ella", "Benjamin", ]; const lastNames = [ "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Anderson", "Taylor", "Thomas", "Jackson", "White", "Harris", "Clark", "Lewis", "Robinson", "Walker", ]; function generateUsers(n: number): User[] { const users: User[] = []; for (let i = 0; i < n; i++) { const firstName = firstNames[i % firstNames.length]; const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length]; const name = `${firstName} ${lastName}`; users.push({ email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`, id: i + 1, name, }); } return users; } const users = generateUsers(1000); return ( {(user) => (
{user.email}
)}
); } ``` ## Related Components * **Select**: Dropdown select control * **ComboBox**: Text input with searchable dropdown list * **Avatar**: Display user profile images ## Styling ### Passing Tailwind CSS classes ```tsx import { ListBox } from '@darkcode-ui/react'; function CustomListBox() { return ( Item 1 ); } ``` ### Customizing the component classes To customize the ListBox component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .list-box { @apply rounded-lg border border-border bg-surface p-2; } .list-box-item { @apply rounded px-2 py-1 cursor-pointer; } .list-box-item--danger { @apply text-danger; } .list-box-item__indicator { @apply text-accent; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ListBox component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/list-box.css)): #### Base Classes * `.list-box` - Base listbox container * `.list-box-item` - Individual listbox item * `.list-box-item__indicator` - Selection indicator icon * `.list-box-section` - Section container for grouping items #### Variant Classes * `.list-box--default` - Default variant styling * `.list-box--danger` - Danger variant styling * `.list-box-item--default` - Default item variant * `.list-box-item--danger` - Danger item variant #### State Classes * `.list-box-item[data-selected="true"]` - Selected item state * `.list-box-item[data-focus-visible="true"]` - Focused item state * `.list-box-item[data-disabled="true"]` - Disabled item state * `.list-box-item__indicator[data-visible="true"]` - Visible indicator state ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` on item * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on item * **Selected**: `[data-selected="true"]` on item * **Disabled**: `:disabled` or `[data-disabled="true"]` on item ## API Reference ### ListBox Props | Prop | Type | Default | Description | | --------------------- | -------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | | `aria-label` | `string` | - | Accessibility label for the listbox | | `aria-labelledby` | `string` | - | ID of element that labels the listbox | | `selectionMode` | `"none" \| "single" \| "multiple"` | `"single"` | Selection behavior | | `selectedKeys` | `Selection` | - | Controlled selected keys | | `defaultSelectedKeys` | `Selection` | - | Initial selected keys | | `onSelectionChange` | `(keys: Selection) => void` | - | Handler called when selection changes | | `disabledKeys` | `Iterable` | - | Keys of disabled items | | `onAction` | `(key: Key) => void` | - | Handler called when an item is activated | | `variant` | `"default" \| "danger"` | `"default"` | Visual variant | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | ListBox items and sections | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ListBox.Item Props | Prop | Type | Default | Description | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | | `id` | `Key` | - | Unique identifier for the item | | `textValue` | `string` | - | Text value for accessibility and typeahead | | `isDisabled` | `boolean` | `false` | Whether this item is disabled | | `variant` | `"default" \| "danger"` | `"default"` | Visual variant | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Item content or render function | | `render` | `(props: DetailedHTMLProps \| React.JSX.IntrinsicElements[keyof React.JSX.IntrinsicElements], renderProps: ListBoxItemRenderProps) => ReactElement` | - | Overrides the default DOM element with a custom render function. | ### ListBox.ItemIndicator Props | Prop | Type | Default | Description | | ----------- | ----------------------------- | ------- | ------------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Custom indicator content or render function | ### ListBox.Section Props | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ------------------------------------------ | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Section content including Header and Items | ### RenderProps When using render functions with ListBox.Item or ListBox.ItemIndicator, these values are provided: | Prop | Type | Description | | ------------ | --------- | --------------------------------- | | `isSelected` | `boolean` | Whether the item is selected | | `isFocused` | `boolean` | Whether the item is focused | | `isDisabled` | `boolean` | Whether the item is disabled | | `isPressed` | `boolean` | Whether the item is being pressed | ### ListLayout | Name | Type | Default | Description | | ------------------------ | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `rowHeight` | `number \| undefined` | 48 | The fixed height of a row in px. | | `estimatedRowHeight` | `number \| undefined` | — | The estimated height of a row, when row heights are variable. | | `headingHeight` | `number \| undefined` | 48 | The fixed height of a section header in px. | | `estimatedHeadingHeight` | `number \| undefined` | — | The estimated height of a section header, when the height is variable. | | `loaderHeight` | `number \| undefined` | 48 | The fixed height of a loader element in px. This loader is specifically for "load more" elements rendered when loading more rows at the root level or inside nested row/sections. | | `dropIndicatorThickness` | `number \| undefined` | 2 | The thickness of the drop indicator. | | `gap` | `number \| undefined` | 0 | The gap between items. | | `padding` | `number \| undefined` | 0 | The padding around the list. | ## Examples ### Basic Usage ```tsx import { ListBox, Label, Description } from '@darkcode-ui/react'; bob@ui.darkcode.dev alice@ui.darkcode.dev ``` ### With Sections ```tsx import { ListBox, Header, Separator } from '@darkcode-ui/react'; console.log(key)}>
Actions
New file Edit file
Danger zone
Delete
``` ### Controlled Selection ```tsx import { ListBox, Selection } from '@darkcode-ui/react'; import { useState } from 'react'; function ControlledListBox() { const [selected, setSelected] = useState(new Set(["1"])); return ( Option 1 Option 2 Option 3 ); } ``` ### Custom Indicator ```tsx import { ListBox, ListBoxItemIndicator } from '@darkcode-ui/react'; import { Icon } from '@iconify/react'; Option 1 {({isSelected}) => isSelected ? : null } ``` ## Accessibility The ListBox component implements the ARIA listbox pattern and provides: * Full keyboard navigation support * Screen reader announcements for selection changes * Proper focus management * Support for disabled states * Typeahead search functionality For more information, see the [React Aria ListBox documentation](https://react-spectrum.adobe.com/react-aria/ListBox.html). # List View **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/list-view **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(collections)/list-view.mdx > A single-column interactive list with keyboard navigation, selection, and accessible item actions — built on React Aria's GridList. ## Import ```tsx import { ListView } from '@darkcode-ui/react'; ``` ## Usage The `ListView` component displays a list of interactive items with built-in keyboard navigation, typeahead search, and selection. Pass your data to the `items` prop and render each row with a function child. ```tsx "use client"; import {Button, ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; const files = [ {icon: "gravity-ui:file-text", id: "1", name: "Project Proposal.pdf", size: "2.4 MB"}, {icon: "gravity-ui:picture", id: "2", name: "Design Mockups.fig", size: "8.1 MB"}, {icon: "gravity-ui:file", id: "3", name: "Q4 Financial Report.xlsx", size: "1.2 MB"}, {icon: "gravity-ui:picture", id: "4", name: "Team Photo.png", size: "5.7 MB"}, {icon: "gravity-ui:file-text", id: "5", name: "Meeting Notes.docx", size: "342 KB"}, ]; export function Default() { return ( {(file) => (
{file.name} {file.size}
)}
); } ``` ## Anatomy Import the ListView component and access all parts using dot notation. Each `Item` holds an `ItemContent` (icon/avatar + text) and an optional trailing `ItemAction`. Selection checkboxes are rendered automatically when selection is enabled. ```tsx import { ListView, Button } from '@darkcode-ui/react'; {(file) => (
{file.name} {file.size}
)}
``` ## Selection Modes ListView supports three selection modes: `none` (read-only), `single` (radio-style), and `multiple` (checkboxes). Selection checkboxes are rendered automatically when selection is enabled. ```tsx "use client"; import {ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; const files = [ {icon: "gravity-ui:file-text", id: "1", name: "Project Proposal.pdf", size: "2.4 MB"}, {icon: "gravity-ui:picture", id: "2", name: "Design Mockups.fig", size: "8.1 MB"}, {icon: "gravity-ui:file", id: "3", name: "Q4 Financial Report.xlsx", size: "1.2 MB"}, {icon: "gravity-ui:picture", id: "4", name: "Team Photo.png", size: "5.7 MB"}, ]; function FileList({selectionMode}: {selectionMode: "none" | "single" | "multiple"}) { return ( {(file) => (
{file.name} {file.size}
)}
); } export function SelectionModes() { return (

None

Single

Multiple

); } ``` ## Variants Use the `variant` prop to switch between the two visual styles. * **Primary** (default) — gray background wrapper with surface-tinted rows forming a card shape. * **Secondary** — no wrapper; transparent rows separated by hairline borders with accent selection. ```tsx "use client"; import {ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; const files = [ {icon: "gravity-ui:file-text", id: "1", name: "Project Proposal.pdf", size: "2.4 MB"}, {icon: "gravity-ui:picture", id: "2", name: "Design Mockups.fig", size: "8.1 MB"}, {icon: "gravity-ui:file", id: "3", name: "Q4 Financial Report.xlsx", size: "1.2 MB"}, ]; function FileList({variant}: {variant: "primary" | "secondary"}) { return ( {(file) => (
{file.name} {file.size}
)}
); } export function Variants() { return (

Primary

Secondary

); } ``` ## Secondary The secondary variant drops the card wrapper for a flatter, divided list — ideal inside panels that already provide their own surface. ```tsx "use client"; import {Button, ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; const people = [ {avatar: "gravity-ui:person", id: "1", name: "Olivia Martin", role: "Product Designer"}, {avatar: "gravity-ui:person", id: "2", name: "Liam Johnson", role: "Frontend Engineer"}, {avatar: "gravity-ui:person", id: "3", name: "Emma Williams", role: "Engineering Manager"}, {avatar: "gravity-ui:person", id: "4", name: "Noah Brown", role: "Data Scientist"}, ]; export function Secondary() { return ( {(person) => (
{person.name} {person.role}
)}
); } ``` ## Disabled Items Use the `disabledKeys` prop to disable specific items. Disabled items cannot be selected, focused, or interacted with. ```tsx "use client"; import {ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; const files = [ {icon: "gravity-ui:file-text", id: "1", name: "Project Proposal.pdf", size: "2.4 MB"}, {icon: "gravity-ui:picture", id: "2", name: "Design Mockups.fig", size: "8.1 MB"}, {icon: "gravity-ui:file", id: "3", name: "Q4 Financial Report.xlsx", size: "1.2 MB"}, {icon: "gravity-ui:picture", id: "4", name: "Team Photo.png", size: "5.7 MB"}, {icon: "gravity-ui:file-text", id: "5", name: "Meeting Notes.docx", size: "342 KB"}, ]; export function DisabledItems() { return ( {(file) => (
{file.name} {file.size}
)}
); } ``` ## With Action Bar Combine with [ActionBar](/react/components/action-bar) for bulk selection workflows. The ActionBar appears when items are selected and provides contextual actions. ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {ActionBar, Button, ListView} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; const files = [ {icon: "gravity-ui:file-text", id: "1", name: "Project Proposal.pdf", size: "2.4 MB"}, {icon: "gravity-ui:picture", id: "2", name: "Design Mockups.fig", size: "8.1 MB"}, {icon: "gravity-ui:file", id: "3", name: "Q4 Financial Report.xlsx", size: "1.2 MB"}, {icon: "gravity-ui:picture", id: "4", name: "Team Photo.png", size: "5.7 MB"}, {icon: "gravity-ui:file-text", id: "5", name: "Meeting Notes.docx", size: "342 KB"}, ]; export function WithActions() { const [selected, setSelected] = useState(new Set()); const count = selected === "all" ? files.length : selected.size; return ( <> {(file) => (
{file.name} {file.size}
)}
0}> setSelected(new Set())} /> ); } ``` ## Virtualization For very large datasets, pass `virtualized` (and optionally `rowHeight`) to render only the visible rows. Give the list a fixed height so it can scroll. ```tsx {(file) => {/* ... */}} ``` ## CSS Classes ### Base Classes * `.list-view` — Root container. Sets `min-height: 0`, full width, and `outline: none`. * `.list-view--primary` — Gray background wrapper with padding and large border-radius. Rows tint with the surface color. * `.list-view--secondary` — No background or padding. Rows are transparent with bottom borders. ### Element Classes * `.list-view__item` — Individual row. Flex layout with gap, padding, and interactive states. * `.list-view__item-content` — Main content area (icon + text). Flex with `min-width: 0` for truncation. * `.list-view__title` — Primary text. Truncated, `text-sm font-medium`. * `.list-view__description` — Secondary text. Truncated, `text-xs text-muted`. * `.list-view__item-action` — Trailing action slot. Auto-margin to the end. * `.list-view__selection-cell` — Checkbox area. Fixed width, flex-shrink: 0. * `.list-view__selection-control` — The selection checkbox (circular in single-selection mode). * `.list-view__empty-state` — Centered empty-state container. ### Interactive States * **Hover**: `bg-surface/40` (primary) or `bg-default/50` (secondary). * **Selected**: `bg-surface/70` (primary) or `bg-accent-soft` (secondary). * **Focus visible**: Inset focus ring via `box-shadow: inset 0 0 0 2px var(--focus)`. * **Disabled**: `status-disabled` utility. * **Dragging**: `opacity: 0.5`. ## API Reference ### ListView The root list component. Accepts a generic type parameter `T` for the item data shape. Extends RAC `GridListProps`. | Prop | Type | Default | Description | | --------------------- | --------------------------------------- | ----------- | -------------------------------------------------------- | | `aria-label` | `string` | — | Accessible label for the list. Required. | | `variant` | `'primary' \| 'secondary'` | `'primary'` | Visual variant. | | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Item selection mode. | | `selectedKeys` | `Selection` | — | Controlled selected item keys. | | `defaultSelectedKeys` | `Selection` | — | Default selected keys (uncontrolled). | | `onSelectionChange` | `(keys: Selection) => void` | — | Callback when selection changes. | | `selectionBehavior` | `'toggle' \| 'replace'` | `'toggle'` | Selection interaction model. | | `items` | `Iterable` | — | Item objects for dynamic collections. | | `disabledKeys` | `Iterable` | — | Keys of items that should be disabled. | | `onAction` | `(key: Key) => void` | — | Callback when a user performs an action on an item. | | `renderEmptyState` | `() => ReactNode` | — | Render function for the empty state. | | `virtualized` | `boolean` | `false` | Enable row virtualization for large datasets. | | `rowHeight` | `number` | `56` | Estimated row height in pixels for virtualization. | | `className` | `string` | — | Additional CSS classes. | | `children` | `ReactNode \| ((item: T) => ReactNode)` | — | Static items or render function for dynamic collections. | ### ListView\.Item Individual list item. Extends RAC `GridListItemProps`. | Prop | Type | Default | Description | | ------------ | ----------- | ------- | ------------------------------------------------------------------- | | `id` | `Key` | — | Unique item identifier. | | `textValue` | `string` | — | String representation for typeahead and accessibility. | | `isDisabled` | `boolean` | — | Whether the item is disabled. | | `href` | `string` | — | URL to navigate to when the item is actioned. | | `children` | `ReactNode` | — | Item content — typically `ItemContent` and optionally `ItemAction`. | | `className` | `string` | — | Additional CSS classes. | ### ListView\.ItemContent Main content container for the item (icon/avatar + text). ### ListView\.Title Primary text element inside an item. ### ListView\.Description Secondary/muted text element inside an item. ### ListView\.ItemAction Trailing action slot inside an item. ## Related Components * **Listbox**: Scrollable list of selectable items * **Table**: Structured data display in rows and columns * **Checkbox**: Binary choice input control ## Accessibility ListView is built on the React Aria `GridList` primitive and provides: * Full keyboard navigation (Arrow keys, Home, End, Page Up/Down, type-ahead) * Single and multiple selection with `Space`/`Enter` and modifier keys * Roving tabindex and proper `row` / `gridcell` roles * Disabled item handling that skips focus and interaction For more information, see the [React Aria GridList documentation](https://react-spectrum.adobe.com/react-aria/GridList.html).
# TagGroup **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/tag-group **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(collections)/tag-group.mdx > A focusable list of tags with support for keyboard navigation, selection, and removal ## Import ```tsx import { TagGroup } from '@darkcode-ui/react'; ``` ### Usage ```tsx "use client"; import {Tag, TagGroup} from "@darkcode-ui/react"; import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons"; export function TagGroupBasic() { return ( News Travel Gaming Shopping ); } ``` ### Anatomy ```tsx import { TagGroup, Tag, Label, Description, ErrorMessage } from '@darkcode-ui/react'; export default () => ( ) ``` ### Sizes ```tsx "use client"; import {Label, Tag, TagGroup} from "@darkcode-ui/react"; export function TagGroupSizes() { return (
News Travel Gaming News Travel Gaming News Travel Gaming
); } ``` ### Variants ```tsx "use client"; import {Label, Tag, TagGroup} from "@darkcode-ui/react"; export function TagGroupVariants() { return (
News Travel Gaming News Travel Gaming
); } ``` ### Disabled ```tsx "use client"; import {Description, Label, Tag, TagGroup} from "@darkcode-ui/react"; export function TagGroupDisabled() { return (
News Travel Gaming Some tags are disabled News Travel Gaming Tags disabled via disabledKeys prop
); } ``` ### Selection Modes ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import {Description, Label, Tag, TagGroup} from "@darkcode-ui/react"; import {useState} from "react"; export function TagGroupSelectionModes() { const [singleSelected, setSingleSelected] = useState>(new Set(["news"])); const [multipleSelected, setMultipleSelected] = useState>( new Set(["news", "travel"]), ); return (
setSingleSelected(keys)} > News Travel Gaming Shopping Choose one category setMultipleSelected(keys)} > News Travel Gaming Shopping Choose multiple categories
); } ``` ### Controlled ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import {Description, Label, Tag, TagGroup} from "@darkcode-ui/react"; import {useState} from "react"; export function TagGroupControlled() { const [selected, setSelected] = useState>(new Set(["news", "travel"])); return (
setSelected(keys)} > News Travel Gaming Shopping Selected: {Array.from(selected).length > 0 ? Array.from(selected).join(", ") : "None"}
); } ``` ### With Error Message ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import {Description, ErrorMessage, Label, Tag, TagGroup} from "@darkcode-ui/react"; import {useMemo, useState} from "react"; export function TagGroupWithErrorMessage() { const [selected, setSelected] = useState>(new Set()); const isInvalid = useMemo(() => Array.from(selected).length === 0, [selected]); return ( setSelected(keys)} > Laundry Fitness center Parking Swimming pool Breakfast {isInvalid ? "Select at least one category" : "Selected: " + Array.from(selected).join(", ")} {!!isInvalid && <>Please select at least one category} ); } ``` ### With Prefix ```tsx "use client"; import {Avatar, Description, Label, Tag, TagGroup} from "@darkcode-ui/react"; import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons"; export function TagGroupWithPrefix() { return (
News Travel Gaming Shopping Tags with icons F Fred M Michael J Jane Tags with avatars
); } ``` ### With Remove Button ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import {Description, EmptyState, Label, Tag, TagGroup} from "@darkcode-ui/react"; import {CircleXmarkFill} from "@gravity-ui/icons"; import {useState} from "react"; export function TagGroupWithRemoveButton() { type TagItem = {id: string; name: string}; const [tags, setTags] = useState([ {id: "news", name: "News"}, {id: "travel", name: "Travel"}, {id: "gaming", name: "Gaming"}, {id: "shopping", name: "Shopping"}, ]); const [frameworks, setFrameworks] = useState([ {id: "react", name: "React"}, {id: "vue", name: "Vue"}, {id: "angular", name: "Angular"}, {id: "svelte", name: "Svelte"}, ]); const onRemoveTags = (keys: Set) => { setTags(tags.filter((tag) => !keys.has(tag.id))); }; const onRemoveFrameworks = (keys: Set) => { setFrameworks(frameworks.filter((framework) => !keys.has(framework.id))); }; return (
No categories found} > {(tag) => ( {tag.name} )} Click the X to remove tags
No frameworks found} > {(tag) => ( {(renderProps) => ( <> {tag.name} {!!renderProps.allowsRemoving && ( )} )} )} Custom remove button with icon
); } ``` ### With List Data ```tsx "use client"; import type {Key} from "@darkcode-ui/react"; import { Avatar, Description, EmptyState, Label, Tag, TagGroup, useListData, } from "@darkcode-ui/react"; export function TagGroupWithListData() { type User = { id: string; name: string; avatar: string; fallback: string; }; const list = useListData({ getKey: (item) => item.id, initialItems: [ { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/blue.jpg", fallback: "F", id: "fred", name: "Fred", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg", fallback: "M", id: "michael", name: "Michael", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/purple.jpg", fallback: "J", id: "jane", name: "Jane", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/red.jpg", fallback: "A", id: "alice", name: "Alice", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/orange.jpg", fallback: "B", id: "bob", name: "Bob", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/black.jpg", fallback: "C", id: "charlie", name: "Charlie", }, ], initialSelectedKeys: new Set(["fred", "michael"]), }); const onRemove = (keys: Set) => { list.remove(...keys); }; return (
list.setSelectedKeys(keys)} > No team members} > {(user) => ( {user.fallback} {user.name} )} Select team members for your project {list.selectedKeys !== "all" && Array.from(list.selectedKeys).length > 0 && (

Selected:

{Array.from(list.selectedKeys).map((key) => { const user = list.getItem(key); if (!user) return null; return (
{user.fallback} {user.name}
); })}
)}
); } ``` ### Custom Render Function ```tsx "use client"; import {Tag, TagGroup} from "@darkcode-ui/react"; import {PlanetEarth, Rocket, ShoppingBag, SquareArticle} from "@gravity-ui/icons"; export function CustomRenderFunction() { return (
} selectionMode="single" > News Travel Gaming Shopping ); } ``` ## Related Components * **Label**: Accessible label for form controls * **Description**: Helper text for form fields * **ErrorMessage**: Displays validation error messages for components with validation support ## Styling ### Passing Tailwind CSS classes ```tsx import { TagGroup, Tag, Label } from '@darkcode-ui/react'; function CustomTagGroup() { return ( Custom Styled ); } ``` ### Customizing the component classes To customize the TagGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .tag-group { @apply flex flex-col gap-2; } .tag-group__list { @apply flex flex-wrap gap-2; } .tag { @apply rounded-full px-3 py-1; } .tag__remove-button { @apply ml-1; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The TagGroup component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/tag-group.css) and [tag.css](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/tag.css)): #### Base Classes * `.tag-group` - Base tag group container * `.tag-group__list` - Container for the list of tags * `.tag` - Base tag styles * `.tag__remove-button` - Remove button trigger #### Slot Classes * `.tag-group [slot="description"]` - Description slot styles * `.tag-group [slot="errorMessage"]` - ErrorMessage slot styles #### Size Classes * `.tag--sm` - Small size tag * `.tag--md` - Medium size tag (default) * `.tag--lg` - Large size tag #### Variant Classes * `.tag--default` - Default variant * `.tag--surface` - Surface variant with surface background #### State Classes * `.tag[data-selected="true"]` - Selected tag state * `.tag[data-disabled="true"]` - Disabled tag state * `.tag[data-hovered="true"]` - Hovered tag state * `.tag[data-pressed="true"]` - Pressed tag state * `.tag[data-focus-visible="true"]` - Focused tag state (keyboard focus) ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` on tag * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on tag * **Pressed**: `:active` or `[data-pressed="true"]` on tag * **Selected**: `[data-selected="true"]` or `[aria-selected="true"]` on tag * **Disabled**: `:disabled` or `[data-disabled="true"]` on tag ## API Reference ### TagGroup Props | Prop | Type | Default | Description | | --------------------- | ----------------------------------------------------------------- | ----------- | ---------------------------------------------------------------- | | `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | The type of selection that is allowed | | `selectedKeys` | `Selection` | - | The currently selected keys (controlled) | | `defaultSelectedKeys` | `Selection` | - | The initial selected keys (uncontrolled) | | `onSelectionChange` | `(keys: Selection) => void` | - | Handler called when the selection changes | | `disabledKeys` | `Iterable` | - | Keys of disabled tags | | `isDisabled` | `boolean` | - | Whether the tag group is disabled | | `onRemove` | `(keys: Set) => void` | - | Handler called when tags are removed | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the tags in the group | | `variant` | `"default" \| "surface"` | `"default"` | Visual variant of the tags | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | TagGroup content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### TagGroup.List Props | Prop | Type | Default | Description | | ------------------ | -------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- | | `items` | `Iterable` | - | The items to display in the tag list | | `renderEmptyState` | `() => ReactNode` | - | Function to render when the list is empty | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | TagList content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### Tag Props | Prop | Type | Default | Description | | ------------ | ---------------------------------------------------------------------- | ------- | -------------------------------------------------------------------- | | `id` | `Key` | - | The unique identifier for the tag | | `textValue` | `string` | - | A string representation of the tag's content, used for accessibility | | `isDisabled` | `boolean` | - | Whether the tag is disabled | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Tag content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | **Note**: `size`, `variant` are inherited from the parent `TagGroup` component and cannot be set directly on individual `Tag` components. ### Tag.RemoveButton Props | Prop | Type | Default | Description | | ----------- | ----------- | ------- | ----------------------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode` | - | Custom remove button content (defaults to close icon) | **Note**: The `Tag.RemoveButton` component supports customization similar to `SearchField.ClearButton`. When `onRemove` is provided to `TagGroup`: * **Auto-rendering**: If no custom `Tag.RemoveButton` is included in the `Tag` children, a default remove button is automatically rendered. * **Custom button**: If a custom `Tag.RemoveButton` is provided as a child of `Tag`, it will be used instead of the auto-rendered button. * **Custom icon**: You can pass custom content (like icons) to `Tag.RemoveButton` children to customize the appearance. **Example - Auto-rendered (default)**: ```tsx News {/* Remove button is automatically rendered */} ``` **Example - Custom RemoveButton with icon**: ```tsx News ``` **Example - Custom RemoveButton in render props**: ```tsx {(renderProps) => ( <> News {!!renderProps.allowsRemoving && ( )} )} ``` ### RenderProps When using render functions with TagGroup.List, these values are provided: | Prop | Type | Description | | ---------------- | --------- | ---------------------------------- | | `isSelected` | `boolean` | Whether the tag is selected | | `isDisabled` | `boolean` | Whether the tag is disabled | | `isHovered` | `boolean` | Whether the tag is hovered | | `isPressed` | `boolean` | Whether the tag is pressed | | `isFocused` | `boolean` | Whether the tag is focused | | `isFocusVisible` | `boolean` | Whether the tag has keyboard focus | # Cell Color Picker **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/cell-color-picker **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/cell-color-picker.mdx > A compact color picker styled as a settings cell, with a leading label and a trailing swatch and live hex value. ## Import ```tsx import { ColorPicker, ColorArea, ColorSlider } from '@darkcode-ui/react'; ``` The cell color picker is the standard [ColorPicker](/react/components/color-picker) rendered in its cell layout: pass the `variant` prop and compose the `ColorPicker.Label`, `ColorPicker.Swatch`, and `ColorPicker.ValueDisplay` parts inside the trigger. ## Usage ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider} from "@darkcode-ui/react"; export function Default() { return ( Background
); } ``` ## Anatomy The trigger becomes a full-width settings-cell row: a leading `Label` on the left, and a trailing cluster with the `Swatch` preview and the live hex `ValueDisplay` on the right. ```tsx import { ColorPicker, ColorArea, ColorSlider } from '@darkcode-ui/react'; export default () => ( Background
); ``` ## Controlled Pass `value` and `onChange` to control the selected color. `ColorPicker.ValueDisplay` reflects the current value automatically. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function Controlled() { const [color, setColor] = useState(parseColor("#325578")); return (
Background

Selected: {color.toString("hex")}

); } ``` ## Disabled Pass `isDisabled` to disable the cell. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider} from "@darkcode-ui/react"; export function Disabled() { return ( Background
); } ``` ## Settings Group Stack multiple cells in a single inset panel with merged corners and hairline dividers, ideal for preference and configuration forms. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider} from "@darkcode-ui/react"; const cells = [ {defaultValue: "#0485F7", label: "Background"}, {defaultValue: "#F43F5E", label: "Accent"}, {defaultValue: "#22C55E", label: "Success"}, ]; export function SettingsGroup() { return (
Theme colors
{cells.map((cell, index) => ( 0 ? "border-t border-separator" : undefined} defaultValue={cell.defaultValue} variant="default" > {cell.label}
))}
); } ``` ## Variants Use `variant="secondary"` for an accent-tinted cell. The variant styles the trigger row. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider} from "@darkcode-ui/react"; const variants = [ {defaultValue: "#0485F7", label: "Default", variant: "default"}, {defaultValue: "#F43F5E", label: "Secondary", variant: "secondary"}, ] as const; export function Variants() { return (
{variants.map((item) => ( {item.label}
))}
); } ``` ## With Presets Add a `ColorSwatchPicker` inside the popover to offer a palette of preset colors alongside the full picker. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider, ColorSwatchPicker} from "@darkcode-ui/react"; const presets = [ "#EF4444", "#F97316", "#EAB308", "#22C55E", "#06B6D4", "#3B82F6", "#8B5CF6", "#EC4899", ]; export function WithPresets() { return ( Label color
{presets.map((preset) => ( ))}
); } ``` ## Styling The cell color picker is the [ColorPicker](/react/components/color-picker) component, so all of its styling patterns apply. The cell layout is provided by the `variant` modifier and the BEM classes below. ### CSS Classes The cell layout uses the following CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-picker.css)): #### Base Classes * `.color-picker` - Root wrapper * `.color-picker__trigger` - The visible cell row button * `.color-picker__popover` - Dropdown color picker panel #### Element Classes * `.color-picker__trigger--default` - Default variant trigger styling * `.color-picker__trigger--secondary` - Secondary variant trigger styling * `.color-picker__label` - Leading text label * `.color-picker__value-display` - Live hex value text * `.color-picker__swatch` - Color swatch preview ### Interactive States The component supports both CSS pseudo-classes and data attributes: * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` * **Disabled**: `:disabled` or `[data-disabled="true"]` ## API Reference ### ColorPicker The root component. Add the `variant` prop to enable the cell layout. | Prop | Type | Default | Description | | --------- | -------------------------- | ------- | ----------------------------------------------------------------------------------------------------- | | `variant` | `'default' \| 'secondary'` | - | Renders the trigger as a settings-cell row. When omitted, the trigger stays a compact inline trigger. | Also supports all [DarkCode UI ColorPicker](/react/components/color-picker) props (`value`, `defaultValue`, `onChange`, `isDisabled`, etc.). ### ColorPicker.Trigger The visible cell row button. Wraps React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html). Also supports all React Aria `Button` props. ### ColorPicker.Label Leading text label rendered as a `span`. | Prop | Type | Default | Description | | ---------- | ----------- | ------- | ----------- | | `children` | `ReactNode` | - | Label text | Also supports all native `span` HTML attributes. ### ColorPicker.ValueDisplay Displays the current color as a hex value (e.g. `#FF5733`). Reads from the color picker state automatically. | Prop | Type | Default | Description | | ----------- | --------------------------------------- | ------- | ---------------------------------------------------------------------------------- | | `uppercase` | `boolean` | `true` | Render the hex value in uppercase | | `children` | `ReactNode \| (({ hex }) => ReactNode)` | - | Override the displayed content; receives the resolved `hex` string when a function | Also supports all native `span` HTML attributes. ### ColorPicker.Swatch Color swatch preview. Wraps DarkCode UI [ColorSwatch](/react/components/color-swatch). Also supports all DarkCode UI `ColorSwatch` props (`size`, `shape`, etc.). ### ColorPicker.Popover Dropdown color picker panel. | Prop | Type | Default | Description | | ----------- | ----------- | --------------- | ----------------------------------------- | | `placement` | `Placement` | `'bottom left'` | Popover placement relative to the trigger | Also supports all DarkCode UI `ColorPicker.Popover` props. ## Related Components * **ColorArea**: 2D color picker for selecting colors from a gradient area * **ColorWheel**: Circular slider for selecting the hue of a color * **ColorSlider**: Slider for adjusting individual color channel values
# ColorArea **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-area **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-area.mdx > A 2D color picker that allows users to select colors from a gradient area ## Import ```tsx import { ColorArea } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ColorArea} from "@darkcode-ui/react"; export function ColorAreaBasic() { return ( ); } ``` ### Anatomy ```tsx import { ColorArea } from '@darkcode-ui/react'; export default () => ( ); ``` ### With Dots ```tsx import {ColorArea} from "@darkcode-ui/react"; export function ColorAreaWithDots() { return ( ); } ``` ### Controlled ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {ColorArea, ColorSwatch, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function ColorAreaControlled() { const [color, setColor] = useState(parseColor("#9B80FF")); return (

Current color:{" "} {color ? color.toString("hex") : "(empty)"}

); } ``` ### Color Space & Channels Use `colorSpace` to set the color space (RGB, HSL, HSB) and `xChannel`/`yChannel` props to customize which color channels are displayed on each axis. ```tsx "use client"; import type {ColorSpace, Key} from "@darkcode-ui/react"; import {ColorArea, Label, ListBox, Select, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; type ColorChannel = "hue" | "saturation" | "brightness" | "lightness" | "red" | "green" | "blue"; interface ChannelOption { id: ColorChannel; name: string; } const colorSpaces: Array<{id: ColorSpace; name: string}> = [ {id: "rgb", name: "RGB"}, {id: "hsl", name: "HSL"}, {id: "hsb", name: "HSB"}, ]; const channelsBySpace: Record = { hsb: [ {id: "hue", name: "Hue"}, {id: "saturation", name: "Saturation"}, {id: "brightness", name: "Brightness"}, ], hsl: [ {id: "hue", name: "Hue"}, {id: "saturation", name: "Saturation"}, {id: "lightness", name: "Lightness"}, ], rgb: [ {id: "red", name: "Red"}, {id: "green", name: "Green"}, {id: "blue", name: "Blue"}, ], }; export function ColorAreaSpaceAndChannels() { const [colorSpace, setColorSpace] = useState("hsb"); const [color, setColor] = useState(() => parseColor("hsb(219, 58%, 93%)")); const channels = channelsBySpace[colorSpace]; const defaultX = colorSpace === "rgb" ? "blue" : "saturation"; const defaultY = colorSpace === "rgb" ? "green" : colorSpace === "hsl" ? "lightness" : "brightness"; const [xChannel, setXChannel] = useState(defaultX); const [yChannel, setYChannel] = useState(defaultY); const handleColorSpaceChange = (newSpace: Key | null) => { if (!newSpace) return; const space = newSpace as ColorSpace; setColorSpace(space); // Reset channels to appropriate defaults for the new color space if (space === "rgb") { setXChannel("blue"); setYChannel("green"); } else if (space === "hsl") { setXChannel("saturation"); setYChannel("lightness"); } else { setXChannel("saturation"); setYChannel("brightness"); } }; // Filter out the other channel from options (can't have same channel on both axes) const xChannelOptions = channels.filter((c) => c.id !== yChannel); const yChannelOptions = channels.filter((c) => c.id !== xChannel); return (
{/* Controls */}
{/* Color Space Select */} {/* X Channel Select */} {/* Y Channel Select */}
{/* Color Area */} {/* Color Value Display */}
{color.toString(colorSpace)}
); } ``` ### Disabled ```tsx import {ColorArea} from "@darkcode-ui/react"; export function ColorAreaDisabled() { return ( ); } ``` ### Custom Render Function ```tsx "use client"; import {ColorArea} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
} >
} /> ); } ``` ## Related Components * **ColorSwatch**: Visual preview of a color value * **ColorSwatchPicker**: Color swatch selection from a list of colors * **ColorField**: Input for entering color values with hex format ## Styling ### Passing Tailwind CSS classes ```tsx import { ColorArea } from '@darkcode-ui/react'; function CustomColorArea() { return ( ); } ``` ### Customizing the component classes To customize the ColorArea component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .color-area { @apply rounded-3xl; } .color-area__thumb { @apply size-5 border-4; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ColorArea component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-area.css)): #### Base Classes * `.color-area` - Base styles with gradient background and inner shadow * `.color-area--show-dots` - Adds dot grid overlay for precision picking #### Element Classes * `.color-area__thumb` - Draggable thumb indicator ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Disabled**: `[data-disabled="true"]` * **Focus**: `[data-focus-visible="true"]` * **Dragging**: `[data-dragging="true"]` (thumb only) ## API Reference ### ColorArea Props Inherits from [React Aria ColorArea](https://react-spectrum.adobe.com/react-aria/ColorArea.html). | Prop | Type | Default | Description | | -------------- | ---------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------- | | `value` | `string \| Color` | - | The current color value (controlled) | | `defaultValue` | `string \| Color` | - | The default color value (uncontrolled) | | `onChange` | `(color: Color) => void` | - | Handler called when the color changes while dragging | | `onChangeEnd` | `(color: Color) => void` | - | Handler called when the user stops dragging | | `xChannel` | `ColorChannel` | `"saturation"` | Color channel for the horizontal axis | | `yChannel` | `ColorChannel` | `"brightness"` | Color channel for the vertical axis | | `colorSpace` | `ColorSpace` | - | The color space for the channels | | `isDisabled` | `boolean` | `false` | Whether the color area is disabled | | `showDots` | `boolean` | `false` | Whether to show the dot grid overlay | | `className` | `string` | - | Additional CSS classes | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ColorArea.Thumb Props | Prop | Type | Default | Description | | ----------- | ----------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `style` | `CSSProperties \| ((renderProps) => CSSProperties)` | - | Inline styles or render props function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | # ColorField **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-field **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-field.mdx > Color input field with labels, descriptions, and validation built on React Aria ColorField ## Import ```tsx import { ColorField, parseColor } from '@darkcode-ui/react'; ``` ### Usage ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {ColorField, ColorSwatch, Label, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function Basic() { const [color, setColor] = useState(parseColor("#0485F7")); return ( ); } ``` ### Anatomy ```tsx import {ColorField, Label, ColorSwatch, Description, FieldError, parseColor} from '@darkcode-ui/react'; export default () => ( ) ``` > **ColorField** combines label, color input, description, and error into a single accessible component. ### With Description ```tsx import {ColorField, Description, Label} from "@darkcode-ui/react"; export function WithDescription() { return (
Enter your brand's primary color Used for highlights and CTAs
); } ``` ### Required Field ```tsx import {ColorField, Description, Label} from "@darkcode-ui/react"; export function Required() { return (
Required field
); } ``` ### Validation Use `isInvalid` together with `FieldError` to surface validation messages. ```tsx import {ColorField, FieldError, Label} from "@darkcode-ui/react"; export function Invalid() { return (
Please enter a valid hex color Invalid color format. Use hex (e.g., #FF5733)
); } ``` ### Channel Editing ColorField supports editing individual color channels (hue, saturation, lightness, red, green, blue, alpha) by setting the `colorSpace` and `channel` props. ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {ColorField, ColorSwatch, Label, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function ChannelEditing() { const [color, setColor] = useState(parseColor("#7F007F")); return (

Edit individual HSL channels:

% %
Current: {color ? color.toString("hex") : "(empty)"}
); } ``` ### Controlled Control the value to synchronize with other components or state management. ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {Button, ColorField, ColorSwatch, Description, Label, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function Controlled() { const [value, setValue] = useState(parseColor("#0485F7")); return (
Current value: {value ? value.toString("hex") : "(empty)"}
); } ``` ### Disabled State ```tsx "use client"; import {ColorField, Description, Label} from "@darkcode-ui/react"; export function Disabled() { return (
This color field is disabled This color field is disabled
); } ``` ### Full Width ```tsx import {ColorField, Label} from "@darkcode-ui/react"; export function FullWidth() { return (
); } ``` ### Variants The ColorField.Group component supports two visual variants: * **`primary`** (default) - Standard styling with shadow, suitable for most use cases * **`secondary`** - Lower emphasis variant without shadow, suitable for use in Surface components ```tsx import {ColorField, Label} from "@darkcode-ui/react"; export function Variants() { return (
); } ``` ### On Surface When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` on ColorField.Group to apply the lower emphasis variant suitable for surface backgrounds. ```tsx import {ColorField, Description, Label, Surface} from "@darkcode-ui/react"; export function OnSurface() { return ( Select your theme color ); } ``` ### Form Example Complete form example with validation and submission handling. ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {Button, ColorField, ColorSwatch, Description, Form, Label} from "@darkcode-ui/react"; import {useState} from "react"; export function FormExample() { const [value, setValue] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!value) { return; } setIsSubmitting(true); // Simulate API call setTimeout(() => { console.log("Color submitted:", {color: value.toString("hex")}); setValue(null); setIsSubmitting(false); }, 1500); }; return (
Choose your brand's primary color
); } ``` ### Custom Render Function ```tsx "use client"; import type {Color} from "@darkcode-ui/react"; import {ColorField, ColorSwatch, Label, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function CustomRenderFunction() { const [color, setColor] = useState(parseColor("#0485F7")); return (
} value={color} onChange={setColor} >
}> ); } ``` ## Related Components * **ColorSwatch**: Visual preview of a color value * **ColorSwatchPicker**: Color swatch selection from a list of colors * **ColorPicker**: Composable color picker with popover ## Styling ### Passing Tailwind CSS classes ```tsx import {ColorField, Label, ColorSwatch, Description} from '@darkcode-ui/react'; function CustomColorField() { return ( Select your brand's primary color. ); } ``` ### Customizing the component classes ColorField has minimal default styling. Override the `.color-field` class to customize the container styling. ```css @layer components { .color-field { @apply flex flex-col gap-1; &[data-invalid="true"], &[aria-invalid="true"] { [data-slot="description"] { @apply hidden; } } [data-slot="label"] { @apply w-fit; } [data-slot="description"] { @apply px-1; } } } ``` ### CSS Classes * `.color-field` – Root container with minimal styling (`flex flex-col gap-1`) > **Note:** Child components ([Label](/docs/components/label), [Description](/docs/components/description), [FieldError](/docs/components/field-error)) have their own CSS classes and styling. See their respective documentation for customization options. ColorField.Group styling is documented below in the API Reference section. ### Interactive States ColorField automatically manages these data attributes based on its state: * **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` - Automatically hides the description slot when invalid * **Required**: `[data-required="true"]` - Applied when `isRequired` is true * **Disabled**: `[data-disabled="true"]` - Applied when `isDisabled` is true * **Focus Within**: `[data-focus-within="true"]` - Applied when any child input is focused ## API Reference ### ColorField Props ColorField inherits all props from React Aria's [ColorField](https://react-aria.adobe.com/ColorField.md) component. #### Base Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------- | | `children` | `React.ReactNode \| (values: ColorFieldRenderProps) => React.ReactNode` | - | Child components (Label, ColorField.Group, etc.) or render function. | | `className` | `string \| (values: ColorFieldRenderProps) => string` | - | CSS classes for styling, supports render props. | | `style` | `React.CSSProperties \| (values: ColorFieldRenderProps) => React.CSSProperties` | - | Inline styles, supports render props. | | `fullWidth` | `boolean` | `false` | Whether the color field should take full width of its container | | `id` | `string` | - | The element's unique identifier. | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | #### Value Props | Prop | Type | Default | Description | | -------------- | -------------------------------- | ------- | -------------------------------------- | | `value` | `Color \| null` | - | Current value (controlled). | | `defaultValue` | `Color \| null` | - | Default value (uncontrolled). | | `onChange` | `(color: Color \| null) => void` | - | Handler called when the value changes. | #### Channel Props | Prop | Type | Default | Description | | ------------ | -------------- | ------- | ---------------------------------------------------------------------------- | | `colorSpace` | `ColorSpace` | - | The color space that the color field operates in when `channel` is provided. | | `channel` | `ColorChannel` | - | The color channel to edit. If not provided, edits hex value. | #### Validation Props | Prop | Type | Default | Description | | -------------------- | ---------------------------------------------------------------- | ---------- | -------------------------------------------------------------- | | `isRequired` | `boolean` | `false` | Whether user input is required before form submission. | | `isInvalid` | `boolean` | - | Whether the value is invalid. | | `validate` | `(value: Color) => ValidationError \| true \| null \| undefined` | - | Custom validation function. | | `validationBehavior` | `'native' \| 'aria'` | `'native'` | Whether to use native HTML form validation or ARIA attributes. | #### State Props | Prop | Type | Default | Description | | ----------------- | --------- | ------- | -------------------------------------------------- | | `isDisabled` | `boolean` | - | Whether the input is disabled. | | `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed. | | `isWheelDisabled` | `boolean` | - | Whether to disable changing the value with scroll. | #### Form Props | Prop | Type | Default | Description | | ----------- | --------- | ------- | ---------------------------------------------------- | | `name` | `string` | - | Name of the input element, for HTML form submission. | | `autoFocus` | `boolean` | - | Whether the element should receive focus on render. | #### Accessibility Props | Prop | Type | Default | Description | | ------------------ | -------- | ------- | ----------------------------------------------------- | | `aria-label` | `string` | - | Accessibility label when no visible label is present. | | `aria-labelledby` | `string` | - | ID of elements that label this field. | | `aria-describedby` | `string` | - | ID of elements that describe this field. | | `aria-details` | `string` | - | ID of elements with additional details. | ### Composition Components ColorField works with these separate components that should be imported and used directly: * **Label** - Field label component from `@darkcode-ui/react` * **ColorField.Group** - Color input group component (documented below) * **ColorField.Input** - Input element within ColorField.Group * **ColorField.Prefix** / **ColorField.Suffix** - Prefix and suffix slots for the input group * **ColorSwatch** - Color preview component from `@darkcode-ui/react` * **Description** - Helper text component from `@darkcode-ui/react` * **FieldError** - Validation error message from `@darkcode-ui/react` Each of these components has its own props API. Use them directly within ColorField for composition: ```tsx import {ColorField, Label, ColorSwatch, Description, FieldError, parseColor} from '@darkcode-ui/react'; Select your brand's primary color. Please enter a valid color. ``` ### Color Types ColorField uses `Color` objects from React Aria Components: ```tsx import {parseColor} from '@darkcode-ui/react'; // Parse from hex string const color = parseColor('#3B82F6'); // Get hex string from color const hex = color.toString('hex'); // "#3b82f6" // Get RGB values const rgb = color.toString('rgb'); // "rgb(59, 130, 246)" // Use in ColorField {/* ... */} ``` ### ColorFieldRenderProps When using render props with `className`, `style`, or `children`, these values are available: | Prop | Type | Description | | ---------------- | --------- | ----------------------------------------------- | | `isDisabled` | `boolean` | Whether the field is disabled. | | `isInvalid` | `boolean` | Whether the field is currently invalid. | | `isReadOnly` | `boolean` | Whether the field is read-only. | | `isRequired` | `boolean` | Whether the field is required. | | `isFocused` | `boolean` | Whether the field is currently focused. | | `isFocusWithin` | `boolean` | Whether any child element is focused. | | `isFocusVisible` | `boolean` | Whether focus is visible (keyboard navigation). | ### ColorField.Group Props ColorField.Group accepts all props from React Aria's `Group` component plus the following: | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `className` | `string` | - | Tailwind classes merged with the component styles. | | `fullWidth` | `boolean` | `false` | Whether the color input group should take full width of its container | | `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ColorField.Input Props ColorField.Input accepts all props from React Aria's `Input` component plus the following: | Prop | Type | Default | Description | | ------------- | -------- | ------- | -------------------------------------------------- | | `className` | `string` | - | Tailwind classes merged with the component styles. | | `placeholder` | `string` | - | Placeholder text shown when empty. | ### ColorField.Prefix Props ColorField.Prefix accepts standard HTML `div` attributes: | Prop | Type | Default | Description | | ----------- | ----------- | ------- | -------------------------------------------------- | | `className` | `string` | - | Tailwind classes merged with the component styles. | | `children` | `ReactNode` | - | Content to display in the prefix slot. | ### ColorField.Suffix Props ColorField.Suffix accepts standard HTML `div` attributes: | Prop | Type | Default | Description | | ----------- | ----------- | ------- | -------------------------------------------------- | | `className` | `string` | - | Tailwind classes merged with the component styles. | | `children` | `ReactNode` | - | Content to display in the suffix slot. | ## ColorField.Group Styling ### Customizing the component classes The base classes power every instance. Override them once with `@layer components`. ```css @layer components { .color-input-group { @apply inline-flex h-9 items-center overflow-hidden rounded-field border bg-field text-sm text-field-foreground shadow-field outline-none; &:hover, &[data-hovered="true"] { @apply bg-field-hover; } &[data-focus-within="true"], &:focus-within { @apply status-focused-field; } &[data-invalid="true"] { @apply status-invalid-field; } &[data-disabled="true"], &[aria-disabled="true"] { @apply status-disabled; } } .color-input-group__input { @apply flex flex-1 items-center rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none; } .color-input-group__prefix, .color-input-group__suffix { @apply shrink-0 text-field-placeholder flex items-center; } } ``` ### ColorField.Group CSS Classes * `.color-input-group` – Root container styling * `.color-input-group__input` – Input wrapper styling * `.color-input-group__prefix` – Prefix element styling * `.color-input-group__suffix` – Suffix element styling ### ColorField.Group Interactive States * **Hover**: `:hover` or `[data-hovered="true"]` * **Focus Within**: `[data-focus-within="true"]` or `:focus-within` * **Invalid**: `[data-invalid="true"]` (also syncs with `aria-invalid`) * **Disabled**: `[data-disabled="true"]` or `[aria-disabled="true"]` # ColorPicker **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-picker **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-picker.mdx > A composable color picker that synchronizes color value between multiple color components ## Import ```tsx import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, ColorField, ColorSwatchPicker, } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; export function Basic() { return ( ); } ``` ### Anatomy The ColorPicker is a composable component that combines multiple color components: ```tsx import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, Label } from '@darkcode-ui/react'; export default () => ( ); ``` ### Controlled ```tsx "use client"; import { Button, ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, Label, parseColor, } from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; export function Controlled() { const [color, setColor] = useState(parseColor("#325578")); const colorPresets = [ "#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#8b5cf6", "#ec4899", "#f43f5e", ]; const shuffleColor = () => { const randomHue = Math.floor(Math.random() * 360); const randomSaturation = 50 + Math.floor(Math.random() * 50); // 50-100% const randomLightness = 40 + Math.floor(Math.random() * 30); // 40-70% setColor(parseColor(`hsl(${randomHue}, ${randomSaturation}%, ${randomLightness}%)`)); }; return (
{colorPresets.map((preset) => ( ))}

Selected: {color.toString("hex")}

); } ``` ### With Swatches ```tsx import { ColorArea, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker, Label, } from "@darkcode-ui/react"; export function WithSwatches() { const presets = [ "#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#8b5cf6", "#ec4899", "#f43f5e", ]; return ( {presets.map((preset) => ( ))} ); } ``` ### With Fields Use `ColorField` to allow users to edit individual color channel values with a `Select` to switch between color spaces. ```tsx "use client"; import type {ColorChannel, ColorSpace} from "@darkcode-ui/react"; import { ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch, Label, ListBox, Select, } from "@darkcode-ui/react"; import {useState} from "react"; export function WithFields() { const [colorSpace, setColorSpace] = useState("hsl"); const colorChannelsByColorSpace: Record = { hsb: ["hue", "saturation", "brightness"], hsl: ["hue", "saturation", "lightness"], rgb: ["red", "green", "blue"], }; return (
{colorChannelsByColorSpace[colorSpace].map((channel) => ( ))}
); } ``` ### With Sliders Use multiple `ColorSlider` components to adjust each channel of a color value. ```tsx "use client"; import type {ColorChannel, ColorSpace} from "@darkcode-ui/react"; import {ColorPicker, ColorSlider, ColorSwatch, Label, ListBox, Select} from "@darkcode-ui/react"; import {useState} from "react"; export function WithSliders() { const [colorSpace, setColorSpace] = useState("hsl"); const colorChannelsByColorSpace: Record = { hsb: ["hue", "saturation", "brightness", "alpha"], hsl: ["hue", "saturation", "lightness", "alpha"], rgb: ["red", "green", "blue", "alpha"], }; return (
{colorChannelsByColorSpace[colorSpace].map((channel: ColorChannel) => ( // @ts-expect-error - TypeScript can't correlate dynamic colorSpace with channel type ))}
); } ``` ### Eyedropper Add `ColorPicker.EyeDropper` to let users sample a color from anywhere on screen using the native [EyeDropper API](https://developer.mozilla.org/en-US/docs/Web/API/EyeDropper). The button automatically hides where the API is unavailable (set `unsupportedBehavior="disable"` to render it disabled instead). Place it inside the `Popover`, never inside the `Trigger`. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; export function WithEyeDropper() { return (
); } ``` ### Recent colors Set `recordHistory` on the root and drop in `ColorPicker.Recent` to show a persisted row of recently used colors. History is stored in `localStorage` and powered by the [`useColorHistory`](#usecolorhistory) hook. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; const recentColors = ["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6", "#8b5cf6"]; export function WithRecent() { return ( ); } ``` ### Copy to clipboard `ColorPicker.CopyButton` copies the current color (hex by default) and shows a transient check. Pass `format` to copy `rgb`, `hsl`, `hexa`, etc. ```tsx "use client"; import {ColorArea, ColorPicker, ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; export function WithCopy() { return (
); } ``` ## Related Components * **ColorArea**: 2D color picker for selecting colors from a gradient area * **ColorWheel**: Circular slider for selecting the hue of a color * **ColorSlider**: Slider for adjusting individual color channel values ## Styling ### Passing Tailwind CSS classes ```tsx import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, Label } from '@darkcode-ui/react'; function CustomColorPicker() { return ( ); } ``` ### Customizing the component classes To customize the ColorPicker component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .color-picker { @apply inline-flex; } .color-picker__trigger { @apply inline-flex items-center gap-4 rounded-lg; } .color-picker__popover { @apply p-4 rounded-xl; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ColorPicker component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-picker.css)): #### Base Classes * `.color-picker` - Base container * `.color-picker__trigger` - Trigger button * `.color-picker__popover` - Popover container ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` * **Disabled**: `:disabled` or `[data-disabled="true"]` ## API Reference ### ColorPicker Props Inherits from [React Aria ColorPicker](https://react-spectrum.adobe.com/react-aria/ColorPicker.html). | Prop | Type | Default | Description | | ---------------- | ------------------------ | ------- | --------------------------------------------------------------------------------------- | | `value` | `string \| Color` | - | The current color value (controlled) | | `defaultValue` | `string \| Color` | - | The default color value (uncontrolled) | | `onChange` | `(color: Color) => void` | - | Handler called when the color changes | | `recordHistory` | `boolean` | `false` | Track the committed color in a recent-colors history (consumed by `ColorPicker.Recent`) | | `historyOptions` | `UseColorHistoryOptions` | - | Options forwarded to the underlying `useColorHistory` hook | | `children` | `React.ReactNode` | - | Content of the color picker (Trigger, Popover, etc.) | | `className` | `string` | - | Additional CSS classes | ### ColorPicker.Trigger Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------- | ------- | ------------------------------ | | `children` | `React.ReactNode \| ((renderProps) => React.ReactNode)` | - | Trigger content or render prop | | `className` | `string` | - | Additional CSS classes | ### ColorPicker.Popover Props | Prop | Type | Default | Description | | ----------- | ----------------- | --------------- | ------------------------ | | `placement` | `Placement` | `"bottom left"` | Placement of the popover | | `children` | `React.ReactNode` | - | Popover content | | `className` | `string` | - | Additional CSS classes | ### ColorPicker.EyeDropper Props Wraps React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html). Must be placed inside the `Popover`. | Prop | Type | Default | Description | | --------------------- | --------------------- | -------- | -------------------------------------------------- | | `unsupportedBehavior` | `'hide' \| 'disable'` | `'hide'` | What to do where the EyeDropper API is unavailable | | `children` | `React.ReactNode` | - | Override the icon/content | ### ColorPicker.CopyButton Props | Prop | Type | Default | Description | | ----------- | ----------------------------------------------------------------------------------- | ------- | ----------------------------------- | | `format` | `'hex' \| 'hexa' \| 'css' \| 'rgb' \| 'hsl' \| 'hsb' \| ((color: Color) => string)` | `'hex'` | The value copied to the clipboard | | `uppercase` | `boolean` | `true` | Uppercase the value for hex formats | | `children` | `React.ReactNode \| (({ copied }) => React.ReactNode)` | - | Override the button content | ### ColorPicker.Recent Props | Prop | Type | Default | Description | | --------------- | ------------------------- | ------- | ------------------------------------------------------ | | `colors` | `string[]` | - | Explicit colors; falls back to the ColorPicker history | | `onColorSelect` | `(color: string) => void` | - | Called on select; defaults to applying the color | | `label` | `React.ReactNode` | - | Optional heading above the swatches | | `size` | `'xs' \| 'sm' \| 'md'` | `'xs'` | Swatch size | ### Hooks These reusable hooks power the parts above and can be used standalone. #### useColorHistory `useColorHistory({ storageKey?, maxLength?, colors?, defaultColors?, onChange? }) → { colors, addColor, clear }`. Tracks recently used colors in `localStorage` (deduped, newest first, capped at `maxLength`, default `8`). Pass `storageKey: null` to keep history in memory only. #### useEyeDropper `useEyeDropper({ onResult?, onError? }) → { isSupported, isOpen, open }`. Feature-detects and opens the native EyeDropper. `isSupported` is `false` during SSR and on unsupported browsers. #### useClipboard `useClipboard({ timeout? }) → { copy, copied, error }`. Copies text with a transient `copied` flag that resets after `timeout` ms (default `2000`). ### Related Types #### Color Represents a color value. See [React Aria Color](https://react-spectrum.adobe.com/react-aria/ColorPicker.html#color) for full API. | Method | Description | | ---------------------------------- | ---------------------------------------------------------------------------- | | `toString(format)` | Converts the color to a string in the given format (hex, rgb, hsl, hsb, css) | | `toFormat(format)` | Converts the color to the given format and returns a new Color object | | `getChannelValue(channel)` | Returns the numeric value for a given channel | | `withChannelValue(channel, value)` | Sets the numeric value for a channel and returns a new Color | #### parseColor ```tsx import { parseColor } from 'react-aria-components'; // Parse from string const color = parseColor('#ff0000'); const hslColor = parseColor('hsl(0, 100%, 50%)'); ```
# ColorSlider **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-slider **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-slider.mdx > A color slider allows users to adjust an individual channel of a color value ## Import ```tsx import { ColorSlider, Label } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ColorSlider, Label} from "@darkcode-ui/react"; export function Basic() { return ( ); } ``` ### Anatomy Import the ColorSlider component and access all parts using dot notation. ```tsx import { ColorSlider, Label } from '@darkcode-ui/react'; export default () => ( ) ``` ### Vertical ```tsx import {ColorSlider} from "@darkcode-ui/react"; export function Vertical() { return (
); } ``` ### Disabled ```tsx import {ColorSlider, Label} from "@darkcode-ui/react"; export function Disabled() { return ( ); } ``` ### Controlled ```tsx "use client"; import {ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; import {useState} from "react"; import {parseColor} from "react-aria-components"; export function Controlled() { const [color, setColor] = useState(parseColor("hsl(200, 100%, 50%)")); return (

Current color: {color.toString("hsl")}

); } ``` ### HSL Channels Use multiple ColorSliders to control different channels of a color value. The sliders can share the same color value to create a complete color picker. ```tsx "use client"; import {ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; import {useState} from "react"; import {parseColor} from "react-aria-components"; export function Channels() { const [color, setColor] = useState(parseColor("hsl(0, 100%, 50%)")); return (

Current color: {color.toString("hsl")}

); } ``` ### Alpha Channel The alpha channel slider shows a transparency checkerboard pattern to help visualize the transparency level. ```tsx import {ColorSlider, Label} from "@darkcode-ui/react"; export function AlphaChannel() { return ( ); } ``` ### RGB Channels You can also use RGB color space with red, green, and blue channels. ```tsx "use client"; import {ColorSlider, ColorSwatch, Label} from "@darkcode-ui/react"; import {useState} from "react"; import {parseColor} from "react-aria-components"; export function RGBChannels() { const [color, setColor] = useState(parseColor("rgb(255, 100, 50)")); return (

Current color: {color.toString("rgb")}

); } ``` ### Custom Render Function ```tsx "use client"; import {ColorSlider, Label} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
} > ); } ``` ## Related Components * **ColorSwatch**: Visual preview of a color value * **ColorSwatchPicker**: Color swatch selection from a list of colors * **ColorPicker**: Composable color picker with popover ## Styling ### Passing Tailwind CSS classes ```tsx import { ColorSlider, Label } from '@darkcode-ui/react'; function CustomColorSlider() { return ( ); } ``` ### Customizing the component classes To customize the ColorSlider component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .color-slider { @apply flex flex-col gap-2; } .color-slider__output { @apply text-muted text-sm; } .color-slider__track { @apply relative h-5 w-full rounded-full; } .color-slider__thumb { @apply size-4 rounded-full border-3 border-white shadow-overlay; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ColorSlider component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-slider.css)): #### Base Classes * `.color-slider` - Base slider container * `.color-slider__output` - Output element displaying current value * `.color-slider__track` - Track element with color gradient * `.color-slider__thumb` - Thumb element showing current color #### State Classes * `.color-slider[data-disabled="true"]` - Disabled slider state * `.color-slider[data-orientation="vertical"]` - Vertical orientation * `.color-slider__thumb[data-dragging="true"]` - Thumb being dragged * `.color-slider__thumb[data-focus-visible="true"]` - Thumb keyboard focused * `.color-slider__thumb[data-disabled="true"]` - Disabled thumb state ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` on thumb * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on thumb * **Dragging**: `[data-dragging="true"]` on thumb * **Disabled**: `:disabled` or `[data-disabled="true"]` on slider or thumb ## API Reference ### ColorSlider Props Inherits from [React Aria ColorSlider](https://react-spectrum.adobe.com/react-aria/ColorSlider.html). | Prop | Type | Default | Description | | -------------- | ------------------------------------------------------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------- | | `channel` | `ColorChannel` | - | The color channel that the slider manipulates (hue, saturation, lightness, brightness, alpha, red, green, blue) | | `colorSpace` | `ColorSpace` | - | The color space (hsl, hsb, rgb). Defaults to the color space of the value | | `value` | `string \| Color` | - | The current color value (controlled) | | `defaultValue` | `string \| Color` | - | The default color value (uncontrolled) | | `onChange` | `(value: Color) => void` | - | Handler called when the value changes during dragging | | `onChangeEnd` | `(value: Color) => void` | - | Handler called when dragging ends | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The orientation of the slider | | `isDisabled` | `boolean` | - | Whether the slider is disabled | | `name` | `string` | - | The name of the input element for form submission | | `aria-label` | `string` | - | Accessibility label for the slider | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Slider content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ColorSlider.Output Props | Prop | Type | Default | Description | | ----------- | ----------------------------- | ------- | --------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Output content or render function | ### ColorSlider.Track Props | Prop | Type | Default | Description | | ----------- | --------------------------------- | ------- | -------------------------------- | | `className` | `string` | - | Additional CSS classes | | `style` | `CSSProperties \| RenderFunction` | - | Inline styles or render function | | `children` | `ReactNode \| RenderFunction` | - | Track content or render function | ### ColorSlider.Thumb Props | Prop | Type | Default | Description | | ----------- | --------------------------------- | ------- | -------------------------------- | | `className` | `string` | - | Additional CSS classes | | `style` | `CSSProperties \| RenderFunction` | - | Inline styles or render function | | `children` | `ReactNode \| RenderFunction` | - | Thumb content or render function | ### RenderProps When using render functions, these values are provided: | Prop | Type | Description | | ------------- | ---------------------------- | ------------------------------ | | `state` | `ColorSliderState` | The state of the color slider | | `color` | `Color` | The current color value | | `orientation` | `"horizontal" \| "vertical"` | The orientation of the slider | | `isDisabled` | `boolean` | Whether the slider is disabled | ## Accessibility The ColorSlider component implements the ARIA slider pattern and provides: * Full keyboard navigation support (Arrow keys, Home, End, Page Up/Down) * Screen reader announcements for value changes * Proper focus management * Support for disabled states * HTML form integration via hidden input elements * Internationalization support with locale-aware value formatting For more information, see the [React Aria ColorSlider documentation](https://react-spectrum.adobe.com/react-aria/ColorSlider.html). # ColorSwatchPicker **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-swatch-picker **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-swatch-picker.mdx > A list of color swatches that allows users to select a color from a predefined palette. ## Import ```tsx import { ColorSwatchPicker, parseColor } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function Basic() { return ( {colors.map((color) => ( ))} ); } ``` ### Anatomy Import the ColorSwatchPicker component and access all parts using dot notation. ```tsx import { ColorSwatchPicker } from '@darkcode-ui/react'; export default () => ( ); ``` ### Variants ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function Variants() { return (
Circle (default) {colors.map((color) => ( ))}
Square {colors.map((color) => ( ))}
); } ``` ### Sizes ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; const sizes = ["xs", "sm", "md", "lg", "xl"] as const; export function Sizes() { return (
{sizes.map((size) => (
{size} {colors.map((color) => ( ))}
))}
); } ``` ### Stack Layout ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function StackLayout() { return ( {colors.map((color) => ( ))} ); } ``` ### Default Value ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function DefaultValue() { return ( {colors.map((color) => ( ))} ); } ``` ### Controlled ```tsx "use client"; import {ColorSwatchPicker, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function Controlled() { const [value, setValue] = useState(parseColor("#F43F5E")); return (
{colors.map((color) => ( ))}

Selected: {value.toString("hex")}

); } ``` ### Disabled ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function Disabled() { return ( {colors.map((color) => ( ))} ); } ``` ### Custom Indicator ```tsx import {ColorSwatchPicker} from "@darkcode-ui/react"; import {HeartFill} from "@gravity-ui/icons"; export function CustomIndicator() { const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; return ( {colors.map((color) => ( ))} ); } ``` ### Custom Render Function ```tsx "use client"; import {ColorSwatchPicker} from "@darkcode-ui/react"; const colors = ["#F43F5E", "#D946EF", "#8B5CF6", "#3B82F6", "#06B6D4", "#10B981", "#84CC16"]; export function CustomRenderFunction() { return (
}> {colors.map((color) => ( ))} ); } ``` ## Related Components * **ColorSwatch**: Visual preview of a color value * **ColorField**: Input for entering color values with hex format * **ColorArea**: 2D color picker for selecting colors from a gradient area ## Styling ### Passing Tailwind CSS classes You can customize the ColorSwatchPicker using className props: ```tsx import { ColorSwatchPicker } from '@darkcode-ui/react'; function CustomColorSwatchPicker() { return ( ); } ``` ### Customizing the component classes To customize the ColorSwatchPicker component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .color-swatch-picker { @apply gap-4; } .color-swatch-picker__item { @apply shadow-md; } .color-swatch-picker__swatch { @apply border-2 border-white; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ColorSwatchPicker component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-swatch-picker.css)): #### Base & Structure * `.color-swatch-picker` - Base container (flex layout) * `.color-swatch-picker__item` - Individual swatch item wrapper * `.color-swatch-picker__swatch` - The color swatch visual element #### Size Classes * `.color-swatch-picker--xs` - Extra small (16px) * `.color-swatch-picker--sm` - Small (24px) * `.color-swatch-picker--md` - Medium (32px, default) * `.color-swatch-picker--lg` - Large (36px) * `.color-swatch-picker--xl` - Extra large (40px) #### Shape Variants * `.color-swatch-picker--circle` - Circle shape (default) * `.color-swatch-picker--square` - Square shape with rounded corners #### Layout Classes * `.color-swatch-picker--grid` - Horizontal wrapping layout (default) * `.color-swatch-picker--stack` - Vertical stacked layout ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` - Scale up to 1.1 (only when not selected) * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` - Focus ring * **Selected**: `[data-selected="true"]` - Inner border with same color as swatch * **Disabled**: `[data-disabled="true"]` - Reduced opacity ## API Reference ### ColorSwatchPicker Props Inherits from [React Aria ColorSwatchPicker](https://react-spectrum.adobe.com/react-aria/ColorSwatchPicker.html). | Prop | Type | Default | Description | | -------------- | ------------------------------------------------------------------------------------ | ---------- | ---------------------------------------------------------------- | | `value` | `string \| Color` | - | The current selected color (controlled) | | `defaultValue` | `string \| Color` | - | The default selected color (uncontrolled) | | `onChange` | `(value: Color) => void` | - | Handler called when selection changes | | `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | `"md"` | Size of the swatches | | `variant` | `"circle" \| "square"` | `"circle"` | Shape of the swatches | | `layout` | `"grid" \| "stack"` | `"grid"` | Layout direction | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | ColorSwatchPicker.Item elements | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ColorSwatchPicker.Item Props | Prop | Type | Default | Description | | ------------ | ---------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------- | | `color` | `string \| Color` | **Required** | The color of the swatch | | `isDisabled` | `boolean` | `false` | Whether the item is disabled | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | ColorSwatchPicker.Swatch element | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### ColorSwatchPicker.Swatch Props | Prop | Type | Default | Description | | ----------- | -------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | ### parseColor The `parseColor` function is re-exported from React Aria Components for convenience: ```tsx import { parseColor } from '@darkcode-ui/react'; // Parse hex color const red = parseColor('#ff0000'); // Parse RGB const green = parseColor('rgb(0, 255, 0)'); // Parse HSL const blue = parseColor('hsl(240, 100%, 50%)'); ``` # ColorSwatch **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-swatch **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-swatch.mdx > A visual preview of a color value with accessibility support ## Import ```tsx import { ColorSwatch } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchBasic() { return (
); } ``` ### Sizes ```tsx import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchSizes() { return (
); } ``` ### Shapes ```tsx import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchShapes() { return (
); } ``` ### Transparency ```tsx import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchTransparency() { return (
); } ``` ### Custom Styles with Render Props You can use the `style` render props to access the color value and create custom visual effects. ```tsx "use client"; import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchCustomStyles() { const colors = ["#0485F7", "#EF4444", "#F59E0B", "#10B981", "#D946EF"]; return (
{/* Glow effect */}
Glow Effect
{colors.map((color) => ( ({ boxShadow: `0 0 20px 2px ${color}`, })} /> ))}
{/* Gradient swatch */}
Gradient
{colors.map((color) => ( ({ background: `linear-gradient(135deg, ${c.toString("css")}, white)`, })} /> ))}
); } ``` ### Accessibility Use `colorName` to provide a custom accessible name for the color, and `aria-label` to add context about how the color is used. ```tsx import {ColorSwatch} from "@darkcode-ui/react"; export function ColorSwatchAccessibility() { return (
); } ``` ### Custom Render Function ```tsx "use client"; import {ColorSwatch} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
} />
} />
} />
} />
} />
); } ``` ## Related Components * **ColorSwatchPicker**: Color swatch selection from a list of colors * **ColorField**: Input for entering color values with hex format * **ColorArea**: 2D color picker for selecting colors from a gradient area ## Styling ### Passing Tailwind CSS classes ```tsx import {ColorSwatch} from '@darkcode-ui/react'; function CustomColorSwatch() { return ( ); } ``` ### Customizing the component classes To customize the ColorSwatch component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .color-swatch { @apply border-2 border-white; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The ColorSwatch component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-swatch.css)): #### Base Classes * `.color-swatch` - Base swatch styles with checkered background for transparency #### Shape Classes * `.color-swatch--circle` - Circular shape (default) * `.color-swatch--square` - Square shape with rounded corners #### Size Classes * `.color-swatch--xs` - Extra small (16px) * `.color-swatch--sm` - Small (24px) * `.color-swatch--md` - Medium (32px, default) * `.color-swatch--lg` - Large (36px) * `.color-swatch--xl` - Extra large (40px) ## API Reference ### ColorSwatch Props | Prop | Type | Default | Description | | ------------ | ------------------------------------------------------------------------------ | ---------- | -------------------------------------------------------------------- | | `color` | `string \| Color` | - | The color value to display (hex, rgb, hsl, etc.) | | `colorName` | `string` | - | Accessible name for the color (overrides auto-generated description) | | `className` | `string` | - | Additional CSS classes | | `shape` | `"circle" \| "square"` | `"circle"` | Shape of the swatch | | `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | `"md"` | Size of the swatch | | `style` | `CSSProperties \| ((renderProps) => CSSProperties)` | - | Inline styles or render props function with access to color | | `aria-label` | `string` | - | Accessible label for the swatch | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### Style Render Props When using the `style` prop as a function, you receive render props with access to the color: ```tsx ({ boxShadow: `0 4px 14px ${color.toString("css")}80`, })} /> ``` The `color` object provides methods like: * `color.toString("css")` - Returns CSS color string * `color.toString("hex")` - Returns hex color string * `color.getChannelValue("alpha")` - Returns alpha channel value # ColorWheel **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/color-wheel **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(colors)/color-wheel.mdx > A circular slider for selecting the hue of a color value ## Import ```tsx import { ColorWheel } from '@darkcode-ui/react'; ``` ## Usage ```tsx "use client"; import {ColorWheel} from "@darkcode-ui/react"; export function Basic() { return ( ); } ``` ## Anatomy Import the ColorWheel component and access all parts using dot notation. ```tsx import { ColorWheel } from '@darkcode-ui/react'; export default () => ( ); ``` ## Controlled Pass `value` and `onChange` to control the selected hue. ```tsx "use client"; import {ColorWheel, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function Controlled() { const [color, setColor] = useState(parseColor("hsl(50, 100%, 50%)")); return (

Current hue: {color.toString("hsl")}

); } ``` ## Disabled Pass `isDisabled` to disable the wheel. ```tsx "use client"; import {ColorWheel} from "@darkcode-ui/react"; export function Disabled() { return ( ); } ``` ## With ColorArea Combine a `ColorWheel` for hue with a centered `ColorArea` for saturation and brightness to build a complete color picker. Both components share the same controlled color value. ```tsx "use client"; import {ColorArea, ColorSwatch, ColorWheel, parseColor} from "@darkcode-ui/react"; import {useState} from "react"; export function WithColorArea() { const [color, setColor] = useState(parseColor("hsl(220, 100%, 50%)")); return (
{color.toString("hex")}
); } ``` ## Related Components * **ColorArea**: 2D color picker for selecting colors from a gradient area * **ColorSlider**: Slider for adjusting individual color channel values * **ColorPicker**: Composable color picker with popover ## Styling ### CSS Classes The ColorWheel component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/color-wheel.css)): #### Base Classes * `.color-wheel` - Root wrapper * `.color-wheel__track` - Circular hue gradient track * `.color-wheel__thumb` - Draggable thumb ### Interactive States * **Focus**: `[data-focus-visible="true"]` * **Dragging**: `[data-dragging="true"]` * **Disabled**: `[data-disabled="true"]` ## API Reference ### ColorWheel Inherits from [React Aria ColorWheel](https://react-spectrum.adobe.com/react-aria/ColorWheel.html). | Prop | Type | Default | Description | | -------------- | ------------------------ | --------------------- | ------------------------------------------- | | `value` | `string \| Color` | - | The current color value (controlled) | | `defaultValue` | `string \| Color` | `'hsl(0, 100%, 50%)'` | The default color value (uncontrolled) | | `onChange` | `(color: Color) => void` | - | Handler called as the value changes | | `onChangeEnd` | `(color: Color) => void` | - | Handler called when the user stops dragging | | `outerRadius` | `number` | `100` | The outer radius of the wheel | | `innerRadius` | `number` | `74` | The inner radius of the wheel (the hole) | | `isDisabled` | `boolean` | `false` | Whether the wheel is disabled | ### ColorWheel.Track The circular gradient track. Wraps React Aria `ColorWheelTrack`. ### ColorWheel.Thumb The draggable thumb. Wraps React Aria `ColorThumb`.
# Slider **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/slider **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(controls)/slider.mdx > A slider allows a user to select one or more values within a range ## Import ```tsx import { Slider } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {Label, Slider} from "@darkcode-ui/react"; export function Default() { return ( ); } ``` ### Anatomy Import the Slider component and access all parts using dot notation. ```tsx import { Slider, Label } from '@darkcode-ui/react'; export default () => ( ) ``` ### Range Slider Anatomy ```tsx import { Slider, Label } from '@darkcode-ui/react'; export default () => ( ) ``` ### Vertical ```tsx import {Label, Slider} from "@darkcode-ui/react"; export function Vertical() { return (
); } ``` ### Range ```tsx "use client"; import {Label, Slider} from "@darkcode-ui/react"; export function Range() { return ( {({state}) => ( <> {state.values.map((_, i) => ( ))} )} ); } ``` ### Disabled ```tsx import {Label, Slider} from "@darkcode-ui/react"; export function Disabled() { return ( ); } ``` ### Custom Render Function ```tsx "use client"; import {Label, Slider} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
} > ); } ``` ## Related Components * **Label**: Accessible label for form controls * **Form**: Form validation and submission handling * **Description**: Helper text for form fields ## Styling ### Passing Tailwind CSS classes ```tsx import { Slider, Label } from '@darkcode-ui/react'; function CustomSlider() { return ( ); } ``` ### Customizing the component classes To customize the Slider component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .slider { @apply flex flex-col gap-2; } .slider__output { @apply text-muted-fg text-sm; } .slider-track { @apply relative h-2 w-full rounded-full bg-surface-secondary; } .slider-fill { @apply absolute h-full rounded-full bg-accent; } .slider-thumb { @apply size-4 rounded-full bg-accent border-2 border-background; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Slider component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/slider.css)): #### Base Classes * `.slider` - Base slider container * `.slider__output` - Output element displaying current value(s) * `.slider-track` - Track element containing fill and thumbs * `.slider-fill` - Fill element showing selected range * `.slider-thumb` - Individual thumb element #### State Classes * `.slider[data-disabled="true"]` - Disabled slider state * `.slider[data-orientation="vertical"]` - Vertical orientation * `.slider-thumb[data-dragging="true"]` - Thumb being dragged * `.slider-thumb[data-focus-visible="true"]` - Thumb keyboard focused * `.slider-thumb[data-disabled="true"]` - Disabled thumb state * `.slider-track[data-fill-start="true"]` - Fill starts at beginning * `.slider-track[data-fill-end="true"]` - Fill ends at end ### Interactive States The component supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` on thumb * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` on thumb * **Dragging**: `[data-dragging="true"]` on thumb * **Disabled**: `:disabled` or `[data-disabled="true"]` on slider or thumb ## API Reference ### Slider Props | Prop | Type | Default | Description | | ----------------- | ------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------- | | `value` | `number \| number[]` | - | The current value (controlled) | | `defaultValue` | `number \| number[]` | - | The default value (uncontrolled) | | `onChange` | `(value: number \| number[]) => void` | - | Handler called when the value changes | | `onChangeEnd` | `(value: number \| number[]) => void` | - | Handler called when dragging ends | | `minValue` | `number` | `0` | The slider's minimum value | | `maxValue` | `number` | `100` | The slider's maximum value | | `step` | `number` | `1` | The slider's step value | | `formatOptions` | `Intl.NumberFormatOptions` | - | The display format of the value label | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The orientation of the slider | | `isDisabled` | `boolean` | - | Whether the slider is disabled | | `aria-label` | `string` | - | Accessibility label for the slider | | `aria-labelledby` | `string` | - | ID of element that labels the slider | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Slider content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### Slider.Output Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Output content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### Slider.Track Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Track content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### Slider.Fill Props | Prop | Type | Default | Description | | ----------- | --------------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | | `style` | `CSSProperties` | - | Inline styles | ### Slider.Thumb Props | Prop | Type | Default | Description | | ------------ | ------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------- | | `index` | `number` | `0` | Index of the thumb within the slider | | `isDisabled` | `boolean` | - | Whether this thumb is disabled | | `name` | `string` | - | The name of the input element, used when submitting an HTML form | | `className` | `string` | - | Additional CSS classes | | `children` | `ReactNode \| RenderFunction` | - | Thumb content or render function | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### RenderProps When using render functions with Slider.Output or Slider.Track, these values are provided: | Prop | Type | Description | | -------------------- | ---------------------------- | -------------------------------------------------------- | | `state` | `SliderState` | The state of the slider | | `values` | `number[]` | Values managed by the slider by thumb index | | `getThumbValueLabel` | `(index: number) => string` | Returns the string label for the specified thumb's value | | `orientation` | `"horizontal" \| "vertical"` | The orientation of the slider | | `isDisabled` | `boolean` | Whether the slider is disabled | ## Examples ### Basic Usage ```tsx import { Slider, Label } from '@darkcode-ui/react'; ``` ### Range Slider ```tsx import { Slider, Label } from '@darkcode-ui/react'; {({state}) => ( <> {state.values.map((_, i) => ( ))} )} ``` ### Controlled Value ```tsx import { Slider, Label } from '@darkcode-ui/react'; import { useState } from 'react'; function ControlledSlider() { const [value, setValue] = useState(25); return ( <>

Current value: {value}

); } ``` ### Custom Value Formatting ```tsx import { Slider, Label } from '@darkcode-ui/react'; ``` ### Vertical Orientation ```tsx import { Slider, Label } from '@darkcode-ui/react'; ``` ### Custom Output Display ```tsx import { Slider, Label } from '@darkcode-ui/react'; {({state}) => state.values.map((_, i) => state.getThumbValueLabel(i)).join(' – ') } {({state}) => ( <> {state.values.map((_, i) => ( ))} )} ``` ## Accessibility The Slider component implements the ARIA slider pattern and provides: * Full keyboard navigation support (Arrow keys, Home, End, Page Up/Down) * Screen reader announcements for value changes * Proper focus management * Support for disabled states * HTML form integration via hidden input elements * Internationalization support with locale-aware value formatting * Right-to-left (RTL) language support For more information, see the [React Aria Slider documentation](https://react-spectrum.adobe.com/react-aria/Slider.html). # Switch **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/switch **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(controls)/switch.mdx > A toggle switch component for boolean states ## Import ```tsx import { Switch, SwitchGroup, Label } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {Label, Switch} from "@darkcode-ui/react"; export function Basic() { return ( ); } ``` ### Anatomy Import the Switch component and access all parts using dot notation. ```tsx import { Switch, Description } from '@darkcode-ui/react'; export default () => ( {/* Optional */} Label {/* Optional, sibling of Switch.Content */} ); ``` For grouping multiple switches, use the `SwitchGroup` component: ```tsx import { Switch, SwitchGroup, Label } from '@darkcode-ui/react'; export default () => ( ); ``` ### Disabled ```tsx import {Label, Switch} from "@darkcode-ui/react"; export function Disabled() { return ( ); } ``` ### Default Selected ```tsx import {Label, Switch} from "@darkcode-ui/react"; export function DefaultSelected() { return ( ); } ``` ### Controlled ```tsx "use client"; import {Label, Switch} from "@darkcode-ui/react"; import React from "react"; export function Controlled() { const [isSelected, setIsSelected] = React.useState(false); return (

Switch is {isSelected ? "on" : "off"}

); } ``` ### Without Label ```tsx import {Switch} from "@darkcode-ui/react"; export function WithoutLabel() { return ( ); } ``` ### Sizes ```tsx import {Label, Switch} from "@darkcode-ui/react"; export function Sizes() { return (
); } ``` ### Label Position ```tsx import {Label, Switch} from "@darkcode-ui/react"; export function LabelPosition() { return (
); } ``` ### With Icons ```tsx "use client"; import {Switch} from "@darkcode-ui/react"; import { BellFill, BellSlash, Check, Microphone, MicrophoneSlash, Moon, Power, Sun, VolumeFill, VolumeSlashFill, } from "@gravity-ui/icons"; export function WithIcons() { const icons = { check: { off: Power, on: Check, selectedControlClass: "bg-green-500/80", }, darkMode: { off: Moon, on: Sun, selectedControlClass: "", }, microphone: { off: Microphone, on: MicrophoneSlash, selectedControlClass: "bg-red-500/80", }, notification: { off: BellSlash, on: BellFill, selectedControlClass: "bg-purple-500/80", }, volume: { off: VolumeFill, on: VolumeSlashFill, selectedControlClass: "bg-blue-500/80", }, }; return (
{Object.entries(icons).map(([key, value]) => ( {({isSelected}) => ( <> {isSelected ? ( ) : ( )} )} ))}
); } ``` ### With Description ```tsx import {Description, Label, Switch} from "@darkcode-ui/react"; export function WithDescription() { return (
Allow others to see your profile information
); } ``` ### Group ```tsx import {Label, Switch, SwitchGroup} from "@darkcode-ui/react"; export function Group() { return ( ); } ``` ### Group Horizontal ```tsx import {Label, Switch, SwitchGroup} from "@darkcode-ui/react"; export function GroupHorizontal() { return ( ); } ``` ### Render Props ```tsx "use client"; import {Label, Switch} from "@darkcode-ui/react"; export function RenderProps() { return ( {({isSelected}) => ( <> )} ); } ``` ### Form Integration ```tsx "use client"; import {Button, Label, Switch, SwitchGroup} from "@darkcode-ui/react"; import React from "react"; export function Form() { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.target as HTMLFormElement); alert( `Form submitted with:\n${Array.from(formData.entries()) .map(([key, value]) => `${key}: ${value}`) .join("\n")}`, ); }; return (
); } ``` ### Custom Styles ```tsx "use client"; import {Switch} from "@darkcode-ui/react"; import {Check, Power} from "@gravity-ui/icons"; export function CustomStyles() { return ( {({isSelected}) => ( <> {isSelected ? ( ) : ( )} )} ); } ``` ### Custom Render Function ```tsx "use client"; import {Switch} from "@darkcode-ui/react"; export function CustomRenderFunction() { return (
}> Enable notifications ); } ``` ## Related Components * **Label**: Accessible label for form controls * **Description**: Helper text for form fields * **Button**: Allows a user to perform an action ## Styling ### Passing Tailwind CSS classes You can customize individual Switch components: ```tsx import { Switch, Label } from '@darkcode-ui/react'; function CustomSwitch() { return ( {({isSelected}) => ( <> )} ); } ``` Or customize the SwitchGroup layout: ```tsx import { Switch, SwitchGroup, Label } from '@darkcode-ui/react'; function CustomSwitchGroup() { return ( ); } ``` ### Customizing the component classes To customize the Switch component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .switch { @apply inline-flex gap-3 items-center; } .switch__control { @apply h-5 w-8 bg-gray-400 data-[selected=true]:bg-blue-500; } .switch__thumb { @apply bg-white shadow-sm; } .switch__content { @apply flex flex-col gap-1; } .switch__icon { @apply h-3 w-3 text-current; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes #### Switch Classes The Switch component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/switch.css)): * `.switch` - Base switch container * `.switch__content` - Optional content container * `.switch__control` - Switch control track * `.switch__thumb` - Switch thumb that moves * `.switch__icon` - Optional icon inside the thumb * `.switch--sm` - Small size variant * `.switch--md` - Medium size variant (default) * `.switch--lg` - Large size variant #### SwitchGroup Classes The SwitchGroup component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/switch-group.css)): * `.switch-group` - Switch group container * `.switch-group__items` - Container for switch items * `.switch-group--horizontal` - Horizontal layout * `.switch-group--vertical` - Vertical layout (default) ### Interactive States The switch supports both CSS pseudo-classes and data attributes for flexibility: * **Selected**: `[data-selected="true"]` (thumb position and background color change) * **Hover**: `:hover` or `[data-hovered="true"]` * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring) * **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events) * **Pressed**: `:active` or `[data-pressed="true"]` ## API Reference ### Switch Props Inherits from [React Aria Switch](https://react-spectrum.adobe.com/react-aria/Switch.html). | Prop | Type | Default | Description | | ----------------- | ------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | The size of the switch | | `isSelected` | `boolean` | `false` | Whether the switch is on | | `defaultSelected` | `boolean` | `false` | Whether the switch is on by default (uncontrolled) | | `isDisabled` | `boolean` | `false` | Whether the switch is disabled | | `name` | `string` | - | The name of the input element, used when submitting an HTML form | | `value` | `string` | - | The value of the input element, used when submitting an HTML form | | `onChange` | `(isSelected: boolean) => void` | - | Handler called when the switch value changes | | `onPress` | `(e: PressEvent) => void` | - | Handler called when the switch is pressed | | `children` | `React.ReactNode \| (values: SwitchRenderProps) => React.ReactNode` | - | Switch content or render prop | | `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function. | ### SwitchRenderProps When using the render prop pattern, these values are provided: | Prop | Type | Description | | ---------------- | --------- | --------------------------------------- | | `isSelected` | `boolean` | Whether the switch is currently on | | `isHovered` | `boolean` | Whether the switch is hovered | | `isPressed` | `boolean` | Whether the switch is currently pressed | | `isFocused` | `boolean` | Whether the switch is focused | | `isFocusVisible` | `boolean` | Whether the switch is keyboard focused | | `isDisabled` | `boolean` | Whether the switch is disabled | | `isReadOnly` | `boolean` | Whether the switch is read only | | `state` | `-` | State of the switch. | ### SwitchGroup Props | Prop | Type | Default | Description | | ------------- | ---------------------------- | ------------ | ----------------------------------- | | `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | The orientation of the switch group | | `children` | `React.ReactNode` | - | The switch items to render | | `className` | `string` | - | Additional CSS class names | # Badge **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/badge **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/badge.mdx > Displays a small indicator positioned relative to another element, commonly used for notification counts, status dots, and labels ## Import ```tsx import { Badge } from '@darkcode-ui/react'; ``` ## Anatomy Badge is designed to be positioned relative to another element using `Badge.Anchor`. Plain-text children are automatically wrapped in ``. > For standalone label usage, use the [Chip](/docs/react/components/chip) component instead. ```tsx 5 ``` ### Usage ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; const GREEN_AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; const ORANGE_AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/orange.jpg"; const BLUE_AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/blue.jpg"; export function BadgeBasic() { return (
JD 5 AB New CD
); } ``` ### Colors ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgeColors() { const colors = ["default", "accent", "success", "warning", "danger"] as const; return (
{colors.map((color) => ( JD ))}
); } ``` ### Sizes ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgeSizes() { const sizes = ["sm", "md", "lg"] as const; return (
{sizes.map((size) => ( JD 5 ))}
); } ``` ### Variants ```tsx import {Avatar, Badge, Separator} from "@darkcode-ui/react"; import React from "react"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgeVariants() { const variants = ["primary", "secondary", "soft"] as const; const colors = ["accent", "default", "success", "warning", "danger"] as const; return (
{variants.map((variant, index) => (

{variant}

{colors.map((color) => ( JD 5 ))}
{index < variants.length - 1 && }
))}
); } ``` ### Placements ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgePlacements() { const placements = ["top-right", "top-left", "bottom-right", "bottom-left"] as const; return (
{placements.map((placement) => (
JD {placement}
))}
); } ``` ### With Content Badge supports text, numbers, and icons as content. When no children are provided, it renders as a dot indicator. ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; import {Bell} from "@gravity-ui/icons"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgeWithContent() { return (
JD 5 JD New JD 99+ JD
); } ``` ### Dot Badge Empty badges act as status indicators — useful for online/offline states or activity signals. ```tsx import {Avatar, Badge} from "@darkcode-ui/react"; const AVATAR_URL = "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg"; export function BadgeDot() { const colors = ["accent", "success", "warning", "danger"] as const; return (
{colors.map((color) => ( JD ))}
); } ``` ## Related Components * **Avatar**: Display user profile images * **Chip**: Compact elements for tags and filters ## Styling ### Passing Tailwind CSS classes You can style the root container and individual slots: ```tsx import {Badge, Avatar} from '@darkcode-ui/react'; function CustomBadge() { return ( 99+ ); } ``` ### Customizing the component classes To customize the Badge component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .badge { @apply rounded-full text-xs; } .badge__label { @apply font-semibold; } .badge--accent { @apply shadow-sm; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Badge component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/badge.css)): #### Base Classes * `.badge` - Base badge container styles * `.badge__label` - Label text slot styles * `.badge-anchor` - Positioning wrapper for the anchored element #### Color Classes * `.badge--accent` - Accent color variant * `.badge--danger` - Danger color variant * `.badge--default` - Default color variant * `.badge--success` - Success color variant * `.badge--warning` - Warning color variant #### Variant Classes * `.badge--primary` - Primary variant with filled background * `.badge--secondary` - Secondary variant with default background * `.badge--soft` - Soft variant with lighter background #### Size Classes * `.badge--sm` - Small size * `.badge--md` - Medium size (default) * `.badge--lg` - Large size #### Placement Classes * `.badge--top-right` - Position at top-right corner (default) * `.badge--top-left` - Position at top-left corner * `.badge--bottom-right` - Position at bottom-right corner * `.badge--bottom-left` - Position at bottom-left corner #### Compound Variant Classes Badges support combining variant and color classes (e.g., `.badge--primary.badge--accent`). The following combinations have default styles defined: **Primary Variants:** * `.badge--primary.badge--accent` - Primary accent with filled background * `.badge--primary.badge--default` - Primary default with filled background * `.badge--primary.badge--success` - Primary success with filled background * `.badge--primary.badge--warning` - Primary warning with filled background * `.badge--primary.badge--danger` - Primary danger with filled background **Soft Variants:** * `.badge--soft.badge--accent` - Soft accent with lighter background * `.badge--soft.badge--default` - Soft default with lighter background * `.badge--soft.badge--success` - Soft success with lighter background * `.badge--soft.badge--warning` - Soft warning with lighter background * `.badge--soft.badge--danger` - Soft danger with lighter background ## API Reference ### Badge Props | Prop | Type | Default | Description | | ----------- | -------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------- | | `children` | `React.ReactNode` | - | Content to display inside the badge (text, number, or icon). When omitted, renders as a dot. | | `className` | `string` | - | Additional CSS classes for the root element | | `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Color variant of the badge | | `variant` | `"primary" \| "secondary" \| "soft"` | `"primary"` | Visual style variant | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the badge | | `placement` | `"top-right" \| "top-left" \| "bottom-right" \| "bottom-left"` | `"top-right"` | Position of the badge relative to its anchor | ### Badge.Anchor Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | --------------------------------------------------------- | | `children` | `React.ReactNode` | - | The element to anchor the badge to, plus the Badge itself | | `className` | `string` | - | Additional CSS classes for the anchor wrapper | ### Badge.Label Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ----------------------------------------- | | `children` | `React.ReactNode` | - | Label text content | | `className` | `string` | - | Additional CSS classes for the label slot |
# Chip **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/chip **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/chip.mdx > Small informational badges for displaying labels, statuses, and categories ## Import ```tsx import { Chip } from '@darkcode-ui/react'; ``` ## Anatomy Import the Chip component and access all parts using dot notation. > Plain-text children are automatically wrapped in ``. ```tsx Label text ``` ### Usage ```tsx import {Chip} from "@darkcode-ui/react"; export function ChipBasic() { return (
Default Accent Success Warning Danger
); } ``` ### Variants ```tsx import {Chip, Separator} from "@darkcode-ui/react"; import {CircleDashed} from "@gravity-ui/icons"; import React from "react"; export function ChipVariants() { const sizes = ["lg", "md", "sm"] as const; const variants = ["primary", "secondary", "tertiary", "soft"] as const; const colors = ["accent", "default", "success", "warning", "danger"] as const; return (
{sizes.map((size, index) => (

{size}

{/* Color labels header */}
{colors.map((color) => (
{color}
))}
{variants.map((variant) => (
{variant}
{colors.map((color) => (
Label
))}
))}
{index < sizes.length - 1 && } ))}
); } ``` ### With Icons ```tsx import {Chip} from "@darkcode-ui/react"; import {ChevronDown, CircleCheckFill, CircleFill, Clock, Xmark} from "@gravity-ui/icons"; export function ChipWithIcon() { return (
Information Completed Pending Failed Label
); } ``` ### Statuses ```tsx import {Chip} from "@darkcode-ui/react"; import {Ban, Check, CircleFill, CircleInfo, TriangleExclamation} from "@gravity-ui/icons"; export function ChipStatuses() { return (
Default Active Pending Inactive
New Feature Available Beta Deprecated
); } ``` ## Related Components * **Avatar**: Display user profile images * **CloseButton**: Button for dismissing overlays * **Separator**: Visual divider between content ## Styling ### Passing Tailwind CSS classes You can style the root container and individual slots: ```tsx import {Chip} from '@darkcode-ui/react'; function CustomChip() { return ( Custom Styled ); } ``` ### Customizing the component classes To customize the Chip component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .chip { @apply rounded-full text-xs; } .chip__label { @apply font-medium; } .chip--accent { @apply border-accent/20; } .chip--accent .chip__label { @apply text-accent; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Chip component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/chip.css)): #### Base Classes * `.chip` - Base chip container styles * `.chip__label` - Label text slot styles #### Color Classes * `.chip--accent` - Accent color variant * `.chip--danger` - Danger color variant * `.chip--default` - Default color variant * `.chip--success` - Success color variant * `.chip--warning` - Warning color variant #### Variant Classes * `.chip--primary` - Primary variant with filled background * `.chip--secondary` - Secondary variant with border * `.chip--tertiary` - Tertiary variant with transparent background * `.chip--soft` - Soft variant with lighter background #### Size Classes * `.chip--sm` - Small size * `.chip--md` - Medium size (default) * `.chip--lg` - Large size #### Compound Variant Classes Chips support combining variant and color classes (e.g., `.chip--secondary.chip--accent`). The following combinations have default styles defined: **Primary Variants:** * `.chip--primary.chip--accent` - Primary accent combination with filled background * `.chip--primary.chip--success` - Primary success combination with filled background * `.chip--primary.chip--warning` - Primary warning combination with filled background * `.chip--primary.chip--danger` - Primary danger combination with filled background **Soft Variants:** * `.chip--accent.chip--soft` - Soft accent combination with lighter background * `.chip--success.chip--soft` - Soft success combination with lighter background * `.chip--warning.chip--soft` - Soft warning combination with lighter background * `.chip--danger.chip--soft` - Soft danger combination with lighter background **Note:** You can apply custom styles to any variant-color combination (e.g., `.chip--secondary.chip--accent`, `.chip--tertiary.chip--success`) using the `@layer components` directive in your CSS. ## API Reference ### Chip Props | Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------- | ------------- | ------------------------------------------- | | `children` | `React.ReactNode` | - | Content to display inside the chip | | `className` | `string` | - | Additional CSS classes for the root element | | `color` | `"default" \| "accent" \| "success" \| "warning" \| "danger"` | `"default"` | Color variant of the chip | | `variant` | `"primary" \| "secondary" \| "tertiary" \| "soft"` | `"secondary"` | Visual style variant | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size of the chip | ### Chip.Label Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ----------------------------------------- | | `children` | `React.ReactNode` | - | Label text content | | `className` | `string` | - | Additional CSS classes for the label slot | # Data Grid **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/data-grid **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/data-grid.mdx > A full-featured data grid with sorting, selection, column resizing, pinned columns, drag-and-drop row reorder, expandable rows, virtualization, and async loading — built on the Table component. ## Import ```tsx import { DataGrid } from '@darkcode-ui/react'; ``` ### Usage The `DataGrid` component takes a flat `data` array, a `columns` definition, and a `getRowId` function. It renders a fully accessible table with built-in support for sorting, selection, column resizing, and more. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Button, Chip, DataGrid, Dropdown, Label} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; interface Payment { id: string; customer: string; email: string; transactionId: string; status: "succeeded" | "processing" | "refunded" | "failed"; amount: number; fee: number; method: string; } const payments: Payment[] = [ { amount: 2450, customer: "Emma Wilson", email: "emma@example.com", fee: 73.5, id: "1", method: "Visa •••• 4242", status: "succeeded", transactionId: "pay_1N3xDR", }, { amount: 299, customer: "Isabella Nguyen", email: "isabella@example.com", fee: 8.97, id: "2", method: "Visa •••• 1234", status: "succeeded", transactionId: "pay_1N3x9M", }, { amount: 39, customer: "Jackson Lee", email: "jackson@example.com", fee: 1.43, id: "3", method: "Mastercard •••• 5555", status: "processing", transactionId: "pay_1N3x8L", }, { amount: 150, customer: "Liam Johnson", email: "liam@example.com", fee: 4.5, id: "4", method: "Mastercard •••• 6789", status: "refunded", transactionId: "pay_1N3xCQ", }, { amount: 1999, customer: "Olivia Martin", email: "olivia@example.com", fee: 59.97, id: "5", method: "Visa •••• 4242", status: "succeeded", transactionId: "pay_1N3x7K", }, { amount: 99, customer: "William Kim", email: "will@example.com", fee: 3.17, id: "6", method: "Amex •••• 3782", status: "failed", transactionId: "pay_1N3xAN", }, ]; const statusColor: Record = { failed: "danger", processing: "warning", refunded: "default", succeeded: "success", }; const formatCurrency = (value: number) => value.toLocaleString("en-US", {currency: "USD", style: "currency"}); const columns: DataGridColumn[] = [ { accessorKey: "customer", allowsSorting: true, cell: (item) => (
{item.customer} {item.email}
), header: "Customer", id: "customer", isRowHeader: true, }, { accessorKey: "transactionId", cell: (item) => {item.transactionId}, header: "Transaction ID", id: "transactionId", }, { accessorKey: "status", cell: (item) => ( {item.status.charAt(0).toUpperCase() + item.status.slice(1)} ), header: "Status", id: "status", }, { accessorKey: "amount", align: "end", allowsSorting: true, cell: (item) => {formatCurrency(item.amount)}, header: "Amount", id: "amount", }, { accessorKey: "fee", align: "end", cell: (item) => {formatCurrency(item.fee)}, header: "Fee", id: "fee", }, {accessorKey: "method", header: "Payment Method", id: "method"}, { cell: () => ( ), header: "", id: "actions", }, ]; export function Default() { return ( item.id} selectionMode="multiple" /> ); } ``` ### Anatomy Unlike most DarkCode UI components, `DataGrid` is not a compound component — it has no `DataGrid.*` sub-parts. Everything is configured through props and column definitions. ```tsx import { DataGrid } from '@darkcode-ui/react'; item.id} /> ``` ### Column Definitions Columns are defined as an array of `DataGridColumn` objects. Each column has an `id`, a `header`, and either an `accessorKey` (to read a value from the row object) or a custom `cell` renderer. ```tsx import type { DataGridColumn } from '@darkcode-ui/react'; interface Payment { id: string; customer: string; amount: number; status: 'succeeded' | 'failed'; } const columns: DataGridColumn[] = [ { id: 'customer', header: 'Customer', accessorKey: 'customer', isRowHeader: true, allowsSorting: true, }, { id: 'amount', header: 'Amount', accessorKey: 'amount', align: 'end', cell: (item) => `$${item.amount.toFixed(2)}`, }, { id: 'status', header: 'Status', accessorKey: 'status', }, ]; ``` The `cell` function receives the full row item and the column definition and can return any `ReactNode`. The `header` property accepts a string, a `ReactNode`, or a render function that receives `{ sortDirection }` for sortable columns. ### Sorting Mark columns as sortable with `allowsSorting: true`. In uncontrolled mode, the DataGrid sorts data client-side using locale-aware comparison (or a custom `sortFn` on the column). For server-side sorting, pass a controlled `sortDescriptor` and handle `onSortChange`. ```tsx // Uncontrolled (client-side) // Controlled (server-side) ``` ### Row Selection Enable row selection with `selectionMode` and `showSelectionCheckboxes`. Both `"single"` and `"multiple"` modes are supported, with controlled (`selectedKeys` + `onSelectionChange`) or uncontrolled (`defaultSelectedKeys`) state. ### Pinned Columns Pin columns to the start or end edge with `pinned: "start"` or `pinned: "end"` so they stay visible during horizontal scroll. Pinned columns must have a numeric `width` or `minWidth`. A separator appears on the boundary column whenever the pinned group detaches from the scrollable content. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Chip, DataGrid, Link} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; interface Company { id: string; name: string; dotColor: string; categories: string[]; linkedin: string; lastInteraction: string; strength: "none" | "weak" | "very-weak" | "very-strong"; followers: number; twitter: string; } const companies: Company[] = [ { categories: ["B2C", "Consumer Discretionary", "E-commerce"], dotColor: "bg-warning", followers: 198135, id: "1", lastInteraction: "No contact", linkedin: "lvmh", name: "LVMH", strength: "none", twitter: "LVMH", }, { categories: ["B2C", "E-commerce"], dotColor: "bg-success", followers: 10140332, id: "2", lastInteraction: "No contact", linkedin: "disneymusicgroup", name: "Disney", strength: "none", twitter: "Disney", }, { categories: ["B2C", "Finance", "Financial Services"], dotColor: "bg-accent", followers: 969425, id: "3", lastInteraction: "No contact", linkedin: "paypal", name: "PayPal", strength: "none", twitter: "PayPal", }, { categories: ["B2C", "E-commerce", "Food"], dotColor: "bg-success", followers: 3200, id: "4", lastInteraction: "about 2 hours ago", linkedin: "sweetgreen", name: "sweetgreen", strength: "very-strong", twitter: "sweetgreen", }, { categories: ["Automation", "B2B", "Enterprise"], dotColor: "bg-accent", followers: 1340, id: "5", lastInteraction: "about 3 hours ago", linkedin: "attio", name: "Attio", strength: "very-weak", twitter: "attio", }, { categories: ["B2B", "B2C", "Broadcasting"], dotColor: "bg-accent", followers: 28946065, id: "6", lastInteraction: "9 days ago", linkedin: "google", name: "Google", strength: "weak", twitter: "Google", }, ]; const strengthDisplay: Record = { none: {className: "text-muted", label: "No communication"}, "very-strong": {className: "text-success", label: "Very strong"}, "very-weak": {className: "text-danger", label: "Very weak"}, weak: {className: "text-warning", label: "Weak"}, }; const chipColors = ["warning", "danger", "success", "accent"] as const; const columns: DataGridColumn[] = [ { accessorKey: "name", cell: (item) => ( {item.name} ), header: "Company", id: "name", isRowHeader: true, minWidth: 180, pinned: "start", }, { cell: (item) => ( {item.categories.slice(0, 3).map((category) => ( {category.length > 14 ? `${category.slice(0, 12)}…` : category} ))} ), header: "Categories", id: "categories", minWidth: 320, }, { accessorKey: "linkedin", cell: (item) => ( {item.linkedin} ), header: "LinkedIn", id: "linkedin", minWidth: 180, }, { accessorKey: "lastInteraction", cell: (item) => {item.lastInteraction}, header: "Last interaction", id: "lastInteraction", minWidth: 170, }, { cell: (item) => ( {strengthDisplay[item.strength].label} ), header: "Connection strength", id: "strength", minWidth: 190, }, { accessorKey: "followers", align: "end", cell: (item) => {item.followers.toLocaleString("en-US")}, header: "Twitter followers", id: "followers", minWidth: 150, }, { accessorKey: "twitter", cell: (item) => {item.twitter}, header: "Twitter", id: "twitter", minWidth: 140, pinned: "end", }, ]; export function PinnedColumns() { return ( item.id} selectionMode="multiple" /> ); } ``` ### Drag and Drop Enable row reorder with the `onReorder` callback. The DataGrid renders built-in drag handles with keyboard support (Enter to grab, arrows to move, Enter to drop) and fires the callback with the reordered data array. For advanced scenarios, pass `dragAndDropHooks` from React Aria's `useDragAndDrop` instead — it takes precedence over `onReorder`. With expandable rows enabled, the built-in reorder applies to top-level rows only. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Chip, DataGrid} from "@darkcode-ui/react"; import {useState} from "react"; interface Task { id: string; title: string; priority: "high" | "medium" | "low"; assignee: string; status: "todo" | "in-progress" | "done"; } const initialTasks: Task[] = [ { assignee: "Olivia", id: "1", priority: "high", status: "in-progress", title: "Design system audit", }, {assignee: "Jackson", id: "2", priority: "high", status: "todo", title: "API rate limiting"}, { assignee: "Isabella", id: "3", priority: "medium", status: "in-progress", title: "Onboarding flow redesign", }, { assignee: "William", id: "4", priority: "high", status: "todo", title: "Database migration script", }, { assignee: "Sofia", id: "5", priority: "low", status: "done", title: "Unit test coverage report", }, { assignee: "Liam", id: "6", priority: "medium", status: "todo", title: "Performance profiling", }, ]; const priorityColor: Record = { high: "danger", low: "default", medium: "warning", }; const statusDisplay: Record< Task["status"], {color: "warning" | "success" | "default"; label: string} > = { done: {color: "success", label: "Done"}, "in-progress": {color: "warning", label: "In Progress"}, todo: {color: "default", label: "To Do"}, }; const columns: DataGridColumn[] = [ {accessorKey: "title", header: "Task", id: "title", isRowHeader: true}, { accessorKey: "priority", cell: (item) => ( {item.priority.charAt(0).toUpperCase() + item.priority.slice(1)} ), header: "Priority", id: "priority", }, {accessorKey: "assignee", header: "Assignee", id: "assignee"}, { accessorKey: "status", cell: (item) => ( {statusDisplay[item.status].label} ), header: "Status", id: "status", }, ]; export function DragAndDrop() { const [tasks, setTasks] = useState(initialTasks); return (

Drag rows to reorder. Use keyboard (Enter to grab, arrows to move, Enter to drop).

item.id} onReorder={(event) => setTasks(event.reorderedData)} />
); } ``` ### Expandable Rows Render hierarchical data by providing a `getChildren` function. The DataGrid recursively renders child rows, auto-generates a chevron toggle in the `treeColumn`, and indents each nested level by `treeIndent` pixels (default `20`, set `0` to disable). The `treeColumn` prop specifies which column displays the chevron. If omitted, it defaults to the first `isRowHeader` column (or the first column). Use `defaultExpandedKeys` for uncontrolled expansion, or pair `expandedKeys` with `onExpandedChange` for controlled behavior. With client-side sorting, each sibling group is sorted independently. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Chip, DataGrid} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; interface FileRow { id: string; name: string; type: "Folder" | "Text"; size: string; modified: string; children?: FileRow[]; } const files: FileRow[] = [ { children: [ { children: [ {id: "3", modified: "Jul 10, 2025", name: "Weekly Report.md", size: "8 KB", type: "Text"}, {id: "4", modified: "Aug 20, 2025", name: "Budget.md", size: "6 KB", type: "Text"}, ], id: "2", modified: "Aug 2, 2025", name: "Project Alpha", size: "2 items", type: "Folder", }, {id: "8", modified: "Sep 14, 2025", name: "Meeting Notes.md", size: "12 KB", type: "Text"}, ], id: "1", modified: "Oct 20, 2025", name: "Documents", size: "3 items", type: "Folder", }, { children: [ {id: "6", modified: "Jan 23, 2026", name: "Image 1.png", size: "1.2 MB", type: "Text"}, {id: "7", modified: "Feb 3, 2026", name: "Image 2.png", size: "2.1 MB", type: "Text"}, ], id: "5", modified: "Feb 3, 2026", name: "Photos", size: "2 items", type: "Folder", }, {id: "9", modified: "Mar 1, 2026", name: "readme.txt", size: "4 KB", type: "Text"}, ]; const columns: DataGridColumn[] = [ { accessorKey: "name", cell: (item) => ( {item.name} ), header: "Name", id: "name", isRowHeader: true, }, { accessorKey: "type", cell: (item) => ( {item.type} ), header: "Type", id: "type", }, {accessorKey: "size", align: "end", header: "Size", id: "size"}, {accessorKey: "modified", header: "Date Modified", id: "modified"}, ]; export function ExpandableRows() { return ( item.children} getRowId={(item) => item.id} treeColumn="name" /> ); } ``` ### Editable Cells Use the `cell` render function to embed any interactive component — selects, number steppers, switches, text fields, and more. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Chip, DataGrid, ListBox, NumberField, Select, Switch} from "@darkcode-ui/react"; import {useState} from "react"; interface Feature { id: string; name: string; priority: "high" | "medium" | "low"; points: number; enabled: boolean; } const initialFeatures: Feature[] = [ {enabled: true, id: "1", name: "Dark mode", points: 8, priority: "high"}, {enabled: true, id: "2", name: "CSV export", points: 3, priority: "medium"}, {enabled: false, id: "3", name: "Keyboard shortcuts", points: 5, priority: "low"}, {enabled: true, id: "4", name: "Two-factor auth", points: 13, priority: "high"}, {enabled: false, id: "5", name: "Bulk import", points: 8, priority: "medium"}, {enabled: true, id: "6", name: "Audit log", points: 5, priority: "high"}, ]; const priorityColor: Record = { high: "danger", low: "default", medium: "warning", }; export function EditableCells() { const [features, setFeatures] = useState(initialFeatures); const updateFeature = (id: string, patch: Partial) => { setFeatures((current) => current.map((feature) => (feature.id === id ? {...feature, ...patch} : feature)), ); }; const columns: DataGridColumn[] = [ { accessorKey: "name", cell: (item) => {item.name}, header: "Feature", id: "name", isRowHeader: true, }, { cell: (item) => ( ), header: "Priority", id: "priority", }, { align: "center", cell: (item) => ( updateFeature(item.id, {points: value})} > ), header: "Story Points", id: "points", }, { cell: (item) => ( updateFeature(item.id, {enabled: isSelected})} > ), header: "Enabled", id: "enabled", }, ]; const enabledCount = features.filter((feature) => feature.enabled).length; const totalPoints = features.reduce((sum, feature) => sum + feature.points, 0); return (
item.id} />

{enabledCount} of {features.length} enabled · Total effort: {totalPoints} pts

); } ``` ### Empty State Provide a `renderEmptyState` function to display a custom empty state when `data` is empty. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Button, DataGrid, EmptyState} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; interface Project { id: string; name: string; owner: string; files: number; updated: string; } const columns: DataGridColumn[] = [ {accessorKey: "name", header: "Project", id: "name", isRowHeader: true}, {accessorKey: "owner", header: "Owner", id: "owner"}, {accessorKey: "files", align: "end", header: "Files", id: "files"}, {accessorKey: "updated", header: "Last Updated", id: "updated"}, ]; export function EmptyStateDemo() { return ( item.id} renderEmptyState={() => ( No Projects Yet Get started by creating your first project. You can always import existing projects later. )} /> ); } ``` ### Async Loading Use `onLoadMore`, `isLoadingMore`, and `loadMoreContent` to implement infinite scroll loading. The DataGrid renders a sentinel row that triggers `onLoadMore` when it scrolls into view. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Chip, DataGrid, Spinner} from "@darkcode-ui/react"; import {useState} from "react"; interface Invoice { id: string; invoice: string; client: string; amount: number; status: "paid" | "pending" | "overdue"; issued: string; } const clients = [ "Cyberdyne Systems", "Soylent Corp", "LexCorp", "Pied Piper", "Massive Dynamic", "Umbrella LLC", "Hooli", "Initech", ]; const statuses: Invoice["status"][] = ["overdue", "paid", "paid", "pending", "paid"]; function generateInvoices(start: number, count: number): Invoice[] { return Array.from({length: count}, (_, index) => { const number = start + index; return { amount: ((number * 3767) % 9500) + 300, client: clients[number % clients.length] ?? "Acme Corp", id: String(number), invoice: `INV-${1000 + number}`, issued: new Date(2025, number % 12, (number % 27) + 1).toLocaleDateString("en-US", { day: "numeric", month: "short", year: "numeric", }), status: statuses[number % statuses.length] ?? "paid", }; }); } const statusDisplay: Record< Invoice["status"], {color: "success" | "warning" | "danger"; label: string} > = { overdue: {color: "danger", label: "Overdue"}, paid: {color: "success", label: "Paid"}, pending: {color: "warning", label: "Pending"}, }; const columns: DataGridColumn[] = [ { accessorKey: "invoice", cell: (item) => {item.invoice}, header: "Invoice", id: "invoice", isRowHeader: true, }, { accessorKey: "client", cell: (item) => {item.client}, header: "Client", id: "client", }, { accessorKey: "amount", align: "end", cell: (item) => ${item.amount.toLocaleString("en-US")}, header: "Amount", id: "amount", }, { accessorKey: "status", cell: (item) => ( {statusDisplay[item.status].label} ), header: "Status", id: "status", }, { accessorKey: "issued", cell: (item) => {item.issued}, header: "Issued", id: "issued", }, ]; const TOTAL = 50; export function AsyncLoading() { const [items, setItems] = useState(() => generateInvoices(0, 8)); const [isLoading, setIsLoading] = useState(false); const hasMore = items.length < TOTAL; const handleLoadMore = () => { if (isLoading) return; setIsLoading(true); setTimeout(() => { setItems((current) => [ ...current, ...generateInvoices(current.length, Math.min(8, TOTAL - current.length)), ]); setIsLoading(false); }, 900); }; return (

Invoices

{items.length} / {TOTAL}
item.id} isLoadingMore={isLoading} loadMoreContent={} scrollContainerClassName="max-h-[400px] overflow-y-auto" onLoadMore={hasMore ? handleLoadMore : undefined} />
); } ``` ### Column Resizing Enable column resizing with `allowsColumnResize` on the DataGrid and `allowsResizing` on individual columns. Columns should have `minWidth` set. Track widths with `onColumnResize` and `onColumnResizeEnd`. ### Virtualization Enable row virtualization for large datasets (1,000+ rows) with the `virtualized` prop. Only visible rows are rendered to the DOM. Set `rowHeight` and `headingHeight` to match your row content, and constrain the height via `contentClassName`. ```tsx "use client"; import type {DataGridColumn, DataGridSelection} from "@darkcode-ui/react"; import {Chip, DataGrid, SearchField, ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; interface Product { id: string; name: string; sku: string; status: "in-stock" | "low-stock" | "out-of-stock"; stock: number; price: number; rating: number; sales: number; } const brands = ["ZenCore", "EchoLink", "TechFlow", "PrimeByte", "PixelForge"]; const lines = ["Turbo", "Advanced", "Deluxe", "Elite", "Slim", "Ultra"]; const kinds = ["Sensor", "Mouse", "Drive", "Tablet", "Cable", "Headphones", "Webcam"]; function generateProducts(count: number): Product[] { return Array.from({length: count}, (_, index) => { const stock = (index * 37) % 180 === 0 ? 0 : (index * 37) % 180; const status: Product["status"] = stock === 0 ? "out-of-stock" : stock < 6 ? "low-stock" : "in-stock"; return { id: String(index + 1), name: `${brands[index % brands.length]} ${lines[index % lines.length]} ${ kinds[index % kinds.length] }`, price: ((index * 977) % 1500) + 300, rating: ((index * 13) % 21) / 4 + 0.5, sales: (index * 179) % 800, sku: `SKU-${3000 + index}`, status, stock, }; }); } const products = generateProducts(1000); const statusDisplay: Record< Product["status"], {color: "success" | "warning" | "danger"; icon: string; label: string} > = { "in-stock": {color: "success", icon: "gravity-ui:circle-check-fill", label: "In Stock"}, "low-stock": {color: "warning", icon: "gravity-ui:clock-fill", label: "Low Stock"}, "out-of-stock": {color: "danger", icon: "gravity-ui:circle-xmark-fill", label: "Out of Stock"}, }; function StarRating({value}: {value: number}) { return ( {Array.from({length: 5}, (_, index) => ( ))} ); } const columns: DataGridColumn[] = [ { accessorKey: "name", cell: (item) => ( {item.name} ), header: "Product", id: "name", isRowHeader: true, width: 280, }, { accessorKey: "sku", cell: (item) => {item.sku}, header: "SKU", id: "sku", width: 120, }, { accessorKey: "status", cell: (item) => ( {statusDisplay[item.status].label} ), header: "Status", id: "status", width: 150, }, {accessorKey: "stock", align: "end", header: "Stock", id: "stock", width: 90}, { accessorKey: "price", align: "end", cell: (item) => ${item.price.toLocaleString("en-US")}, header: "Price", id: "price", width: 100, }, { accessorKey: "rating", cell: (item) => , header: "Rating", id: "rating", width: 130, }, {accessorKey: "sales", align: "end", header: "Sales", id: "sales", width: 90}, ]; export function Virtualized() { const [filter, setFilter] = useState(new Set(["all"])); const [query, setQuery] = useState(""); const activeFilter = filter === "all" ? "all" : ([...filter][0] ?? "all"); const filteredProducts = products.filter((product) => { if (activeFilter !== "all" && product.status !== activeFilter) return false; if (query && !product.name.toLowerCase().includes(query.toLowerCase())) return false; return true; }); return (
{ setFilter((keys as Set).size === 0 ? new Set(["all"]) : keys); }} > All In Stock Out of Stock Low Stock
item.id} headingHeight={37} rowHeight={58} selectionMode="multiple" />
); } ``` ### Bulk Actions Combine row selection with an `ActionBar` to provide bulk operations like export, archive, or delete. ```tsx "use client"; import type {DataGridColumn, DataGridSelection} from "@darkcode-ui/react"; import {ActionBar, Avatar, Chip, DataGrid} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; interface Employee { id: string; name: string; email: string; avatar: string; department: string; status: "active" | "pending" | "inactive"; joined: string; } const employees: Employee[] = [ { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/red.jpg", department: "Support", email: "amara.okafor@company.com", id: "1", joined: "Jun 27, 2024", name: "Amara Okafor", status: "pending", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg", department: "HR", email: "elena.rodriguez@company.com", id: "2", joined: "Jan 28, 2024", name: "Elena Rodriguez", status: "active", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/blue.jpg", department: "Finance", email: "james.o.brien@company.com", id: "3", joined: "Apr 14, 2024", name: "James O'Brien", status: "active", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/purple.jpg", department: "Engineering", email: "luca.bianchi@company.com", id: "4", joined: "Jul 25, 2024", name: "Luca Bianchi", status: "active", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/orange.jpg", department: "Design", email: "marcus.chen@company.com", id: "5", joined: "Feb 3, 2024", name: "Marcus Chen", status: "pending", }, { avatar: "https://darkcode-ui-assets.darkcode.dev/avatars/black.jpg", department: "Product", email: "yuki.tanaka@company.com", id: "6", joined: "May 8, 2024", name: "Yuki Tanaka", status: "inactive", }, ]; const statusDisplay: Record< Employee["status"], {color: "success" | "warning" | "danger"; label: string} > = { active: {color: "success", label: "Active"}, inactive: {color: "danger", label: "Inactive"}, pending: {color: "warning", label: "Pending"}, }; const columns: DataGridColumn[] = [ { accessorKey: "name", allowsSorting: true, cell: (item) => ( {item.name.charAt(0)} {item.name} {item.email} ), header: "Employee", id: "name", isRowHeader: true, }, {accessorKey: "department", header: "Department", id: "department"}, { accessorKey: "status", cell: (item) => ( {statusDisplay[item.status].label} ), header: "Status", id: "status", }, {accessorKey: "joined", header: "Joined", id: "joined"}, ]; export function BulkActions() { const [selectedKeys, setSelectedKeys] = useState(new Set()); const selectedCount = selectedKeys === "all" ? employees.length : selectedKeys.size; return (
item.id} selectedKeys={selectedKeys} selectionMode="multiple" onSelectionChange={setSelectedKeys} /> 0} onClose={() => setSelectedKeys(new Set())}> }> Export }> Archive } variant="danger-soft" > Delete
); } ``` ### Users A minimal user directory using the `"secondary"` variant with `accessorKey`-only columns and a row action link — no selection, no sorting. ```tsx "use client"; import type {DataGridColumn} from "@darkcode-ui/react"; import {Button, DataGrid, Link} from "@darkcode-ui/react"; interface User { id: string; name: string; title: string; email: string; role: string; } const users: User[] = [ { email: "lindsay.walton@example.com", id: "1", name: "Lindsay Walton", role: "Member", title: "Front-end Developer", }, { email: "courtney.henry@example.com", id: "2", name: "Courtney Henry", role: "Admin", title: "Designer", }, { email: "tom.cook@example.com", id: "3", name: "Tom Cook", role: "Member", title: "Director of Product", }, { email: "whitney.francis@example.com", id: "4", name: "Whitney Francis", role: "Admin", title: "Copywriter", }, { email: "leonard.krasner@example.com", id: "5", name: "Leonard Krasner", role: "Owner", title: "Senior Designer", }, { email: "floyd.miles@example.com", id: "6", name: "Floyd Miles", role: "Member", title: "Principal Designer", }, ]; const columns: DataGridColumn[] = [ { accessorKey: "name", cell: (item) => {item.name}, header: "Name", id: "name", isRowHeader: true, }, {accessorKey: "title", header: "Title", id: "title"}, {accessorKey: "email", header: "Email", id: "email"}, {accessorKey: "role", header: "Role", id: "role"}, { align: "end", cell: (item) => ( Edit ), header: "", id: "edit", }, ]; export function Users() { return (

Users

A list of all the users in your account including their name, title, email and role.

item.id} variant="secondary" />
); } ``` ### Team Members A full-featured HR table with controlled sorting, multi-selection, pinned columns, column resizing, column visibility toggling, and search. ```tsx "use client"; import type {DataGridColumn, DataGridSelection, DataGridSortDescriptor} from "@darkcode-ui/react"; import {Avatar, Button, Chip, DataGrid, Dropdown, Label, SearchField} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; interface Member { id: string; workerId: string; name: string; email: string; avatar: string; country: string; flag: string; role: string; workerType: "Employee" | "Contractor"; status: "active" | "inactive" | "vacation"; startDate: string; } const seed: [ string, string, string, string, string, Member["workerType"], Member["status"], string, ][] = [ [ "Aaron Carter", "red", "Canada", "🇨🇦", "Software Engineer", "Contractor", "inactive", "Sep 16, 2025", ], [ "Alice Johnson", "green", "South Korea", "🇰🇷", "Network Administrator", "Contractor", "active", "May 5, 2025", ], [ "Bella Brown", "blue", "United Kingdom", "🇬🇧", "Technical Support Specialist", "Employee", "active", "Jul 12, 2025", ], [ "Carlos Mendez", "purple", "United States", "🇺🇸", "Operations Coordinator", "Contractor", "vacation", "Apr 14, 2025", ], ["Daniela Rossi", "orange", "Brazil", "🇧🇷", "Accountant", "Employee", "vacation", "Jan 14, 2025"], [ "Emi Sato", "black", "Japan", "🇯🇵", "Graphic Designer", "Contractor", "inactive", "Apr 11, 2026", ], [ "Farah Khan", "red", "India", "🇮🇳", "Technical Support Specialist", "Employee", "active", "Apr 24, 2026", ], ["Gustav Berg", "green", "Sweden", "🇸🇪", "Product Manager", "Employee", "active", "Feb 2, 2026"], ]; const members: Member[] = seed.map( ([name, color, country, flag, role, workerType, status, startDate], index) => ({ avatar: `https://darkcode-ui-assets.darkcode.dev/avatars/${color}.jpg`, country, email: `${name.toLowerCase().replace(/[^a-z]+/g, ".")}@example.com`, flag, id: String(index + 1), name, role, startDate, status, workerId: `WRK-${9100000 + index * 37231}`, workerType, }), ); const statusDisplay: Record< Member["status"], {color: "success" | "warning" | "danger"; label: string} > = { active: {color: "success", label: "Active"}, inactive: {color: "danger", label: "Inactive"}, vacation: {color: "warning", label: "Vacation"}, }; export function TeamMembers() { const [selectedKeys, setSelectedKeys] = useState(new Set()); const [sortDescriptor, setSortDescriptor] = useState({ column: "name", direction: "ascending", }); const [query, setQuery] = useState(""); const [visibleColumns, setVisibleColumns] = useState( new Set(["workerId", "name", "country", "role", "workerType", "status", "startDate"]), ); const allColumns: DataGridColumn[] = [ { accessorKey: "workerId", cell: (item) => {item.workerId}, header: "Worker ID", id: "workerId", minWidth: 140, }, { accessorKey: "name", allowsResizing: true, allowsSorting: true, cell: (item) => ( {item.name.charAt(0)} {item.name} {item.email} ), header: "Member", id: "name", isRowHeader: true, minWidth: 220, pinned: "start", width: 260, }, { accessorKey: "country", allowsResizing: true, allowsSorting: true, cell: (item) => ( {item.flag} {item.country} ), header: "Country", id: "country", minWidth: 150, }, {accessorKey: "role", allowsResizing: true, header: "Role", id: "role", minWidth: 200}, { accessorKey: "workerType", allowsSorting: true, header: "Worker Type", id: "workerType", minWidth: 130, }, { accessorKey: "status", cell: (item) => ( {statusDisplay[item.status].label} ), header: "Status", id: "status", minWidth: 110, }, { accessorKey: "startDate", cell: (item) => ( {item.startDate} ), header: "Start Date", id: "startDate", minWidth: 170, }, ]; const columns = allColumns.filter( (column) => visibleColumns === "all" || visibleColumns.has(column.id), ); const rows = members .filter((member) => member.name.toLowerCase().includes(query.toLowerCase())) .sort((a, b) => { const key = sortDescriptor.column as keyof Member; const result = String(a[key]).localeCompare(String(b[key])); return sortDescriptor.direction === "descending" ? -result : result; }); return (
{allColumns.map((column) => ( ))}
item.id} selectedKeys={selectedKeys} selectionMode="multiple" sortDescriptor={sortDescriptor} onSelectionChange={setSelectedKeys} onSortChange={setSortDescriptor} />
); } ``` ### Servers A server monitoring dashboard with controlled sorting, selection, search, and rich cell renderers including sparkline charts and circular progress indicators. ```tsx "use client"; import type {DataGridColumn, DataGridSelection, DataGridSortDescriptor} from "@darkcode-ui/react"; import {Button, Chip, DataGrid, ProgressCircle, SearchField} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; interface Server { id: string; cluster: string; instances: number; status: "active" | "inactive"; region: string; capacity: number; cost: number; requests: number[]; } function generateSparkline(seed: number): number[] { return Array.from({length: 16}, (_, index) => 30 + ((seed * (index + 3) * 7919) % 40)); } const servers: Server[] = [ { capacity: 20.01, cluster: "#4586930", cost: 830.04, id: "1", instances: 25, region: "US-West 1", requests: generateSparkline(1), status: "active", }, { capacity: 7.36, cluster: "#4586931", cost: 752.72, id: "2", instances: 13, region: "US-East 2", requests: generateSparkline(2), status: "active", }, { capacity: 48.25, cluster: "#4586932", cost: 517.6, id: "3", instances: 27, region: "EU-Central 1", requests: generateSparkline(3), status: "inactive", }, { capacity: 21.15, cluster: "#4586933", cost: 544.93, id: "4", instances: 28, region: "AP-South 1", requests: generateSparkline(4), status: "inactive", }, { capacity: 73.12, cluster: "#4586934", cost: 412.71, id: "5", instances: 6, region: "US-West 2", requests: generateSparkline(5), status: "active", }, { capacity: 72.65, cluster: "#4586935", cost: 354.06, id: "6", instances: 19, region: "EU-West 1", requests: generateSparkline(6), status: "active", }, { capacity: 15.19, cluster: "#4586936", cost: 775.45, id: "7", instances: 27, region: "CA-Central 1", requests: generateSparkline(7), status: "active", }, { capacity: 11.35, cluster: "#4586937", cost: 146.05, id: "8", instances: 27, region: "AP-Northeast 1", requests: generateSparkline(8), status: "active", }, ]; function Sparkline({values}: {values: number[]}) { const max = Math.max(...values); const min = Math.min(...values); const range = max - min || 1; const points = values .map((value, index) => { const x = (index / (values.length - 1)) * 96; const y = 26 - ((value - min) / range) * 22; return `${x.toFixed(1)},${y.toFixed(1)}`; }) .join(" "); return ( ); } const capacityColor = (value: number): "accent" | "warning" | "danger" => value >= 90 ? "danger" : value >= 70 ? "warning" : "accent"; const formatCurrency = (value: number) => value.toLocaleString("en-US", {currency: "USD", style: "currency"}); const columns: DataGridColumn[] = [ { accessorKey: "cluster", allowsSorting: true, cell: (item) => ( {item.cluster} ), header: "Cluster ID", id: "cluster", isRowHeader: true, }, { accessorKey: "instances", align: "center", allowsSorting: true, header: "Instances", id: "instances", }, { accessorKey: "status", cell: (item) => ( {item.status === "active" ? "Active" : "Inactive"} ), header: "Status", id: "status", }, {accessorKey: "region", header: "Region", id: "region"}, { accessorKey: "capacity", cell: (item) => ( = 70 ? "text-warning" : undefined}> {item.capacity.toFixed(2)}% ), header: "Capacity", id: "capacity", }, { accessorKey: "cost", align: "end", allowsSorting: true, cell: (item) => {formatCurrency(item.cost)}, header: "Cost", id: "cost", }, { cell: (item) => , header: "Requests", id: "requests", }, ]; export function Servers() { const [selectedKeys, setSelectedKeys] = useState(new Set()); const [sortDescriptor, setSortDescriptor] = useState({ column: "cluster", direction: "ascending", }); const [query, setQuery] = useState(""); const rows = servers .filter( (server) => server.cluster.includes(query) || server.region.toLowerCase().includes(query.toLowerCase()), ) .sort((a, b) => { const key = sortDescriptor.column as keyof Server; const av = a[key]; const bv = b[key]; const result = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv)); return sortDescriptor.direction === "descending" ? -result : result; }); return (

Servers

{rows.length}
item.id} selectedKeys={selectedKeys} selectionMode="multiple" sortDescriptor={sortDescriptor} onSelectionChange={setSelectedKeys} onSortChange={setSortDescriptor} />
); } ``` ## Styling ### CSS Classes * `.data-grid` — Root wrapper. Defines the `--data-grid-*` custom properties. * `.data-grid__selection-column` / `.data-grid__selection-cell` — Narrow column for the select-all and row checkboxes. Width from `--data-grid-selection-column-width`. * `.data-grid__drag-handle-column` / `.data-grid__drag-handle-cell` — Narrow column for the drag handles. Width from `--data-grid-drag-handle-column-width`. * `.data-grid__drag-handle` — The grip button inside each row. `cursor: grab`, `grabbing` while pressed. * `.data-grid__sort-icon` — Chevron next to sortable column headers. Rotates 180° when descending. * `.data-grid__empty-state` — Centered container for the empty state message. * `.data-grid__tree-cell` — Flex wrapper inside the `treeColumn` cell holding the chevron toggle and cell content. Per-level indentation via `--data-grid-tree-indent`. * `.data-grid__tree-toggle` / `.data-grid__tree-toggle-icon` — Expand/collapse chevron button and icon. The icon rotates 90° when expanded via `[data-expanded]`. * `.data-grid__tree-toggle-spacer` — Invisible placeholder for leaf rows so content aligns with siblings that have a chevron. ### Data Attributes * `[data-align="center" | "end"]` — Set on header and body cells from the column `align` option. * `[data-vertical-align="top" | "middle" | "bottom"]` — Set on the root from the `verticalAlign` prop. * `[data-pinned="start" | "end"]` — Sticky pinned cells. * `[data-pinned-edge]` — Boundary column of a pinned group; shows the separator. * `[data-pinned-start-detached]` / `[data-pinned-end-detached]` — Set on the root while the content is scrolled away from the pinned edge. ### CSS Variables * `--data-grid-selection-column-width` — Width of the selection checkbox column (default `40px`). * `--data-grid-drag-handle-column-width` — Width of the drag handle column (default `32px`). * `--data-grid-tree-toggle-size` — Size of the expand/collapse chevron button and its leaf-row spacer (default `24px`). * `--data-grid-tree-gap` — Gap between the chevron/spacer and the cell content (default `4px`). * `--data-grid-tree-indent` — Indentation per nested level (default `20px`, set from the `treeIndent` prop). ## API Reference ### DataGrid Accepts a generic type parameter `T` for the row data shape. | Prop | Type | Default | Description | | -------------------------- | ------------------------------------------------ | ----------- | -------------------------------------------------------------- | | `data` | `T[]` | - | Row data array | | `columns` | `DataGridColumn[]` | - | Column definitions | | `getRowId` | `(item: T) => Key` | - | Extracts a unique key from each row item | | `aria-label` | `string` | - | Accessible label for the table. Required | | `variant` | `'primary' \| 'secondary'` | `'primary'` | Visual variant passed to the underlying Table | | `className` | `string` | - | Additional className for the root wrapper | | `contentClassName` | `string` | - | Additional className for the inner `` element | | `scrollContainerClassName` | `string` | - | Additional className for the scroll container | | `verticalAlign` | `'top' \| 'middle' \| 'bottom'` | `'middle'` | Vertical alignment of cell content | | `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | Row selection mode | | `selectedKeys` | `DataGridSelection` | - | Controlled selected row keys | | `defaultSelectedKeys` | `DataGridSelection` | - | Default selected row keys (uncontrolled) | | `onSelectionChange` | `(keys: DataGridSelection) => void` | - | Callback when selection changes | | `selectionBehavior` | `'toggle' \| 'replace'` | `'toggle'` | Selection interaction model | | `showSelectionCheckboxes` | `boolean` | `false` | Auto-prepend a checkbox column for selection | | `disabledKeys` | `Iterable` | - | Keys of rows that should be disabled | | `sortDescriptor` | `DataGridSortDescriptor` | - | Controlled sort descriptor | | `defaultSortDescriptor` | `DataGridSortDescriptor` | - | Default sort descriptor (uncontrolled client-side sorting) | | `onSortChange` | `(descriptor: DataGridSortDescriptor) => void` | - | Callback when sort changes. Fires in both modes | | `allowsColumnResize` | `boolean` | `false` | Enable column resizing on columns that opt in | | `onColumnResize` | `(widths: Map) => void` | - | Callback during column resize | | `onColumnResizeEnd` | `(widths: Map) => void` | - | Callback when resize ends | | `onReorder` | `(event: DataGridReorderEvent) => void` | - | Enables built-in drag-and-drop row reorder | | `dragAndDropHooks` | `DataGridDragAndDropHooks` | - | Advanced React Aria drag-and-drop hooks. Overrides `onReorder` | | `onRowAction` | `(key: Key) => void` | - | Callback when a row is actioned | | `renderEmptyState` | `() => ReactNode` | - | Render function for the empty state | | `onLoadMore` | `() => void` | - | Callback when the load-more sentinel scrolls into view | | `isLoadingMore` | `boolean` | `false` | Whether more data is currently being fetched | | `loadMoreContent` | `ReactNode` | - | Content inside the load-more sentinel row | | `virtualized` | `boolean` | `false` | Enable row virtualization for large datasets | | `rowHeight` | `number` | `42` | Fixed row height in pixels (virtualized mode) | | `headingHeight` | `number` | `36` | Header row height in pixels (virtualized mode) | | `getChildren` | `(item: T) => T[] \| undefined` | - | Return child rows. Enables expandable/tree rows | | `treeColumn` | `string` | - | Column id that displays the expand/collapse chevron | | `expandedKeys` | `DataGridSelection` | - | Controlled set of expanded row keys | | `defaultExpandedKeys` | `DataGridSelection` | - | Default expanded row keys (uncontrolled) | | `onExpandedChange` | `(keys: DataGridSelection) => void` | - | Callback when expanded rows change | | `treeIndent` | `number` | `20` | Pixels of indentation per nested level. `0` disables | ### DataGridColumn Column definition object passed to the `columns` prop. | Property | Type | Default | Description | | ----------------- | ---------------------------------- | --------- | ------------------------------------------------------------ | | `id` | `string` | - | Unique column identifier. Used as the sort key. Required | | `header` | `ReactNode \| (info) => ReactNode` | - | Header content. Render function receives `{ sortDirection }` | | `accessorKey` | `keyof T & string` | - | Key on `T` to read the cell value from | | `cell` | `(item: T, column) => ReactNode` | - | Custom cell renderer | | `isRowHeader` | `boolean` | `false` | Mark this column as the row header (for accessibility) | | `allowsSorting` | `boolean` | `false` | Allow this column to be sorted | | `sortFn` | `(a: T, b: T) => number` | - | Custom sort comparator | | `allowsResizing` | `boolean` | - | Allow resizing (with `allowsColumnResize` on the grid) | | `width` | `DataGridColumnSize` | - | Initial column width (px, %, or fr) | | `minWidth` | `number` | - | Minimum column width | | `maxWidth` | `number` | - | Maximum column width | | `align` | `'start' \| 'center' \| 'end'` | `'start'` | Cell text alignment | | `headerClassName` | `string` | - | Additional className for every `
` of this column | | `cellClassName` | `string` | - | Additional className for every `` of this column | | `pinned` | `'start' \| 'end'` | - | Pin this column. Requires numeric `width` or `minWidth` | ### DataGridReorderEvent Event object passed to the `onReorder` callback. | Property | Type | Description | | --------------- | ------------------------------------------------- | ----------------------------------------------------- | | `keys` | `Set` | The keys that were moved | | `target` | `{ key: Key; dropPosition: 'before' \| 'after' }` | The target row key and drop position | | `reorderedData` | `T[]` | The full reordered data array after applying the move | ### Type Exports | Type | Description | | -------------------------- | -------------------------------------------------------- | | `DataGridSelection` | A set of selected keys, or `'all'` | | `DataGridSortDescriptor` | The current sort column and direction | | `DataGridSortDirection` | `'ascending' \| 'descending'` | | `DataGridColumnSize` | A number, numeric string, percentage, or fractional unit | | `DataGridDragAndDropHooks` | Hooks returned by React Aria's `useDragAndDrop` | ## Related Components * **Table**: Structured data display in rows and columns * **Checkbox**: Binary choice input control * **Chip**: Compact elements for tags and filters # Kanban **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/kanban **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/kanban.mdx > A drag-and-drop kanban board with columns, cards, and keyboard navigation for organizing tasks and workflows ## Import ```tsx import { Kanban, useKanban, useKanbanColumn, useKanbanCardPlaceholder } from '@darkcode-ui/react'; ``` ## Anatomy The board is composed from compound parts. State is managed by the `useKanban` hook, while each column wires its drag-and-drop behaviour with `useKanbanColumn`. ```tsx ``` ### Usage Cards can be dragged between columns with a pointer or the keyboard (focus a card, then activate its drag handle). The board keeps all items in a single list and filters them per column. ```tsx "use client"; import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react"; import type {CSSProperties} from "react"; import { Button, Chip, Kanban, useKanban, useKanbanCardPlaceholder, useKanbanColumn, } from "@darkcode-ui/react"; import {Ellipsis, Plus} from "@gravity-ui/icons"; type TagColor = "accent" | "default" | "success" | "warning" | "danger"; interface Task { id: string; title: string; status: string; tag: {label: string; color: TagColor}; } interface Column { id: string; title: string; color: string; } const columns: Column[] = [ {color: "var(--muted)", id: "todo", title: "To Do"}, {color: "var(--warning)", id: "in-progress", title: "In Progress"}, {color: "var(--success)", id: "done", title: "Done"}, ]; const initialTasks: Task[] = [ { id: "1", status: "todo", tag: {color: "accent", label: "Design"}, title: "Audit the onboarding flow", }, { id: "2", status: "todo", tag: {color: "warning", label: "Research"}, title: "Interview 5 power users", }, { id: "3", status: "in-progress", tag: {color: "accent", label: "Design"}, title: "New empty states", }, { id: "4", status: "in-progress", tag: {color: "success", label: "Eng"}, title: "Wire up drag and drop", }, {id: "5", status: "done", tag: {color: "success", label: "Eng"}, title: "Set up the board grid"}, ]; function KanbanColumn({ column, kanban, renderDropIndicator, }: { column: Column; kanban: UseKanbanReturn; renderDropIndicator: UseKanbanCardPlaceholderReturn["renderDropIndicator"]; }) { const {dragAndDropHooks, items} = useKanbanColumn(kanban, column.id, {renderDropIndicator}); return ( {column.title} {items.length} {(item: Task) => ( {item.tag.label} {item.title} )} ); } export function Default() { const kanban = useKanban({ getColumn: (task) => task.status, initialItems: initialTasks, setColumn: (task, status) => ({...task, status}), }); const {renderDropIndicator} = useKanbanCardPlaceholder({ renderIndicator: (target) => , }); return ( {columns.map((column) => ( ))} ); } ``` ### Notion Board ```tsx "use client"; import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react"; import type {CSSProperties} from "react"; import { Button, Chip, Kanban, useKanban, useKanbanCardPlaceholder, useKanbanColumn, } from "@darkcode-ui/react"; import {Ellipsis, Plus} from "@gravity-ui/icons"; type TagColor = "accent" | "default" | "success" | "warning" | "danger"; interface Task { id: string; title: string; status: string; tag: {label: string; color: TagColor}; } interface Column { id: string; title: string; color: string; } const columns: Column[] = [ {color: "var(--muted)", id: "not-started", title: "Not started"}, {color: "var(--accent)", id: "in-progress", title: "In progress"}, {color: "var(--warning)", id: "in-review", title: "In review"}, {color: "var(--success)", id: "done", title: "Done"}, ]; const initialTasks: Task[] = [ { id: "n1", status: "not-started", tag: {color: "accent", label: "Marketing"}, title: "Launch announcement post", }, { id: "n2", status: "not-started", tag: {color: "warning", label: "Ops"}, title: "Update vendor contracts", }, { id: "n3", status: "in-progress", tag: {color: "success", label: "Product"}, title: "Quarterly roadmap draft", }, { id: "n4", status: "in-review", tag: {color: "danger", label: "Legal"}, title: "Privacy policy revision", }, { id: "n5", status: "done", tag: {color: "default", label: "Docs"}, title: "Migrate the help center", }, ]; function KanbanColumn({ column, kanban, renderDropIndicator, }: { column: Column; kanban: UseKanbanReturn; renderDropIndicator: UseKanbanCardPlaceholderReturn["renderDropIndicator"]; }) { const {dragAndDropHooks, items} = useKanbanColumn(kanban, column.id, {renderDropIndicator}); return ( {column.title} {items.length} {(item: Task) => ( {item.title} {item.tag.label} )} ); } export function NotionBoard() { const kanban = useKanban({ getColumn: (task) => task.status, initialItems: initialTasks, setColumn: (task, status) => ({...task, status}), }); const {renderDropIndicator} = useKanbanCardPlaceholder({ renderIndicator: (target) => , }); return ( {columns.map((column) => ( ))} ); } ``` ### Project Board ```tsx "use client"; import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react"; import type {CSSProperties} from "react"; import { Avatar, Button, Chip, Kanban, useKanban, useKanbanCardPlaceholder, useKanbanColumn, } from "@darkcode-ui/react"; import {Calendar, Comment, Ellipsis, Plus} from "@gravity-ui/icons"; type Priority = "low" | "medium" | "high"; type PriorityColor = "default" | "warning" | "danger"; interface Task { id: string; title: string; status: string; priority: Priority; due: string; comments: number; assignees: {name: string; src?: string}[]; } interface Column { id: string; title: string; color: string; } const PRIORITY: Record = { high: {color: "danger", label: "High"}, low: {color: "default", label: "Low"}, medium: {color: "warning", label: "Medium"}, }; const columns: Column[] = [ {color: "var(--muted)", id: "backlog", title: "Backlog"}, {color: "var(--accent)", id: "todo", title: "To Do"}, {color: "var(--warning)", id: "in-progress", title: "In Progress"}, {color: "var(--success)", id: "done", title: "Done"}, ]; const initialTasks: Task[] = [ { assignees: [{name: "Ada Lovelace"}, {name: "Grace Hopper"}], comments: 3, due: "Jun 24", id: "p1", priority: "high", status: "backlog", title: "Define API contract for billing", }, { assignees: [{name: "Alan Turing"}], comments: 1, due: "Jun 26", id: "p2", priority: "medium", status: "todo", title: "Build invoice PDF export", }, { assignees: [{name: "Katherine Johnson"}, {name: "Edsger Dijkstra"}], comments: 8, due: "Jun 21", id: "p3", priority: "high", status: "in-progress", title: "Migrate auth to the new gateway", }, { assignees: [{name: "Barbara Liskov"}], comments: 0, due: "Jun 19", id: "p4", priority: "low", status: "done", title: "Add usage analytics events", }, ]; function Assignees({people}: {people: Task["assignees"]}) { return (
{people.map((person) => ( {person.src ? : null} {person.name .split(" ") .map((part) => part[0]) .join("")} ))}
); } function KanbanColumn({ column, kanban, renderDropIndicator, }: { column: Column; kanban: UseKanbanReturn; renderDropIndicator: UseKanbanCardPlaceholderReturn["renderDropIndicator"]; }) { const {dragAndDropHooks, items} = useKanbanColumn(kanban, column.id, {renderDropIndicator}); return ( {column.title} {items.length} {(item: Task) => (
{PRIORITY[item.priority].label} {item.due}
{item.title}
{item.comments}
)}
); } export function ProjectBoard() { const kanban = useKanban({ getColumn: (task) => task.status, initialItems: initialTasks, setColumn: (task, status) => ({...task, status}), }); const {renderDropIndicator} = useKanbanCardPlaceholder({ renderIndicator: (target) => , }); return ( {columns.map((column) => ( ))} ); } ``` ### Sizes The `size` prop scales column width, card padding, and font size. Use `sm`, `md` (default), or `lg`. ```tsx "use client"; import type {UseKanbanCardPlaceholderReturn, UseKanbanReturn} from "@darkcode-ui/react"; import type {CSSProperties} from "react"; import { Chip, Kanban, useKanban, useKanbanCardPlaceholder, useKanbanColumn, } from "@darkcode-ui/react"; type TagColor = "accent" | "default" | "success" | "warning" | "danger"; interface Task { id: string; title: string; status: string; tag: {label: string; color: TagColor}; } interface Column { id: string; title: string; color: string; } const columns: Column[] = [ {color: "var(--muted)", id: "todo", title: "To Do"}, {color: "var(--warning)", id: "in-progress", title: "In Progress"}, {color: "var(--success)", id: "done", title: "Done"}, ]; const initialTasks: Task[] = [ {id: "1", status: "todo", tag: {color: "accent", label: "Design"}, title: "Audit onboarding"}, {id: "2", status: "in-progress", tag: {color: "success", label: "Eng"}, title: "Drag and drop"}, {id: "3", status: "done", tag: {color: "success", label: "Eng"}, title: "Board grid"}, ]; function KanbanColumn({ column, kanban, renderDropIndicator, }: { column: Column; kanban: UseKanbanReturn; renderDropIndicator: UseKanbanCardPlaceholderReturn["renderDropIndicator"]; }) { const {dragAndDropHooks, items} = useKanbanColumn(kanban, column.id, {renderDropIndicator}); return ( {column.title} {items.length} {(item: Task) => ( {item.tag.label} {item.title} )} ); } function Board({size}: {size: "sm" | "md" | "lg"}) { const kanban = useKanban({ getColumn: (task) => task.status, initialItems: initialTasks, setColumn: (task, status) => ({...task, status}), }); const {renderDropIndicator} = useKanbanCardPlaceholder({ renderIndicator: (target) => , }); return ( {columns.map((column) => ( ))} ); } export function Sizes() { return (
{(["sm", "md", "lg"] as const).map((size) => (

{size}

))}
); } ``` ## Related Components * **List View**: Single-column interactive list with selection and item actions * **Table**: Structured data display in rows and columns * **DropZone**: Drag-and-drop file upload area with a file list preview ## Styling ### Passing Tailwind CSS classes Every part accepts a `className`, and the colored status dot reads the `--kanban-indicator-color` custom property: ```tsx Done ``` ### Customizing the component classes To customize the Kanban component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .kanban { --kanban-column-min-width: 320px; --kanban-column-gap: 24px; } .kanban__card-content { @apply shadow-none ring-1 ring-border; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Kanban component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/kanban.css)): #### Base & Size Classes * `.kanban` - Root board container. CSS grid with horizontal auto-flow and snap scrolling. * `.kanban--sm` - Small size. Narrower columns (`240px`), tighter gap (`12px`). * `.kanban--md` - Medium size (default). Standard columns (`280px`), default gap (`16px`). * `.kanban--lg` - Large size. Wider columns (`320px`), spacious gap (`20px`). #### Element Classes * `.kanban__column` - Individual column section. * `.kanban__column-body` - Visual container for the card list and footer actions. * `.kanban__column-header` - Header area with indicator, title, count, and actions. * `.kanban__column-actions` - Hover-reveal action buttons inside the header. * `.kanban__column-indicator` - Colored status dot (when empty) or icon container (with children). * `.kanban__column-title` - Column heading text (`text-sm font-semibold`). * `.kanban__column-count` - Item count badge (`text-xs text-muted`). * `.kanban__card` - Outer card wrapper. Handles focus ring, cursor, and drag opacity. * `.kanban__card--sm`, `.kanban__card--md`, `.kanban__card--lg` - Card size variants. * `.kanban__card-content` - Inner Motion wrapper carrying background, radius, shadow, and layout. * `.kanban__card-content--sm`, `.kanban__card-content--md`, `.kanban__card-content--lg` - Content size variants. * `.kanban__card-list` - Scrollable GridList container with vertical gap between cards. * `.kanban__scroll-shadow` - Optional ScrollShadow wrapper for constrained, scrollable columns. * `.kanban__drop-indicator` - Custom drop indicator with animated height via `--kanban-drop-height`. * `.kanban__drag-handle` - Grip button, screen-reader-only by default. Reveals on focus. * `.kanban__empty` - Empty state placeholder centered inside an empty column. #### Interactive States * `.kanban__card:hover .kanban__card-content` - applies `shadow-sm`. * `.kanban__card[data-selected="true"] .kanban__card-content` - applies `bg-accent-soft`. * `.kanban__card[data-dragging]` - reduced opacity. * `.kanban__card[data-focus-visible="true"]` - focus ring with `ring-2`. * `.kanban__column-header:hover .kanban__column-actions` - opacity transitions to 1. * `.kanban__drop-indicator[data-drop-target]` - border and animated height. #### CSS Variables * `--kanban-column-min-width` - Minimum column width (default: `280px`). * `--kanban-column-gap` - Gap between columns (default: `16px`). * `--kanban-column-height` - Column height reference (default: `480px`). * `--kanban-drop-height` - Drop indicator placeholder height. Set automatically by `useKanbanCardPlaceholder`. * `--kanban-indicator-color` - Color of the column status dot. ## API Reference ### Kanban The root board container. Wraps children in a horizontal `ScrollShadow`. | Prop | Type | Default | Description | | ------ | ---------------------- | ------- | ------------------------------------------------------------------- | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size variant controlling card padding, font size, and column width. | Also supports all [ScrollShadow](/docs/react/components/scroll-shadow) props (except `size`). ### Kanban.CardList The scrollable card list backed by React Aria `GridList`. | Prop | Type | Default | Description | | ------------------ | ---------------------------------- | -------- | -------------------------------------------------------- | | `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | Selection behavior for cards. | | `renderEmptyState` | `() => ReactNode` | — | Custom empty state content when the column has no cards. | | `dragAndDropHooks` | `DragAndDropHooks` | — | The hooks returned by `useKanbanColumn`. | Also supports all React Aria [GridList](https://react-spectrum.adobe.com/react-aria/GridList.html) props. ### Kanban.Card An individual draggable card. Wraps content in a Motion `layout` animation. Backed by React Aria `GridListItem`. | Prop | Type | Default | Description | | ------------------ | ------------------------------------------- | ------- | --------------------------------------------------------------------------- | | `textValue` | `string` | — | Accessible text value for the card. | | `contentClassName` | `string` | — | Class names for the inner Motion content surface. | | `children` | `ReactNode \| ((renderProps) => ReactNode)` | — | Card content. May be a render function receiving GridListItem render props. | Also supports all React Aria [GridListItem](https://react-spectrum.adobe.com/react-aria/GridList.html#gridlistitem) props. ### Kanban.DropIndicator Visual drop indicator shown during drag operations. | Prop | Type | Default | Description | | -------- | ------------ | ------- | ---------------------------------------------------------------------------------------------------- | | `target` | `DropTarget` | — | The drop target provided by React Aria. | | `height` | `number` | — | Height of the placeholder in pixels. Typically the dragged card's height via `--kanban-drop-height`. | ### Kanban.DragHandle Grip button for keyboard-accessible dragging. Screen-reader-only by default; reveals on focus. Uses `slot="drag"`. Also supports all React Aria [Button](https://react-spectrum.adobe.com/react-aria/Button.html) props. ### Kanban.ScrollShadow Optional scroll wrapper for constrained column heights. Use with `max-h-[...]` to enable vertical scrolling within a column. Also supports all [ScrollShadow](/docs/react/components/scroll-shadow) props. ### Other Parts `Kanban.Column`, `Kanban.ColumnHeader`, `Kanban.ColumnBody`, `Kanban.ColumnActions`, `Kanban.ColumnIndicator`, `Kanban.ColumnTitle`, and `Kanban.ColumnCount` are styling wrappers that render `section`, `header`, `div`, `h3`, and `span` elements respectively and accept standard HTML props. ### useKanban Manages the shared list data for the entire board. ```tsx const kanban = useKanban({ initialItems: tasks, getColumn: (item) => item.status, setColumn: (item, column) => ({ ...item, status: column }), }); ``` | Option | Type | Default | Description | | -------------- | -------------------------------- | ------------------ | ------------------------------------------------------------ | | `initialItems` | `T[]` | — | Items to populate the board with. | | `getColumn` | `(item: T) => string` | — | Return the column identifier for a given item. | | `setColumn` | `(item: T, column: string) => T` | — | Return a copy of the item assigned to a new column. | | `dragType` | `string` | `"kanban-item-id"` | Custom drag type for transferring item keys between columns. | | `getKey` | `(item: T) => string \| number` | `item.id` | Derive a unique key from each item. | **Returns:** `{ list, addItem, removeItem, moveItem, updateItem, getColumn, setColumn, getKey, dragType }`. ### useKanbanColumn Provides filtered items and drag-and-drop hooks for a single column. ```tsx const { items, dragAndDropHooks } = useKanbanColumn(kanban, "in-progress", { renderDropIndicator, }); ``` | Option | Type | Default | Description | | ----------------------------- | -------------------------- | ------- | ---------------------------------------------- | | `kanban` | `UseKanbanReturn` | — | The kanban instance from `useKanban`. | | `column` | `string` | — | The column identifier to filter items by. | | `options.renderDropIndicator` | `(target) => ReactElement` | — | Override the default drop indicator rendering. | **Returns:** `{ items, dragAndDropHooks }` — pass `dragAndDropHooks` to `Kanban.CardList`. ### useKanbanCardPlaceholder Measures the dragged card's height and writes it as a CSS variable for card-sized drop indicators. ```tsx const { renderDropIndicator } = useKanbanCardPlaceholder({ renderIndicator: (target) => , }); ``` | Option | Type | Default | Description | | ----------------- | -------------------------------------- | ------- | ------------------------------------------------------------- | | `renderIndicator` | `(target: DropTarget) => ReactElement` | — | Render function that returns a custom `Kanban.DropIndicator`. | **Returns:** `{ renderDropIndicator }` — pass to `useKanbanColumn` options.
# Number Value **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/number-value **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/number-value.mdx > A formatted number display with locale-aware rendering for currency, percentages, and compact notation. ## Import ```tsx import { NumberValue } from '@darkcode-ui/react'; ``` ## Anatomy Import the NumberValue component and access all parts using dot notation. > The formatted number is always rendered in the `.number-value__value` element. Add > `` and `` to place text around it. ```tsx ``` ### Usage By default, `NumberValue` renders a locale-aware decimal number using `Intl.NumberFormat`. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueDefault() { return (
); } ``` ### Compact Use `notation="compact"` to render large numbers in short form (e.g. `1.2K`, `1.2M`). ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueCompact() { return (
); } ``` ### Currency Set `style="currency"` together with a `currency` code to format monetary values. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueCurrency() { return (
); } ``` ### Format Options Pass `formatOptions` to access the full `Intl.NumberFormat` API. It overrides the individual convenience props. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueFormatOptions() { return (
); } ``` ### Percent Set `style="percent"` to render a fraction as a percentage. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValuePercent() { return (
); } ``` ### Sign Display Use `signDisplay` to control when the `+` / `-` sign is shown. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueSignDisplay() { return (
); } ``` ### Tabular Nums The value uses tabular figures by default, so digits stay aligned as the number changes. ```tsx "use client"; import {NumberValue} from "@darkcode-ui/react"; import React from "react"; export function NumberValueTabularNums() { const [value, setValue] = React.useState(12500.0); React.useEffect(() => { const id = setInterval(() => { setValue((current) => current + (Math.random() - 0.5) * 480); }, 600); return () => clearInterval(id); }, []); return (
); } ``` ### With Prefix Suffix Use `NumberValue.Prefix` and `NumberValue.Suffix` to add contextual text around the value. ```tsx import {NumberValue} from "@darkcode-ui/react"; export function NumberValueWithPrefixSuffix() { return (
/ 5 ~ ms °F
); } ``` ## Localization `NumberValue` reads the locale from the nearest [`I18nProvider`](https://react-spectrum.adobe.com/react-aria/I18nProvider.html). Wrap your app (or a subtree) to format numbers for a specific locale, or pass the `locale` prop to override it on a single value. ```tsx import {I18nProvider} from 'react-aria-components'; import {NumberValue} from '@darkcode-ui/react'; ``` ## Styling ### Passing Tailwind CSS classes ```tsx import {NumberValue} from '@darkcode-ui/react'; function CustomNumberValue() { return ( USD ); } ``` ### Customizing the component classes To customize the NumberValue component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .number-value { @apply items-baseline gap-0.5; } .number-value__value { @apply font-semibold tabular-nums; } .number-value__prefix, .number-value__suffix { @apply text-muted; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The NumberValue component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/number-value.css)): #### Base Classes * `.number-value` - Base inline-flex wrapper #### Element Classes * `.number-value__prefix` - Text before the formatted number * `.number-value__value` - The formatted number * `.number-value__suffix` - Text after the formatted number ## API Reference ### NumberValue The root component. Formats and displays a number using locale-aware `Intl.NumberFormat`. | Prop | Type | Default | Description | | ----------------------- | ------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------- | | `value` | `number` | - | **Required.** The numeric value to format | | `style` | `'decimal' \| 'currency' \| 'percent' \| 'unit'` | `'decimal'` | Formatting style | | `currency` | `string` | - | Currency code (e.g. `"USD"`). Required when `style` is `"currency"` | | `unit` | `string` | - | Unit type (e.g. `"degree"`). Required when `style` is `"unit"` | | `notation` | `'standard' \| 'compact' \| 'scientific' \| 'engineering'` | `'standard'` | Notation style | | `signDisplay` | `'auto' \| 'always' \| 'exceptZero' \| 'never'` | - | Controls when the sign is displayed | | `minimumFractionDigits` | `number` | - | Minimum number of fraction digits | | `maximumFractionDigits` | `number` | - | Maximum number of fraction digits | | `locale` | `string` | - | Override the locale from the nearest I18nProvider | | `formatOptions` | `Intl.NumberFormatOptions` | - | Format options passed directly to the formatter. Overrides individual convenience props | | `children` | `React.ReactNode \| ((formatted: string) => React.ReactNode)` | - | Prefix/Suffix sub-components or a render function receiving the formatted string | Also supports all native `span` HTML attributes except `children` and `style`. ### NumberValue.Prefix Text displayed before the formatted number. | Prop | Type | Default | Description | | ---------- | ----------------- | ------- | ----------- | | `children` | `React.ReactNode` | - | Prefix text | Also supports all native `span` HTML attributes. ### NumberValue.Suffix Text displayed after the formatted number. | Prop | Type | Default | Description | | ---------- | ----------------- | ------- | ----------- | | `children` | `React.ReactNode` | - | Suffix text | Also supports all native `span` HTML attributes.
# Table **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/table **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/table.mdx > Tables display structured data in rows and columns with support for sorting, selection, column resizing, and infinite scrolling. ## Import ```tsx import { Table } from '@darkcode-ui/react'; ``` ### Usage ```tsx import {Table} from "@darkcode-ui/react"; export function Basic() { return ( Name Role Status Email Kate Moore CEO Active kate@acme.com John Smith CTO Active john@acme.com Sara Johnson CMO On Leave sara@acme.com Michael Brown CFO Active michael@acme.com
); } ``` ### Anatomy Import the Table component and access all parts using dot notation. ```tsx import { Table } from '@darkcode-ui/react'; export default () => ( Name Role Kate Moore CEO {/* Optional footer content */}
); ``` ### Secondary Variant ```tsx import {Table} from "@darkcode-ui/react"; export function SecondaryVariant() { return ( Name Role Status Email Kate Moore CEO Active kate@acme.com John Smith CTO Active john@acme.com Sara Johnson CMO On Leave sara@acme.com Michael Brown CFO Active michael@acme.com
); } ``` ### Sorting Columns can be made sortable using the `allowsSorting` prop on `Table.Column`. Use `sortDescriptor` and `onSortChange` on `Table.Content` to manage sort state. ```tsx "use client"; import type {SortDescriptor} from "@darkcode-ui/react"; import {Table, cn} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useMemo, useState} from "react"; interface User { id: number; name: string; role: string; status: string; email: string; } const users: User[] = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, { email: "emily@acme.com", id: 5, name: "Emily Davis", role: "Product Manager", status: "Inactive", }, ]; function SortableColumnHeader({ children, sortDirection, }: { children: React.ReactNode; sortDirection?: "ascending" | "descending"; }) { return ( {children} {!!sortDirection && ( )} ); } export function Sorting() { const [sortDescriptor, setSortDescriptor] = useState({ column: "name", direction: "ascending", }); const sortedUsers = useMemo(() => { return [...users].sort((a, b) => { const col = sortDescriptor.column as keyof User; const first = String(a[col]); const second = String(b[col]); let cmp = first.localeCompare(second); if (sortDescriptor.direction === "descending") { cmp *= -1; } return cmp; }); }, [sortDescriptor]); return ( {({sortDirection}) => ( Name )} {({sortDirection}) => ( Role )} {({sortDirection}) => ( Status )} {({sortDirection}) => ( Email )} {sortedUsers.map((user) => ( {user.name} {user.role} {user.status} {user.email} ))}
); } ``` ### Selection Enable row selection with `selectionMode` on `Table.Content`. Use `Checkbox` with `slot="selection"` for select-all and per-row checkboxes. ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Checkbox, Table} from "@darkcode-ui/react"; import {useState} from "react"; const users = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, ]; export function SelectionDemo() { const [selectedKeys, setSelectedKeys] = useState(new Set()); return (
Name Role Status Email {users.map((user) => ( {user.name} {user.role} {user.status} {user.email} ))}

Selected:{" "} {selectedKeys === "all" ? "All" : selectedKeys.size > 0 ? Array.from(selectedKeys).join(", ") : "None"}

); } ``` ### Custom Cells ```tsx "use client"; import type {Selection, SortDescriptor} from "@darkcode-ui/react"; import {Avatar, Button, Checkbox, Chip, Table, cn} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useMemo, useState} from "react"; interface User { id: number; name: string; image_url: string; role: string; status: "Active" | "Inactive" | "On Leave"; email: string; } const statusColorMap: Record = { Active: "success", Inactive: "danger", "On Leave": "warning", }; const users: User[] = [ { email: "kate@acme.com", id: 4586932, image_url: "https://darkcode-ui-assets.darkcode.dev/avatars/red.jpg", name: "Kate Moore", role: "Chief Executive Officer", status: "Active", }, { email: "john@acme.com", id: 5273849, image_url: "https://darkcode-ui-assets.darkcode.dev/avatars/green.jpg", name: "John Smith", role: "Chief Technology Officer", status: "Active", }, { email: "sara@acme.com", id: 7492836, image_url: "https://darkcode-ui-assets.darkcode.dev/avatars/blue.jpg", name: "Sara Johnson", role: "Chief Marketing Officer", status: "On Leave", }, { email: "michael@acme.com", id: 8293746, image_url: "https://darkcode-ui-assets.darkcode.dev/avatars/purple.jpg", name: "Michael Brown", role: "Chief Financial Officer", status: "Active", }, { email: "emily@acme.com", id: 1234567, image_url: "https://darkcode-ui-assets.darkcode.dev/avatars/orange.jpg", name: "Emily Davis", role: "Product Manager", status: "Inactive", }, ]; function SortableColumnHeader({ children, sortDirection, }: { children: React.ReactNode; sortDirection?: "ascending" | "descending"; }) { return ( {children} {!!sortDirection && ( )} ); } export function CustomCells() { const [selectedKeys, setSelectedKeys] = useState(new Set()); const [sortDescriptor, setSortDescriptor] = useState({ column: "name", direction: "ascending", }); const sortedUsers = useMemo(() => { return [...users].sort((a, b) => { const col = sortDescriptor.column as keyof User; const first = String(a[col]); const second = String(b[col]); let cmp = first.localeCompare(second); if (sortDescriptor.direction === "descending") { cmp *= -1; } return cmp; }); }, [sortDescriptor]); return ( {({sortDirection}) => ( Worker ID )} {({sortDirection}) => ( Member )} {({sortDirection}) => ( Role )} {({sortDirection}) => ( Status )} Actions {sortedUsers.map((user) => (
#{user.id.toString()}{" "}
{user.name .split(" ") .map((n) => n[0]) .join("")}
{user.name} {user.email}
{user.role} {user.status}
))}
); } ``` ### Expandable Rows Rows can be nested to display hierarchical data. Use the `treeColumn` prop to designate a column, and render a `Button` with `slot="chevron"` in that column’s cells so users can expand and collapse the row. Use the `expandedKeys` prop to control which rows are expanded. ```tsx "use client"; import type {Selection} from "@darkcode-ui/react"; import {Button, Table, cn} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import {useState} from "react"; export function ExpandableRows() { type Row = { children: Row[]; date: string; id: string; title: string; type: string; }; const data: Row[] = [ { children: [ { children: [ {children: [], date: "7/10/2025", id: "3", title: "Weekly Report", type: "File"}, {children: [], date: "8/20/2025", id: "4", title: "Budget", type: "File"}, ], date: "8/2/2025", id: "2", title: "Project", type: "Directory", }, ], date: "10/20/2025", id: "1", title: "Documents", type: "Directory", }, { children: [ {children: [], date: "1/23/2026", id: "6", title: "Image 1", type: "File"}, {children: [], date: "2/3/2026", id: "7", title: "Image 2", type: "File"}, ], date: "2/3/2026", id: "5", title: "Photos", type: "Directory", }, ]; const [expandedKeys, setExpandedKeys] = useState(() => new Set(["1"])); const renderExpandableRow = (item: Row) => { return ( {({hasChildItems, isDisabled, isExpanded, isTreeColumn}) => ( {hasChildItems && isTreeColumn ? ( ) : null} {item.title} )} {item.type} {item.date} {renderExpandableRow} ); }; return ( Name Type Date Modified {renderExpandableRow}
); } ``` ### Pagination Use `Table.Footer` to add a pagination component below the table. ```tsx "use client"; import {Pagination, Table} from "@darkcode-ui/react"; import {useMemo, useState} from "react"; const columns = [ {id: "name", name: "Name"}, {id: "role", name: "Role"}, {id: "status", name: "Status"}, {id: "email", name: "Email"}, ]; const users = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, { email: "emily@acme.com", id: 5, name: "Emily Davis", role: "Product Manager", status: "Inactive", }, {email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "Lead Designer", status: "Active"}, { email: "olivia@acme.com", id: 7, name: "Olivia Martinez", role: "Frontend Engineer", status: "Active", }, { email: "james@acme.com", id: 8, name: "James Taylor", role: "Backend Engineer", status: "Active", }, ]; const ROWS_PER_PAGE = 4; export function PaginationDemo() { const [page, setPage] = useState(1); const totalPages = Math.ceil(users.length / ROWS_PER_PAGE); const pages = Array.from({length: totalPages}, (_, i) => i + 1); const paginatedItems = useMemo(() => { const start = (page - 1) * ROWS_PER_PAGE; return users.slice(start, start + ROWS_PER_PAGE); }, [page]); const start = (page - 1) * ROWS_PER_PAGE + 1; const end = Math.min(page * ROWS_PER_PAGE, users.length); return ( {(column) => ( {column.name} )} {(user) => ( {(column) => {user[column.id as keyof typeof user]}} )} {start} to {end} of {users.length} results setPage((p) => Math.max(1, p - 1))} > Prev {pages.map((p) => ( setPage(p)}> {p} ))} setPage((p) => Math.min(totalPages, p + 1))} > Next
); } ``` ### Column Resizing Wrap the table in `Table.ResizableContainer` and add `Table.ColumnResizer` inside each resizable column. ```tsx import {Chip, Table} from "@darkcode-ui/react"; export function ColumnResizing() { return ( Name Role Status Email Kate Moore CEO Active kate@acme.com John Smith CTO Active john@acme.com Sara Johnson CMO On Leave sara@acme.com Michael Brown CFO Active michael@acme.com Emily Davis Product Manager Inactive emily@acme.com
); } ``` ### Empty State Use `renderEmptyState` on `Table.Body` to display a custom message when the table has no data. ```tsx "use client"; import {EmptyState, Table} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; export function EmptyStateDemo() { return ( Name Role Status Email ( No results found )} > {[]}
); } ``` ### Async Loading Use `Table.LoadMore` for infinite scrolling. It renders a sentinel row that triggers `onLoadMore` when scrolled into view. ```tsx "use client"; import {Chip, Spinner, Table} from "@darkcode-ui/react"; import {useCallback, useRef, useState} from "react"; interface User { id: number; name: string; role: string; status: string; email: string; } const statusColorMap: Record = { Active: "success", Inactive: "danger", "On Leave": "warning", }; const allUsers: User[] = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, { email: "emily@acme.com", id: 5, name: "Emily Davis", role: "Product Manager", status: "Inactive", }, {email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "Lead Designer", status: "Active"}, { email: "olivia@acme.com", id: 7, name: "Olivia Martinez", role: "Frontend Engineer", status: "Active", }, { email: "james@acme.com", id: 8, name: "James Taylor", role: "Backend Engineer", status: "Active", }, { email: "sophia@acme.com", id: 9, name: "Sophia Anderson", role: "QA Engineer", status: "On Leave", }, {email: "liam@acme.com", id: 10, name: "Liam Thomas", role: "DevOps Engineer", status: "Active"}, { email: "lucas@acme.com", id: 11, name: "Lucas Martinez", role: "Product Manager", status: "Active", }, { email: "emma@acme.com", id: 12, name: "Emma Johnson", role: "Frontend Engineer", status: "Active", }, {email: "noah@acme.com", id: 13, name: "Noah Davis", role: "Backend Engineer", status: "Active"}, {email: "ava@acme.com", id: 14, name: "Ava Wilson", role: "Lead Designer", status: "Active"}, { email: "oliver@acme.com", id: 15, name: "Oliver Martinez", role: "Frontend Engineer", status: "Active", }, { email: "isabella@acme.com", id: 16, name: "Isabella Johnson", role: "Backend Engineer", status: "Active", }, {email: "mia@acme.com", id: 17, name: "Mia Davis", role: "Lead Designer", status: "Active"}, { email: "william@acme.com", id: 18, name: "William Wilson", role: "Frontend Engineer", status: "Active", }, ]; const ITEMS_PER_PAGE = 6; const columns = [ {id: "name", name: "Name"}, {id: "role", name: "Role"}, {id: "status", name: "Status"}, {id: "email", name: "Email"}, ]; export function AsyncLoading() { const [items, setItems] = useState(() => allUsers.slice(0, ITEMS_PER_PAGE)); const [isLoading, setIsLoading] = useState(false); const isLoadingRef = useRef(false); const hasMore = items.length < allUsers.length; const loadMore = useCallback(() => { if (!hasMore || isLoadingRef.current) return; isLoadingRef.current = true; setIsLoading(true); setTimeout(() => { setItems((prev) => allUsers.slice(0, prev.length + ITEMS_PER_PAGE)); setIsLoading(false); requestAnimationFrame(() => { isLoadingRef.current = false; }); }, 1500); }, [hasMore]); return ( {columns.map((col) => ( {col.name} ))} {(user) => ( {user.name} {user.role} {user.status} {user.email} )} {!!hasMore && ( )}
); } ``` ### Virtualization Table supports virtualization through [Virtualizer](https://react-aria.adobe.com/Virtualizer), enabling efficient rendering of large datasets by displaying only the rows visible within the viewport. ```tsx "use client"; import {Table, TableLayout, Virtualizer} from "@darkcode-ui/react"; interface User { id: number; name: string; role: string; email: string; } export function Virtualization() { const roles = [ "Software Engineer", "Senior Engineer", "Staff Engineer", "Product Manager", "Designer", "Data Analyst", "QA Engineer", "DevOps Engineer", "Marketing Manager", "Sales Representative", ]; const firstNames = [ "Emma", "Liam", "Olivia", "Noah", "Ava", "James", "Sophia", "Oliver", "Isabella", "Lucas", "Mia", "Ethan", "Charlotte", "Mason", "Amelia", "Logan", "Harper", "Alexander", "Ella", "Benjamin", ]; const lastNames = [ "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Anderson", "Taylor", "Thomas", "Jackson", "White", "Harris", "Clark", "Lewis", "Robinson", "Walker", ]; function generateUsers(count: number): User[] { const users: User[] = []; for (let i = 0; i < count; i++) { const firstName = firstNames[i % firstNames.length]; const lastName = lastNames[Math.floor(i / firstNames.length) % lastNames.length]; const name = `${firstName} ${lastName}`; users.push({ email: `${firstName?.toLowerCase()}.${lastName?.toLowerCase()}@acme.com`, id: i + 1, name, role: roles[i % roles.length] || "", }); } return users; } const virtualizedUsers = generateUsers(1000); return ( Name Role Email {(user) => ( {user.name} {user.role} {user.email} )}
); } ``` ### TanStack Table DarkCode UI's Table works as a rendering layer on top of headless table libraries. This example uses [TanStack Table](https://tanstack.com/table) for column definitions, sorting, and pagination — while DarkCode UI handles styling and accessibility. ```tsx "use client"; import type {SortDescriptor} from "@darkcode-ui/react"; import type {SortingState} from "@tanstack/react-table"; import {Chip, Pagination, Table, cn} from "@darkcode-ui/react"; import {Icon} from "@iconify/react"; import { createColumnHelper, flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table"; import {useMemo, useState} from "react"; // --- Data ----------------------------------------------------------------- interface User { id: number; name: string; role: string; status: "Active" | "Inactive" | "On Leave"; email: string; } const statusColorMap: Record = { Active: "success", Inactive: "danger", "On Leave": "warning", }; const users: User[] = [ {email: "kate@acme.com", id: 1, name: "Kate Moore", role: "CEO", status: "Active"}, {email: "john@acme.com", id: 2, name: "John Smith", role: "CTO", status: "Active"}, {email: "sara@acme.com", id: 3, name: "Sara Johnson", role: "CMO", status: "On Leave"}, {email: "michael@acme.com", id: 4, name: "Michael Brown", role: "CFO", status: "Active"}, { email: "emily@acme.com", id: 5, name: "Emily Davis", role: "Product Manager", status: "Inactive", }, {email: "davis@acme.com", id: 6, name: "Davis Wilson", role: "Lead Designer", status: "Active"}, { email: "olivia@acme.com", id: 7, name: "Olivia Martinez", role: "Frontend Engineer", status: "Active", }, { email: "james@acme.com", id: 8, name: "James Taylor", role: "Backend Engineer", status: "Active", }, ]; // --- TanStack Column Definitions ------------------------------------------ const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor("name", {header: "Name"}), columnHelper.accessor("role", {header: "Role"}), columnHelper.accessor("status", { cell: (info) => ( {info.getValue()} ), header: "Status", }), columnHelper.accessor("email", {header: "Email"}), ]; // --- Sorting Bridge ------------------------------------------------------- // Convert TanStack SortingState → React Aria SortDescriptor function toSortDescriptor(sorting: SortingState): SortDescriptor | undefined { const first = sorting[0]; if (!first) return undefined; return { column: first.id, direction: first.desc ? "descending" : "ascending", }; } // Convert React Aria SortDescriptor → TanStack SortingState function toSortingState(descriptor: SortDescriptor): SortingState { return [{desc: descriptor.direction === "descending", id: descriptor.column as string}]; } // --- Sort Header ---------------------------------------------------------- function SortableColumnHeader({ children, sortDirection, }: { children: React.ReactNode; sortDirection?: "ascending" | "descending"; }) { return ( {children} {!!sortDirection && ( )} ); } // --- Component ------------------------------------------------------------ const PAGE_SIZE = 4; export function TanstackTable() { const [sorting, setSorting] = useState([]); // eslint-disable-next-line react-hooks/incompatible-library const table = useReactTable({ columns, data: users, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), initialState: {pagination: {pageSize: PAGE_SIZE}}, onSortingChange: setSorting, state: {sorting}, }); const sortDescriptor = useMemo(() => toSortDescriptor(sorting), [sorting]); const {pageIndex} = table.getState().pagination; const pageCount = table.getPageCount(); const pages = Array.from({length: pageCount}, (_, i) => i + 1); const start = pageIndex * PAGE_SIZE + 1; const end = Math.min((pageIndex + 1) * PAGE_SIZE, users.length); return ( setSorting(toSortingState(d))} > {table.getHeaderGroups()[0]!.headers.map((header) => ( {({sortDirection}) => ( {flexRender(header.column.columnDef.header, header.getContext())} )} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} ))} {start} to {end} of {users.length} results table.previousPage()} > Prev {pages.map((p) => ( table.setPageIndex(p - 1)} > {p} ))} table.nextPage()} > Next
); } ``` ## Related Components * **Data Grid**: Full-featured data grid with sorting, selection, pinned columns, tree rows, and virtualization * **Pagination**: Page navigation with composable page links and controls * **Checkbox**: Binary choice input control ## Styling ### Passing Tailwind CSS classes You can customize individual Table parts: ```tsx import { Table } from '@darkcode-ui/react'; function CustomTable() { return ( Name Kate Moore
); } ``` ### Customizing the component classes To customize the Table component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes). ```css @layer components { .table-root { @apply relative grid w-full overflow-clip; } .table__header { @apply bg-gray-100; } .table__column { @apply px-4 py-2.5 text-left text-xs font-medium text-gray-600; } .table__row { @apply bg-white border-b border-gray-200; } .table__cell { @apply px-4 py-3 text-sm; } .table__footer { @apply flex items-center px-4 py-2.5; } } ``` DarkCode UI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize. ### CSS Classes The Table component uses these CSS classes ([View source styles](https://github.com/DarkCode-Developers/darkcode-ui/blob/main/packages/styles/components/table.css)): #### Base Classes * `.table-root` - Root container (named `table-root` instead of `table` because `table` is a built-in Tailwind CSS utility class for `display: table`) * `.table__scroll-container` - Horizontal scroll wrapper with custom scrollbar * `.table__content` - The `` element * `.table__header` - Header row (``) * `.table__column` - Column header cell (``) * `.table__row` - Row element (``) * `.table__cell` - Data cell (`
`) * `.table__body` - Body section (`
`) * `.table__footer` - Footer container (outside table) #### Advanced Classes * `.table__column-resizer` - Drag handle for column resizing * `.table__resizable-container` - Wrapper enabling column resizing * `.table__load-more` - Sentinel row for infinite scrolling * `.table__load-more-content` - Styled container for the loading indicator #### Variant Classes * `.table-root--primary` - Gray background container with card-style body (default) * `.table-root--secondary` - No background, standalone rounded headers ### Interactive States The Table supports both CSS pseudo-classes and data attributes for flexibility: * **Hover**: `:hover` or `[data-hovered="true"]` (row background change) * **Selected**: `[data-selected="true"]` (row highlight) * **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (inset focus ring on rows, columns, and cells) * **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity) * **Sortable**: `[data-allows-sorting="true"]` (interactive cursor on columns) * **Dragging**: `[data-dragging="true"]` (reduced opacity) * **Drop Target**: `[data-drop-target="true"]` (accent background) ## API Reference ### Table Props | Prop | Type | Default | Description | | ----------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------- | | `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant. Primary has a gray background container; secondary is flat with transparent rows. | | `className` | `string` | - | Additional CSS classes for the root container | | `children` | `React.ReactNode` | - | Table content (ScrollContainer, Footer, etc.) | ### Table.ScrollContainer Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Table.Content element | ### Table.Content Props Inherits from [React Aria Table](https://react-spectrum.adobe.com/react-aria/Table.html). | Prop | Type | Default | Description | | ------------------- | -------------------------------------- | -------- | ------------------------------ | | `aria-label` | `string` | - | Accessible label for the table | | `selectionMode` | `"none" \| "single" \| "multiple"` | `"none"` | Selection behavior | | `selectedKeys` | `Selection` | - | Controlled selected keys | | `onSelectionChange` | `(keys: Selection) => void` | - | Selection change handler | | `sortDescriptor` | `SortDescriptor` | - | Current sort state | | `onSortChange` | `(descriptor: SortDescriptor) => void` | - | Sort change handler | | `className` | `string` | - | Additional CSS classes | ### Table.Header Props Inherits from [React Aria TableHeader](https://react-spectrum.adobe.com/react-aria/Table.html#tableheader). | Prop | Type | Default | Description | | ---------- | --------------------------------------------------- | ------- | ------------------------------------------- | | `columns` | `T[]` | - | Dynamic column data for render prop pattern | | `children` | `React.ReactNode \| (column: T) => React.ReactNode` | - | Static columns or render prop | ### Table.Column Props Inherits from [React Aria Column](https://react-spectrum.adobe.com/react-aria/Table.html#column). | Prop | Type | Default | Description | | --------------- | ------------------------------------------------------------------- | ------- | ------------------------------------------------- | | `id` | `string` | - | Column identifier | | `allowsSorting` | `boolean` | `false` | Whether the column is sortable | | `isRowHeader` | `boolean` | `false` | Whether this column is a row header | | `defaultWidth` | `string \| number` | - | Default width for resizable columns | | `minWidth` | `number` | - | Minimum width for resizable columns | | `children` | `React.ReactNode \| (values: ColumnRenderProps) => React.ReactNode` | - | Column content or render prop with sort direction | ### Table.Body Props Inherits from [React Aria TableBody](https://react-spectrum.adobe.com/react-aria/Table.html#tablebody). | Prop | Type | Default | Description | | ------------------ | ------------------------------------------------- | ------- | ------------------------------------------ | | `items` | `T[]` | - | Dynamic row data for render prop pattern | | `renderEmptyState` | `() => React.ReactNode` | - | Content to display when the table is empty | | `children` | `React.ReactNode \| (item: T) => React.ReactNode` | - | Static rows or render prop | ### Table.Row Props Inherits from [React Aria Row](https://react-spectrum.adobe.com/react-aria/Table.html#row). | Prop | Type | Default | Description | | ----------- | ------------------ | ------- | ---------------------- | | `id` | `string \| number` | - | Row identifier | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Row cells | ### Table.Cell Props Inherits from [React Aria Cell](https://react-spectrum.adobe.com/react-aria/Table.html#cell). | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Cell content | ### Table.Footer Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | --------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Footer content (e.g., pagination) | ### Table.ColumnResizer Props Inherits from [React Aria ColumnResizer](https://react-spectrum.adobe.com/react-aria/Table.html#columnresizer). | Prop | Type | Default | Description | | ----------- | -------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | ### Table.ResizableContainer Props Inherits from [React Aria ResizableTableContainer](https://react-spectrum.adobe.com/react-aria/Table.html#resizabletablecontainer). | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ---------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Table.Content element | ### Table.LoadMore Props Inherits from [React Aria TableLoadMoreItem](https://react-spectrum.adobe.com/react-aria/Table.html). | Prop | Type | Default | Description | | ------------ | ----------------- | ------- | ----------------------------------------------- | | `isLoading` | `boolean` | `false` | Whether data is currently loading | | `onLoadMore` | `() => void` | - | Handler called when the sentinel row is visible | | `children` | `React.ReactNode` | - | Loading indicator content | ### Table.LoadMoreContent Props | Prop | Type | Default | Description | | ----------- | ----------------- | ------- | ----------------------------------------- | | `className` | `string` | - | Additional CSS classes | | `children` | `React.ReactNode` | - | Loading indicator content (e.g., Spinner) | ### Table.Collection Props Re-exported from React Aria `Collection`. Used to render dynamic cells within rows alongside static cells (e.g., checkboxes). | Prop | Type | Default | Description | | ---------- | ------------------------------ | ------- | ------------------------- | | `items` | `T[]` | - | Collection items | | `children` | `(item: T) => React.ReactNode` | - | Render prop for each item | ### TableLayout | Name | Type | Default | Description | | ------------------------ | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `rowHeight` | `number \| undefined` | 48 | The fixed height of a row in px. | | `estimatedRowHeight` | `number \| undefined` | — | The estimated height of a row, when row heights are variable. | | `headingHeight` | `number \| undefined` | 48 | The fixed height of a section header in px. | | `estimatedHeadingHeight` | `number \| undefined` | — | The estimated height of a section header, when the height is variable. | | `loaderHeight` | `number \| undefined` | 48 | The fixed height of a loader element in px. This loader is specifically for "load more" elements rendered when loading more rows at the root level or inside nested row/sections. | | `dropIndicatorThickness` | `number \| undefined` | 2 | The thickness of the drop indicator. | | `gap` | `number \| undefined` | 0 | The gap between items. | | `padding` | `number \| undefined` | 0 | The padding around the list. | # Timeline **Category**: react **URL**: https://ui.darkcode.dev/en/docs/react/components/timeline **Source**: https://raw.githubusercontent.com/DarkCode-Developers/darkcode-ui/refs/heads/main/apps/docs/content/docs/en/react/components/(data-display)/timeline.mdx > A composable, read-only chronology for activity feeds, audit trails, incident logs, and centered milestone roadmaps. ## Import ```tsx import { Timeline } from '@darkcode-ui/react'; ``` ### Usage Timeline is for read-only chronology: activity feeds, audit trails, incident history, release logs, and milestone roadmaps. Use [Stepper](/react/components/stepper) when the user is moving through a known sequence where current, complete, and upcoming steps are the primary meaning. ```tsx import {Timeline} from "@darkcode-ui/react"; type Status = "default" | "current" | "success" | "warning" | "danger" | "muted"; const events: {title: string; time: string; description: string; status: Status}[] = [ { description: "Traffic is now served from the new revision across all regions.", status: "success", time: "10:24 AM", title: "Deploy completed", }, { description: "The canary build is rolling out to 10% of production pods.", status: "current", time: "10:12 AM", title: "Rolling out canary", }, { description: "Container image built and pushed to the registry.", status: "default", time: "10:03 AM", title: "Image built", }, { description: "A new commit landed on the main branch.", status: "muted", time: "09:58 AM", title: "Commit pushed", }, ]; export function Default() { return ( {events.map((event) => (

{event.title}

{event.description}

))}
); } ``` ### Anatomy Import the Timeline component and access all parts using dot notation. ```tsx import { Timeline } from '@darkcode-ui/react'; export default () => (

Event title

Event details

); ``` Timeline intentionally owns only the chronology layout. It renders an `ol` with `li` items, keeps connectors decorative, hides empty markers from assistive technology, and marks `status="current"` items with `aria-current="true"`. Use semantic HTML and other DarkCode UI components such as [Card](/react/components/card), [Chip](/react/components/chip), and [Avatar](/react/components/avatar) inside `Timeline.Content` for the event content itself. The `Timeline.Rail`, `Timeline.Marker`, and `Timeline.Connector` parts are optional — when omitted, each renders a sensible default. The example below is equivalent to the verbose anatomy above: ```tsx Event title ``` ### Content Subparts Instead of hand-rolling markup, compose the optional `Timeline.Header`, `Timeline.Title`, `Timeline.Time`, and `Timeline.Description` parts inside `Timeline.Content`. `Timeline.Time` renders a semantic `