# Honest UI Full Documentation > Full Markdown snapshot generated from the same MDX source as the Honest UI documentation site. ## Accessibility > Understand Honest UI's accessibility foundation and the checks your application still needs. Source: https://www.honestui.com/docs/accessibility Markdown: https://www.honestui.com/docs/accessibility.md Accessibility starts with the component structure and continues through your content, composition, styling, and testing. Honest UI provides a foundation; it does not make an assembled application conformant by itself. ## What Honest UI provides [#what-honest-ui-provides] Honest UI prefers native HTML for familiar controls. Complex interactions such as dialogs, menus, popovers, comboboxes, and tabs use primitives that provide roles, state, focus management, and documented keyboard behavior. Depending on the component, that foundation can include: * Native roles and states. * Keyboard operation and focus management. * Visible focus styles. * Connections among form controls, labels, descriptions, and errors. * Disabled and invalid states. * Reduced-motion handling for documented animated components. Read the component's Accessibility section because responsibilities differ. An Input needs a useful label, an icon-only Button needs an accessible name, and a chart needs an equivalent way to understand important values. ## Give controls useful names [#give-controls-useful-names] Every form control needs an accessible name. Use a persistent visible label for fields that people must identify, understand, or revisit. [Field](/docs/components/field) connects a label, description, validation message, and control with consistent spacing. Write labels for the information or decision: * `Email address`, not `Email` when the expected value could be unclear. * `Project visibility`, not `Visibility` when several settings use that word. * `Receive product updates`, not `Updates` for a preference switch. Descriptions should explain a constraint or consequence rather than repeat the label. Place essential requirements before submission instead of teaching them only through an error. ## Preserve documented keyboard behavior [#preserve-documented-keyboard-behavior] Test each interaction without a mouse. Do not apply one keyboard pattern to every control: native buttons, checkboxes, tabs, menus, sliders, and comboboxes have different expected keys. Check that: * `Tab` and `Shift+Tab` move through interactive elements in a logical order. * Buttons and links respond to their native activation keys. * Composite controls support the arrow-key behavior documented by their primitive. * `Escape` closes dismissible dialogs and popups when supported. * Focus moves into modal content when it opens and returns to a logical place when it closes. * Every pointer-only action has an equivalent keyboard path. ## Keep focus visible [#keep-focus-visible] Do not remove focus styles. Focus must remain visible against the component and adjacent colors, including in dark themes and forced-colors modes. Check that sticky headers, dialogs, and other overlays do not hide the focused element. Avoid moving focus unless the interaction requires it and the destination is predictable. ## Explain unavailable and loading actions [#explain-unavailable-and-loading-actions] Use a disabled state only when the control cannot be used. If the reason is not obvious, explain it nearby. A disabled control cannot normally reveal its own tooltip or receive keyboard focus, so required instructions must remain available elsewhere. For an action in progress, preserve the layout, communicate the busy state, and prevent accidental duplicate work when necessary. Do not announce success until the requested work has completed. ## Write errors that support recovery [#write-errors-that-support-recovery] Connect validation messages to their controls and identify the specific correction: * `Enter an email address in the format name@example.com.` * `Choose at least one role.` * `Enter 12 or more characters.` Avoid `Invalid`, `Required`, and `Something went wrong` when the cause or recovery step is known. Preserve entered values after an error whenever it is safe to do so. ## Do not rely on one sensory cue [#do-not-rely-on-one-sensory-cue] Do not communicate meaning through color, position, shape, sound, motion, hover, or drag alone. Pair important states with visible text and expose the same meaning to assistive technology. Pointer targets should be at least 24 by 24 CSS pixels unless a WCAG 2.2 spacing or other defined exception applies. Prefer larger targets, including 44 by 44 CSS pixels, for important controls and touch-heavy interfaces. ## Test the complete task [#test-the-complete-task] Automated checks catch only some problems. For each changed task, verify: 1. Semantic structure and accessible names. 2. Complete keyboard operation and visible focus. 3. The task with a supported screen-reader and browser combination. 4. Text enlargement and reflow at 200% and 400% zoom where applicable. 5. Light, dark, and forced-colors presentations. 6. Reduced-motion behavior. 7. Labels, errors, loading, empty, disabled, and success states. 8. Touch target size and operation without precise pointer movement. Document which environments you tested. Do not claim WCAG conformance from a component primitive or automated scan alone. --- ## Changelog > What changed in Honest UI, newest first. Source: https://www.honestui.com/docs/changelog Markdown: https://www.honestui.com/docs/changelog.md This page summarizes user-facing changes to Honest UI in reverse chronological order. It covers new components, behavior changes, fixes that affect you, and documentation updates. Internal refactors appear only when they change something you can observe. Entries are maintained by hand alongside each change. Until the first stable release, dates refer to commits on the main branch rather than numbered versions. ## September 1, 2026 [#september-1-2026] * Added complete starter projects for Next.js, Vite, TanStack Start, React Router, Astro, and Laravel. * Added monorepo starters for Next.js, Vite, TanStack Start, React Router, and Astro. * Moved CLI starter source into the public [honestui-starters repository](https://github.com/honestui/honestui-starters). Each starter includes the complete public component registry, HonestUI styles, and local font files. ## August 25–27, 2026 [#august-2527-2026] * Added the [DataTable](/docs/product/data-table) component with search, filters, pagination, a toolbar, and view options. * Added the [DataGrid](/docs/product/data-grid) component with virtualized rows, inline editing, filtering, and selection. * Added the [DateRangePicker](/docs/product/date-range-picker) component with a calendar, presets, and date limits. * Added the [Filter Bar](/docs/product/filter-bar) component with text, number, and date range fields, async loading, and a DataTable integration. * Added the [File Upload](/docs/product/file-upload) component with drag and drop, paste, validation, and image previews. ## August 21, 2026 [#august-21-2026] * Added structured data (JSON-LD) to documentation pages so search engines and answer engines can cite component facts accurately. * Improved agent readiness: registry files now carry the metadata AI coding agents need to install and adapt components correctly. * The docs API now returns proper error responses and supports content negotiation instead of failing silently. * Fixed a missing navigation icon on the Developers resources page. * Clarified developer resource paths in the docs. ## August 18, 2026 [#august-18-2026] * Added the [Command](/docs/components/command) component for command palettes and searchable menus, built on autocomplete with keyboard-first behavior. * Added the [Context Menu](/docs/components/context-menu) component. * Added comparison graphics showing how Honest UI differs from shadcn/ui and Mantine on the [Comparisons](/compare) pages. ## August 16, 2026 [#august-16-2026] * Expanded the registry server with catalog retrieval, item validation, and tests, making CLI installs more reliable. ## August 10, 2026 [#august-10-2026] * Added a standalone `honestui` CLI package as the single way to initialize projects and add components. * Reworked CLI installation commands around that package. * Added a privacy page describing analytics and added consent management before tracking loads. * Pinned two dependency versions (`ast-types-flow`, `cnfast`) to resolve install issues. ## August 9, 2026 [#august-9-2026] * Added the [Color Picker](/docs/components/color-picker) component with HEX, RGB, HSL, and OKLCH output modes. * Added theme documentation covering radius, spacing, typography, and Tailwind integration. * Added input group examples and a Vitest configuration for the registry. * Added scatter plot and heatmap chart examples. ## August 8, 2026 [#august-8-2026] * Added foundational styles: themes, colors, spacing, typography, and z-index tokens that all components now share. ## August 3–4, 2026 [#august-34-2026] * Expanded the assets collection with logos and vectors alongside icons, each behind its own package entry point. * Added event tracking across documentation components, gated by consent. * Updated the Textarea component with new size and variant props. * Removed an unused logging integration from the codebase. ## July 28, 2026 [#july-28-2026] * Added the grid distortion and light rays shaders with interactive effects. * Added animated component examples and a generated index. * Added box plot, scatter, and heatmap chart examples with reworked scenario data. ## July 27, 2026 [#july-27-2026] * Shipped the first registry server implementation: item retrieval, base color responses, environment loading, and tests. --- ## React UI Components > Browse open-source React UI components for forms, navigation, overlays, feedback, and data display. Copy the source into your project and adapt it. Source: https://www.honestui.com/docs/component-guide Markdown: https://www.honestui.com/docs/component-guide.md Honest UI provides open-source React UI components for common product-interface tasks. Use the CLI to copy each component into your project, then edit the source, variants, and styling without depending on a closed component API. Component pages follow a shared structure so you can decide whether a component fits, install it, and adapt it without guessing which behavior must remain intact. Each page explains what the component is for, how to install it, how to import it, and which examples show the important states. ## Browse UI components [#browse-ui-components] * **Actions and feedback:** [Alert](/docs/components/alert), [Alert Dialog](/docs/components/alert-dialog), [Button](/docs/components/button), [Empty](/docs/components/empty), [Progress](/docs/components/progress), [Skeleton](/docs/components/skeleton), [Toast](/docs/components/toast), and [Toggle](/docs/components/toggle). * **Forms and input:** [Autocomplete](/docs/components/autocomplete), [Checkbox](/docs/components/checkbox), [Color Picker](/docs/components/color-picker), [Combobox](/docs/components/combobox), [Field](/docs/components/field), [Fieldset](/docs/components/fieldset), [Form](/docs/components/form), [Input](/docs/components/input), [Input Group](/docs/components/input-group), [Label](/docs/components/label), [Number Field](/docs/components/number-field), [Radio Group](/docs/components/radio-group), [Select](/docs/components/select), [Slider](/docs/components/slider), [Switch](/docs/components/switch), and [Textarea](/docs/components/textarea). * **Navigation and overlays:** [Breadcrumb](/docs/components/breadcrumb), [Command](/docs/components/command), [Context Menu](/docs/components/context-menu), [Dialog](/docs/components/dialog), [Menu](/docs/components/menu), [Pagination](/docs/components/pagination), [Popover](/docs/components/popover), [Preview Card](/docs/components/preview-card), [Scroll Area](/docs/components/scroll-area), [Sheet](/docs/components/sheet), [Tabs](/docs/components/tabs), [Toolbar](/docs/components/toolbar), and [Tooltip](/docs/components/tooltip). * **Content, data, and layout:** [Accordion](/docs/components/accordion), [Avatar](/docs/components/avatar), [Badge](/docs/components/badge), [Card](/docs/components/card), [Collapsible](/docs/components/collapsible), [Frame](/docs/components/frame), [Group](/docs/components/group), [Kanban List](/docs/components/kanban-list), [Meter](/docs/components/meter), [Separator](/docs/components/separator), and [Table](/docs/components/table). ## Page structure [#page-structure] Component pages include: * **What it does:** the task the component supports. * **When to use it:** where it fits and which component to use instead when it does not. * **Anatomy:** the parts and how they work together. * **Behavior and states:** controlled values, loading, errors, disabled states, and forms when they apply. * **Accessibility:** behavior supplied by the component and responsibilities that remain in your application. * **Installation:** the CLI command and manual source. * **Usage:** the smallest useful example. * **Examples:** variants, sizes, states, form usage, and composition patterns. If a component is built on Base UI, the page may link to the Base UI API reference for lower-level behavior and props. ## Choosing a component [#choosing-a-component] Use the component that matches the user's task: * Use **Button** for actions. * Use a **link** for navigation. Use Button's link rendering only when you need Button styling on a real link destination. * Use **Checkbox** for independent yes/no choices. * Use **Radio Group** when only one option can be selected. * Use **Select** when the user chooses from a known list. * Use **Combobox** or **Autocomplete** when search helps the user find an option. * Use **Dialog** for focused work that interrupts the page. * Use **Sheet** for side panels, details, and secondary workflows. * Use **Toast** for short feedback that does not need a response. * Use a **Dialog with an explicit confirmation action** when the user must make a consequential decision before continuing. ## Copying examples [#copying-examples] Examples are not only visual demos. They show recommended structure. When you copy an example: 1. Keep the accessibility parts, such as labels, descriptions, and triggers. 2. Replace sample text with copy that names the real task and outcome. 3. Keep disabled, loading, and error states if the workflow can reach those states. 4. Update imports to match your project. 5. Test the complete example with keyboard navigation and the assistive technologies your product supports. ## Building larger screens [#building-larger-screens] Combine components by responsibility: * Use **Field** around form controls so labels and errors stay consistent. * Use **Card** or **Frame** to group related content. * Use **Tabs** when related views share the same page context. * Use **Menu** for compact command lists. * Use **Toolbar** when controls act on the same surface. * Use **Empty** when a section has no data yet. Start with the smallest component that solves the interaction. Add more structure only when the user needs it. ## Customizing components [#customizing-components] Copied Honest UI components are meant to be edited. Common customizations include: * Changing class names. * Adjusting color variables. * Adding variants. * Replacing icons. * Renaming files or import paths. * Wrapping a component in your app-specific API. Keep behavior and accessibility intact when customizing. Style changes are usually safe. Changes to focus management, keyboard navigation, roles, labels, and portals need more care. --- ## Contributing Guide > How to make focused, useful contributions to Honest UI. Source: https://www.honestui.com/docs/contributing Markdown: https://www.honestui.com/docs/contributing.md The best contributions are focused, easy to review, and clear about the user problem they solve. ## What you can contribute [#what-you-can-contribute] You can help by: * Fixing bugs. * Improving docs. * Adding new UI components. * Adding examples. * Improving accessibility. ## Before you start [#before-you-start] Open an issue before starting a large change so maintainers can confirm the problem and scope. Small fixes, documentation edits, and example improvements can usually go straight to a pull request. Good first changes include: * Fixing unclear docs. * Adding a missing example state. * Improving labels or keyboard behavior. * Cleaning up a component prop or class name. * Fixing a bug with a small reproduction. ## Quick steps [#quick-steps] 1. Fork the repo and clone it. 2. Create a new branch. 3. Make a small, focused change. 4. Run the checks that apply to the changed files. 5. Open a pull request with a clear description. ## Commands to run before you open a PR [#commands-to-run-before-you-open-a-pr] ```bash npm run lint -- path/to/changed-file.tsx npm run build npm run build:package npm --prefix package/honestui run typecheck ``` If you changed docs, check that the docs site still builds and the changed pages render correctly. ## Write clear pull requests [#write-clear-pull-requests] Please include: * What changed. * Why you changed it. * Screenshots if UI changed. * Any breaking changes. * The checks you ran. Keep unrelated changes in separate pull requests so each change can be reviewed and verified on its own. ## Component contribution checklist [#component-contribution-checklist] When adding or changing a component, check: * The default example is useful. * Important states are documented. * The component works with keyboard navigation. * Labels, descriptions, and errors are connected where needed. * The manual installation block lists required dependencies and CSS variables. * The component source and examples use the same import style as the rest of the registry. ## Docs contribution checklist [#docs-contribution-checklist] When changing docs, use simple language. Prefer short paragraphs and specific examples. Explain when to use a component, when not to use it, and what each example demonstrates. ## Need more details? [#need-more-details] For the full technical process, file conventions, and registry details, see: [Full CONTRIBUTING.md](https://github.com/honestui/honestui/blob/main/CONTRIBUTING.md) --- ## Developer Resources > Choose the Honest UI CLI, REST API, MCP server, and agent documentation for your development task. Source: https://www.honestui.com/docs/developers Markdown: https://www.honestui.com/docs/developers.md Use this page to find the right Honest UI resource for your task. The detailed sections below document the public, read-only REST API and Model Context Protocol (MCP) server for people and tools that integrate with Honest UI. ## Choose the right starting point [#choose-the-right-starting-point] * **Add components to a React project:** follow the [installation guide](/docs/get-started) for CLI commands, project requirements, and package imports. * **Call Honest UI from code:** start with the [REST API v1 index](/api/v1), then use the [OpenAPI 3.1 specification](/openapi.json) as the complete machine-readable contract. * **Connect an AI agent:** give it [llms.txt](/llms.txt) to discover the documentation, or connect an MCP client to the [Honest UI MCP server](#honest-ui-mcp-server) for typed registry tools. * **Contribute to Honest UI:** read the [contributing guide](/docs/contributing) for the review process, project checks, and contribution guidelines. The REST API and MCP server are public and read-only. You do not need an Honest UI account or credential to use them. ## Honest UI REST API v1 [#honest-ui-rest-api-v1] The current base URL is: ```text https://www.honestui.com/api/v1 ``` | Resource | Purpose | | ----------------------------- | ---------------------------------------------------------------------------------- | | `GET /api/v1` | Discover the current version, documentation, specification, and primary resources. | | `GET /api/v1/registry` | Retrieve the public shadcn-compatible registry catalog. | | `GET /api/v1/registry/index` | List public registry item names and types. | | `GET /api/v1/registry/{name}` | Retrieve one public registry item. | | `GET /api/v1/colors/{name}` | Retrieve the registry base-color definition. | | `GET /api/v1/init` | Generate an initialization preset from documented query parameters. | The existing `/r/*` and `/init` URLs remain available for CLI and registry compatibility. New HTTP integrations should use `/api/v1` so their major API version is explicit. ## Authentication and access [#authentication-and-access] The documented API is public and read-only. Requests do not require an account and do not carry a key, token, session cookie, or other credential. Use the [Honest UI developer resource index](/developers), [agent skill](/skill.md), and [llms.txt](/llms.txt) for discovery. ## Honest UI MCP server [#honest-ui-mcp-server] Honest UI also publishes a public, read-only Model Context Protocol server at: ```text https://www.honestui.com/mcp ``` Use the Streamable HTTP transport. The server implements MCP protocol version `2026-07-28` and supports the standard `server/discover`, `tools/list`, and `tools/call` methods. It does not require a credential. Use `list_registry_items` when an agent needs valid Honest UI component names. Its optional `query` argument filters names by a case-insensitive substring. Then call `get_registry_item` with an exact returned name to retrieve the shadcn-compatible source files and dependencies. Both tools are read-only and idempotent. If `get_registry_item` reports that a name is missing, call `list_registry_items` instead of guessing another name. MCP clients can also discover the connection without manual configuration: * [AI Catalog](/.well-known/ai-catalog.json) — the domain-level discovery document that lists Honest UI MCP servers. * [MCP Server Card](/mcp/server-card) — the SEP-2127 card describing identity, transport, and supported protocol versions, served as `application/mcp-server-card+json`. ## Fair use and rate limits [#fair-use-and-rate-limits] The REST API is free and read-only, and clients are expected to keep request rates reasonable. Responses from `/api/v1` carry rate-limit headers describing the documented fair-use window: | Header | Meaning | | --------------------- | ------------------------------------------------- | | `RateLimit-Limit` | Maximum requests per client per window (600). | | `RateLimit-Remaining` | Requests left in the current window. | | `RateLimit-Reset` | Unix timestamp in seconds when the window resets. | | `RateLimit-Policy` | The published quota expression: `600;w=60`. | The window is one minute per client address. When a client exceeds it, the API responds with `429` using the standard problem-details body, code `RATE_LIMIT_EXCEEDED`, and a `Retry-After` header giving the number of seconds to wait. Enforcement is best-effort and applied per serving instance on horizontally scaled hosts, so a busy client may see `RateLimit-Remaining` reset early or never reach zero before receiving a `429`. Treat the headers as advisory pacing guidance rather than a hard guarantee, and back off whenever `Retry-After` is present. ## JSON error responses [#json-error-responses] Versioned API errors follow [RFC 9457 Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457) and use `Content-Type: application/problem+json`. Every problem includes the standard `type`, `title`, `status`, `detail`, and `instance` members plus stable `code`, `message`, and `resolution` extensions. ```json { "type": "https://www.honestui.com/docs/developers#registry-item-not-found", "title": "Registry item not found", "status": 404, "detail": "No public Honest UI registry item is named \"missing-item\".", "instance": "https://www.honestui.com/api/v1/registry/missing-item", "code": "REGISTRY_ITEM_NOT_FOUND", "message": "Registry item not found", "resolution": "GET /api/v1/registry to find a valid item name." } ``` Clients should branch on the HTTP status and `code`, not parse the human-readable `detail`, `message`, or `resolution` strings. ### Registry item not found [#registry-item-not-found] `REGISTRY_ITEM_NOT_FOUND` uses HTTP 404 when no public registry item matches the requested name. Retrieve `/api/v1/registry` before retrying with a valid name. ### Invalid preset configuration [#invalid-preset-configuration] `INVALID_PRESET_CONFIGURATION` uses HTTP 400 when an `/api/v1/init` query parameter has an unsupported value. Read `/openapi.json` for the accepted values. ### Invalid only value [#invalid-only-value] `INVALID_ONLY_VALUE` uses HTTP 400 when the `only` query parameter contains a registry subset other than `theme`, `font`, or `fonts`. ### API route not found [#api-route-not-found] `API_ROUTE_NOT_FOUND` uses HTTP 404 when the requested `/api/v1` path is not part of the published contract. Start at `/api/v1` or inspect `/openapi.json`. ### Method not allowed [#method-not-allowed] `API_METHOD_NOT_ALLOWED` uses HTTP 405 when a client sends a modifying method to a read-only resource. The response includes `Allow: GET, HEAD`. ### Rate limit exceeded [#rate-limit-exceeded] `RATE_LIMIT_EXCEEDED` uses HTTP 429 when a client exceeds the documented fair-use window. Read the `Retry-After` header, wait that many seconds, and resume at the pace published in `RateLimit-Policy`. See [Fair use and rate limits](#fair-use-and-rate-limits). ## Versioning and compatibility [#versioning-and-compatibility] Honest UI versions the REST API in the URL path. The current major version is `v1`. Additive fields and endpoints may be introduced within `v1`; clients should ignore JSON object members they do not recognize. A change that removes or renames a documented field or changes its meaning requires a new major path such as `/api/v2`. The `X-Api-Version` response header identifies the major version. The OpenAPI `info.version` field identifies the contract release within that major version. ## Deprecation policy [#deprecation-policy] No `/api/v1` endpoint is currently deprecated or scheduled for retirement. Every versioned API response links to this policy with `Link: ; rel="deprecation"; type="text/html"`. When an endpoint is scheduled for deprecation, Honest UI will add the RFC 9745 `Deprecation` header using its Structured Field date syntax and publish a migration guide with the replacement and effective date. Honest UI commits to a minimum notice period: a deprecation will be announced, and the `Deprecation` header published, at least **180 days** before the endpoint's documented retirement date. If a deprecated endpoint is scheduled to stop responding, Honest UI will also send the RFC 8594 `Sunset` header with that retirement date. A Sunset date will never be earlier than its Deprecation date, and never earlier than the end of that 180-day notice period. Until those headers and dates are published, clients should not infer a retirement date. ## Agent-readable resources [#agent-readable-resources] * [Honest UI developer resource index](/developers) * [OpenAPI specification](/openapi.json) * [MCP Streamable HTTP endpoint](/mcp) * [MCP Server Card](/mcp/server-card) * [AI Catalog](/.well-known/ai-catalog.json) * [Documentation index](/llms.txt) * [Full documentation snapshot](/llms-full.txt) * [Honest UI agent skill](/skill.md) * [Agent Skills discovery index](/.well-known/agent-skills/index.json) * [XML sitemap](/sitemap.xml) ## CLI and package resources [#cli-and-package-resources] * [Honest UI installation guide](/docs/get-started) * [Official `honestui` package on npm](https://www.npmjs.com/package/honestui) * [Honest UI source repository](https://github.com/honestui/honestui) Use the [GitHub repository](https://github.com/honestui/honestui) to report a contract or documentation problem. --- ## Examples > Complete interfaces built with Honest UI components. Source: https://www.honestui.com/docs/examples Markdown: https://www.honestui.com/docs/examples.md --- ## Get Started > Set up Honest UI, add a component, and verify the files in your project. Source: https://www.honestui.com/docs/get-started Markdown: https://www.honestui.com/docs/get-started.md Set up Honest UI, add a Button, and confirm that its source is part of your project. The CLI creates the shared configuration, copies component files, and installs the dependencies they need. ## Before you start [#before-you-start] Run this guide from the root of an existing project with: * Node.js 20.18.1 or later. * React 19. * Tailwind CSS v4. * A global stylesheet, such as `app/globals.css`. The CLI detects supported project structures and asks where shared styles and copied files should go. You can use aliases such as `@/components` and `@/lib`, but the exact aliases are your choice. ## 1. Initialize Honest UI [#1-initialize-honest-ui] Run: ```bash npx honestui@latest init ``` The CLI creates `components.json`, records your aliases and stylesheet path, and installs the base styles and dependencies used by copied components. Review the prompts before accepting them because they determine where later components are written. If `components.json` already exists, the CLI asks before replacing it. Review the current file and commit or save your work before choosing to overwrite it. ## 2. Add your first component [#2-add-your-first-component] Add Button after initialization: The CLI shows the files and packages it plans to add. To inspect the change without writing files, run: ```bash npx honestui@latest add button --dry-run ``` With the common `@/components/ui` alias, the copied source is available from `@/components/ui/button`. Your configured path may differ. ## 3. Render the component [#3-render-the-component] ```tsx import { Button } from "@/components/ui/button" export function SaveProfileButton() { return } ``` Start your application and confirm that: * The button renders with the base styles. * The focus indicator is visible when you reach it with `Tab`. * The copied component file is present at the path configured in `components.json`. You can now edit the component source and its variants as normal project code. ## Add more UI components [#add-more-ui-components] Every component page includes its CLI name. For example: Start with [Field](/docs/components/field) and the specific controls you need for forms. Use [Dialog](/docs/components/dialog) for focused work, [Table](/docs/components/table) for tabular data, and [Toast](/docs/components/toast) for brief, noncritical feedback. ## Install manually [#install-manually] Use manual installation when you want to review and place every file yourself: 1. Open the component page. 2. Select the manual installation tab. 3. Copy the component source and any supporting files. 4. Install the listed dependencies. 5. Confirm that the shared Honest UI styles are available. 6. Update imports to match your aliases. 7. Test the rendered component and its keyboard behavior. Copy the complete component structure. Removing labels, descriptions, focus handling, or primitive parts can change its behavior and accessibility. ## Package-based collections [#package-based-collections] The component CLI is not the installation path for every Honest UI collection. Charts, icons, logos, vectors, and shaders are installed from the `honestui` package and imported through dedicated entry points. | Collection | Next step | | ------------------------- | ------------------------------------------------- | | Charts | [Install charts](/docs/charts/installation) | | Icons, logos, and vectors | [Install visual assets](/docs/icons/installation) | | Shaders | [Install shaders](/docs/shaders/installation) | Read [Theme](/docs/theme/overview) before changing shared tokens, [Styling](/docs/styling) before changing copied component classes, and [Accessibility](/docs/accessibility) before changing component semantics or interaction behavior. --- ## Introduction > Understand what Honest UI provides, how the code is distributed, and where to begin. Source: https://www.honestui.com/docs Markdown: https://www.honestui.com/docs.md **Good interfaces. Honest code.** Honest UI gives you thoughtful React components, charts, icons, and visual effects that you can adapt to your product. UI components are copied into your project, while charts, icons, logos, vectors, and shaders remain package dependencies with explicit entry points. You can edit copied component source directly and choose which parts to keep. ## Choose what you need [#choose-what-you-need] Honest UI uses two distribution models. Check a feature's installation page before you begin so you know whether its source will be copied or imported from the package. | Collection | How you add it | Where it lives | | ------------------- | ------------------ | ---------------------------- | | UI components | Honest UI CLI | Copied into your project | | Product components | Honest UI CLI | Copied into your project | | Animated components | Honest UI CLI | Copied into your project | | Charts | `honestui/charts` | Installed package dependency | | Icons | `honestui/icons` | Installed package dependency | | Logos | `honestui/logos` | Installed package dependency | | Vectors | `honestui/vectors` | Installed package dependency | | Shaders | `honestui/shaders` | Installed package dependency | Copied components can be edited, renamed, or removed like any other project file. Package entry points keep larger collections available through named imports without copying every source file into your application. ## What we live by [#what-we-live-by] * **Good defaults, without giving up control.** Start with a considered implementation, then change it to fit your product. * **Clear code over clever abstractions.** The code should explain its structure and behavior to the people maintaining it. * **Useful design over unnecessary decoration.** Components and effects should support a task or communicate meaning. * **Accessibility from the start.** Native HTML and accessible primitives provide a foundation; your content and composition still matter. * **No lock-in. You own the code.** Copied component source stays in your project, and package features use explicit entry points. * **Quality over hype.** The docs state what the code does, where its limits are, and what you still need to verify. ## Start here [#start-here] Use [Get Started](/docs/get-started) to initialize an existing React project and add your first UI component. Then use the collection that matches your task: * [Components](/docs/component-guide) explains how to choose, install, and adapt interface components. * [Product components](/docs/product) combines primitives into complete application patterns such as data tables. * [Theme](/docs/theme/overview) explains the color, typography, spacing, radius, and effects shared by UI components. * [Animated components](/docs/animated) adds motion where it clarifies state, direction, or spatial relationships. * [Charts](/docs/charts) provides composable ECharts-based visualizations. * [Icons, logos, and vectors](/docs/icons) provides React SVG assets through dedicated package entry points. * [Shaders](/docs/shaders) provides progressive-enhancement effects for expressive surfaces. Each component page includes the smallest useful example, installation instructions, component anatomy, accessibility responsibilities, and examples for important states when they apply. ## Accessibility is shared work [#accessibility-is-shared-work] Honest UI provides native elements and interaction primitives where they fit. A component cannot make an entire application accessible by itself. You are still responsible for meaningful labels, logical focus order, useful validation, sufficient contrast, equivalent access to information, and testing the assembled task. Read [Accessibility](/docs/accessibility) before changing semantics, keyboard behavior, focus management, validation, or motion. ## Open source [#open-source] Honest UI is available under the MIT license. Read the [Contributing Guide](/docs/contributing) to report a problem, improve the documentation, or propose a focused change. --- ## Styling > Customize Honest UI's semantic CSS tokens, themes, and copied component classes. Source: https://www.honestui.com/docs/styling Markdown: https://www.honestui.com/docs/styling.md Honest UI separates visual decisions into CSS tokens and component classes. Tokens keep shared decisions consistent; copied component files give you local control when one component needs to change. For the complete token reference, start with [Theme](/docs/theme/overview), then open the page for [colors](/docs/theme/colors), [typography](/docs/theme/typography), [spacing](/docs/theme/spacing), [radius](/docs/theme/radius), or [effects](/docs/theme/effects). This page focuses on deciding where a customization belongs. ## Install the shared styles [#install-the-shared-styles] The `init` command installs the shared style setup and records the stylesheet path in `components.json`: ```bash npx honestui@latest init ``` Run initialization once before adding UI components. If the project is already initialized, inspect `components.json` instead of replacing it. ## How the token system works [#how-the-token-system-works] Every Honest UI token starts with `--hui-`; `hui` stands for Honest UI. Tokens are organized from low-level values to semantic roles: | Layer | Examples | Use it for | | ---------------- | ------------------------------------------------------------------------------ | --------------------------------- | | Primitive colors | `--hui-neutral-9`, `--hui-accent-10` | Building or replacing a palette | | Semantic colors | `--hui-color-background-base-primary`, `--hui-color-foreground-danger-primary` | Styling components by purpose | | Spacing | `--hui-space-3`, `--hui-space-6` | Gaps, padding, and layout rhythm | | Radius | `--hui-radius-2`, `--hui-radius-full` | Corners and pill shapes | | Typography | `--hui-font-body`, `--hui-font-size-regular` | Type families, sizes, and weights | | Effects | `--hui-shadow-floating`, `--hui-focus-ring` | Elevation and focus visibility | Components should normally use semantic tokens rather than primitive palette values. For example, a destructive action uses a danger role so the component remains meaningful when the palette changes. ## Use tokens in Tailwind [#use-tokens-in-tailwind] Copied Honest UI components pass tokens through Tailwind CSS arbitrary-value utilities. Use the same pattern in your own component code: ```tsx
``` The same classes resolve in light and dark themes because the browser reads the current variable values. Tailwind v4 also supports shorter custom-property syntax and optional semantic aliases; see [Tailwind CSS](/docs/theme/tailwind) for the complete guidance. ## Light and dark themes [#light-and-dark-themes] Theme values are scoped with `data-theme`: ```html ``` Switch the value to `dark` for the dark theme. If you use a theme library, configure it to write the same `data-theme` values. The primitive gray and accent scales resolve differently for each theme, while semantic roles keep their names. A component can therefore continue using `--hui-color-background-base-primary` without knowing which theme is active. ## Change the palette [#change-the-palette] Set theme and palette attributes on a shared ancestor: ```html ``` Change primitive palette values when you are adapting the entire system. Change semantic values when your product needs a different meaning for a role. Keep semantic token names stable when possible. Changing a token's value updates every component that uses that role; renaming it requires updating each reference. ## Customize one component [#customize-one-component] Copied UI components are normal source files. Edit their class names when a change belongs to one component: ```tsx ``` Add or adjust a variant when the same option must be reused. Do not create a new shared token for a single isolated value unless it represents a product-wide decision. ## Check every state [#check-every-state] After changing tokens or classes, verify: * Default, hover, active, focus, disabled, and invalid states. * Light and dark themes. * Text and non-text contrast. * Focus visibility in keyboard use. * Meaning without color alone. * Content at 200% and 400% zoom or equivalent reflow. * Forced-colors behavior where the component supports system colors. Use text, icons, labels, or patterns alongside color when a state must be understood. A new palette should not turn warning, success, or destructive states into color-only distinctions. --- ## Action Swap > Cycle an action's label and icon with blur, roll, or letter-cascade transitions. Source: https://www.honestui.com/docs/animated/action-swap Markdown: https://www.honestui.com/docs/animated/action-swap.md ```tsx "use client"; import { Bell, BellOff, Volume2 } from "honestui/icons"; import { ActionSwapButton } from "@/registry/default/animated/action-swap"; const items = [ { id: "every-update", label: "Every update", icon: }, { id: "priority-only", label: "Priority only", icon: }, { id: "paused", label: "Alerts paused", icon: }, ]; export function ActionSwapDemo() { return (
); } ``` ## Overview [#overview] Use Action Swap when one compact control moves through a small, predictable set of related modes. The button can animate its label, icon, or both while preserving a stable hit target. ## Installation [#installation] ## Usage [#usage] ```tsx import { ActionSwapButton } from "@/components/animated/action-swap" const items = [ { id: "digest", label: "Daily digest" }, { id: "paused", label: "Digest paused" }, ] ``` Use `ActionSwapText` and `ActionSwapIcon` directly when another control owns the interaction. The `action-swap-blur`, `action-swap-roll`, and `action-swap-cascade` entry points provide fixed-animation wrappers. ## Accessibility [#accessibility] Keep every state label specific. Icon-only items need an `ariaLabel`. Reduced-motion users receive an immediate, legible state change without the full transition. ## API Reference [#api-reference] ### `items` type: `ActionSwapItem[]` Ordered states with an `id` , visible `label` , optional `icon` , and optional `ariaLabel` . ### `value` type: `string` Controlled active item id. ### `defaultValue` type: `string` Initial item id for uncontrolled use. ### `onValueChange` void"> Runs after the active item changes. ### `animation` type: `"blur" | "roll" | "cascade"` · default: `"blur"` Transition used for the label and icon. ### `variant` type: `"primary" | "secondary" | "outline" | "ghost"` · default: `"secondary"` Visual emphasis of the button. ### `size` type: `"sm" | "md" | "lg" | "icon"` · default: `"md"` Button size. ### `cycle` type: `boolean` · default: `true` Advances to the next item when clicked. --- ## Bouncy Accordion > A connected accordion whose rows separate and settle around expanded content. Source: https://www.honestui.com/docs/animated/bouncy-accordion Markdown: https://www.honestui.com/docs/animated/bouncy-accordion.md ```tsx import { Bell, CreditCard, ShieldCheck } from "honestui/icons"; import { BouncyAccordion } from "@/registry/default/animated/bouncy-accordion"; const items = [ { id: "payment", title: "Payment method", description: "Change the card used for your team plan and future renewals.", icon: , }, { id: "security", title: "Security checks", description: "Review sign-in devices, backup codes, and account recovery.", icon: , }, { id: "email-updates", title: "Email updates", description: "Choose which team activity and weekly summaries you receive.", icon: , }, ]; export function BouncyAccordionDemo() { return ( ); } ``` ## Overview [#overview] Use Bouncy Accordion for a short list of mutually exclusive details. Opening a row separates it from its neighbors and reveals its content with coordinated layout motion. ## Installation [#installation] ## Usage [#usage] ```tsx import { BouncyAccordion } from "@/components/animated/bouncy-accordion" ``` ## Accessibility [#accessibility] Each row uses a button with `aria-expanded` and `aria-controls`; the panel is a labeled region. Disabled rows stay out of the interaction path, and reduced motion removes spring movement. ## API Reference [#api-reference] ### `items` type: `BouncyAccordionItem[]` Rows with `id` , `title` , optional `description` , `icon` , and `disabled` . ### `value` type: `string | null` Controlled open row. ### `defaultValue` type: `string | null` · default: `null` Initially open row. ### `onValueChange` void"> Runs when the open row changes. ### `collapsible` type: `boolean` · default: `true` Lets the open row close without opening another. ### `classNames` type: `BouncyAccordionClassNames` Classes for the root, item, trigger, icon, title, chevron, content, and description slots. --- ## Chromatic Text Reveal > Reveal rotating words with a moving multicolor edge. Source: https://www.honestui.com/docs/animated/chromatic-text-reveal Markdown: https://www.honestui.com/docs/animated/chromatic-text-reveal.md ```tsx import { ChromaticTextReveal } from "@/registry/default/animated/chromatic-text-reveal"; export function ChromaticTextRevealDemo() { return ( ); } ``` ## Overview [#overview] Chromatic Text Reveal keeps a sentence prefix fixed while revealing a sequence of final words. Use it for a focused product statement, not for essential instructions or rapidly changing status. ## Installation [#installation] ## Usage [#usage] ```tsx import { ChromaticTextReveal } from "@/components/animated/chromatic-text-reveal" ``` ## Accessibility [#accessibility] The current full word remains available to assistive technology while visual gradient layers are hidden. Reduced motion shows the word without the sweep. ## API Reference [#api-reference] ### `prefix` type: `string` Text that remains fixed. ### `words` type: `string[]` Words revealed in order. ### `colors` type: `string[]` Colors along the reveal edge. ### `duration` type: `number` · default: `1.2` Sweep duration in seconds. ### `pauseDuration` type: `number` · default: `0.8` Rest between words in seconds. ### `loop` type: `boolean` · default: `true` Returns to the first word. ### `startOnView` type: `boolean` · default: `true` Waits until the text enters the viewport. ### `once` type: `boolean` · default: `true` Observes only the first viewport entry. --- ## Dock > A compact action dock with a shared animated active indicator. Source: https://www.honestui.com/docs/animated/dock Markdown: https://www.honestui.com/docs/animated/dock.md ```tsx "use client"; import { Bell, House as Home, Search, Settings } from "honestui/icons"; import { useState } from "react"; import { Dock, DockItem, DockSeparator } from "@/registry/default/animated/dock"; const items = [ { id: "dashboard", label: "Dashboard", icon: Home }, { id: "browse", label: "Browse", icon: Search }, { id: "updates", label: "Updates", icon: Bell }, ]; export function DockDemo() { const [active, setActive] = useState("dashboard"); return ( {items.map(({ id, label, icon: Icon }) => ( setActive(id)} aria-label={label}> ))} setActive("preferences")} active={active === "preferences"} aria-label="Preferences"> ); } ``` ## Overview [#overview] Dock groups a small set of high-frequency destinations or actions. The active pill moves between items using shared-layout motion while item dimensions remain stable. ## Installation [#installation] ## Usage [#usage] ```tsx import { Dock, DockItem, DockSeparator } from "@/components/animated/dock" ``` ## Accessibility [#accessibility] Items with `onClick` render as buttons and expose `aria-pressed` for the active state. Icon-only items require an `aria-label`. Keep the dock to a short, recognizable action set. ## API Reference [#api-reference] ### `size` type: `number` · default: `44` Width and height of each Dock Item in pixels. ### `active` type: `boolean` Shows the shared active indicator. ### `onClick` void"> Renders the item as a button and handles activation. ### `aria-label` type: `string` Accessible name for an icon-only item. --- ## Animated Components > Copied React components that use motion to clarify state and spatial change. Source: https://www.honestui.com/docs/animated Markdown: https://www.honestui.com/docs/animated.md Animated components add movement to state changes, spatial relationships, progress, and direct manipulation. The CLI copies their source into your project so you can change the timing, styling, fallback, or remove the effect entirely. ## Use motion for a reason [#use-motion-for-a-reason] Choose an animated component when movement helps people understand what changed or where an element went. Avoid adding motion only to make a static surface feel busier. The collection includes: * State and label transitions for compact actions. * Layout motion for disclosures and active indicators. * Text and number reveals for short, deliberate moments. * Pointer-responsive effects for optional visual emphasis. * Repeating movement for nonessential ambient content. ## Accessibility responsibilities [#accessibility-responsibilities] Components document their reduced-motion behavior and preserve native semantics where they own an interactive element. The application is still responsible for meaningful labels, visible focus, keyboard operation, readable status updates, and ensuring that motion is not the only way to understand a change. Pointer-responsive effects must remain optional. Their content and actions need to work on touch devices, with a keyboard, and when reduced motion is enabled. ## Choose carefully [#choose-carefully] * Use **Action Swap** when one compact action moves through a small set of named states. * Use **Bouncy Accordion** when opening one disclosure should explain a layout change. * Use **Number Ticker** for occasional metric changes, not rapid critical updates. * Use **Text Shimmer** for a short in-progress label with a separate programmatic busy state. * Use **Magnetic** or **Tilt Card** only as progressive visual enhancement. * Use **Marquee** only for nonessential content that remains understandable when the loop stops. ## Get started [#get-started] Follow [Installation](/docs/animated/installation), then open a component page to review its purpose, reduced-motion behavior, and API before adding it. --- ## Installation > Copy an animated component into your project and verify its fallback behavior. Source: https://www.honestui.com/docs/animated/installation Markdown: https://www.honestui.com/docs/animated/installation.md Animated components use the same CLI as Honest UI components. The CLI copies the selected source and supporting utilities into your project, then installs dependencies such as Motion when required. ## Requirements [#requirements] * Node.js 20.18.1 or later. * React 19. * Tailwind CSS v4. * An initialized Honest UI project. If the project does not have `components.json`, initialize it first: ```bash npx honestui@latest init ``` ## Add a component [#add-a-component] Pass the component name to the CLI: The default animated-component alias writes source under `components/animated`. Supporting easing functions or input-capability helpers use the `lib` or `hooks` aliases in `components.json`. Review the reported files and dependencies before committing. You can edit the installed source directly. ## Dependencies [#dependencies] Most animated components use Motion for React. The CLI installs it when the selected registry item requires it. For manual installation: ```bash npm install motion ``` Components that render icons may also require the icon dependency listed in their manual installation instructions. ## Verify the result [#verify-the-result] After adding a component: 1. Render its smallest usage example. 2. Confirm the content is understandable before, during, and after the transition. 3. Test the interaction with a keyboard when it is interactive. 4. Enable reduced motion in the operating system and confirm the documented fallback. 5. Test touch behavior for pointer-responsive components. 6. Confirm that disabling animation does not remove content, status, or an action. If you change an effect, preserve or improve the fallback described on that component's page. --- ## Magnetic > Add subtle pointer attraction to a child element. Source: https://www.honestui.com/docs/animated/magnetic Markdown: https://www.honestui.com/docs/animated/magnetic.md ```tsx import { ArrowUpRight } from "honestui/icons"; import { Magnetic } from "@/registry/default/animated/magnetic"; export function MagneticDemo() { return ( ); } ``` ## Overview [#overview] Magnetic is a decorative wrapper that moves toward a fine pointer while hovered. Use it sparingly for prominent actions where the attraction supports focus without changing layout. ## Installation [#installation] ## Usage [#usage] ```tsx import { Magnetic } from "@/components/animated/magnetic" ``` ## Accessibility [#accessibility] The effect disables itself for touch input and reduced motion. The child remains responsible for semantics, focus styling, accessible naming, and keyboard behavior. ## API Reference [#api-reference] ### `children` type: `ReactNode` Element that follows the pointer. ### `strength` type: `number` · default: `0.35` Fraction of the pointer offset applied to the child. ### `className` type: `string` Class for the motion wrapper. --- ## Marquee > Loop a row or column of content with optional fade and hover pause. Source: https://www.honestui.com/docs/animated/marquee Markdown: https://www.honestui.com/docs/animated/marquee.md ```tsx import { Marquee } from "@/registry/default/animated/marquee"; const items = ["Clear by default", "Motion aware", "Easy to adapt", "Built in React", "Ready for touch"]; export function MarqueeDemo() { return ( {items.map((item) => ( {item} ))} ); } ``` ## Overview [#overview] Use Marquee for non-essential collections such as partner marks, short tags, or ambient highlights. It duplicates a static set to create a seamless CSS loop. ## Installation [#installation] ## Usage [#usage] ```tsx import { Marquee } from "@/components/animated/marquee" {items.map((item) => {item})} ``` ## Accessibility [#accessibility] The duplicate track is hidden from assistive technology, hover can pause the loop, and reduced-motion preferences stop the animation. Never place time-sensitive or uniquely actionable content only inside a marquee. ## API Reference [#api-reference] ### `direction` type: `"left" | "right" | "up" | "down"` · default: `"left"` Movement direction. ### `speed` type: `number` · default: `30` Loop duration in seconds. ### `pauseOnHover` type: `boolean` · default: `true` Pauses while the pointer is over the marquee. ### `gap` type: `string` · default: `"1rem"` Space between items and tracks. ### `fade` type: `boolean` · default: `true` Fades the leading and trailing edges. --- ## Number Ticker > Roll changing digits independently while preserving place value. Source: https://www.honestui.com/docs/animated/number-ticker Markdown: https://www.honestui.com/docs/animated/number-ticker.md ```tsx "use client"; import { useState } from "react"; import { NumberTicker } from "@/registry/default/animated/number-ticker"; export function NumberTickerDemo() { const [value, setValue] = useState(8360); return ( ); } ``` ## Overview [#overview] Number Ticker animates each numeric place without re-keying the rest of the value. Use it for metrics whose changes matter, such as balances, counts, scores, or progress totals. ## Installation [#installation] ## Usage [#usage] ```tsx import { NumberTicker } from "@/components/animated/number-ticker" ``` ## Accessibility [#accessibility] Assistive technology receives the complete formatted value instead of individual visual digits. Keep updates at a readable pace and avoid using rapid rolls for critical live data. ## API Reference [#api-reference] ### `value` type: `number` Value to display, rounded to an integer. ### `pad` type: `number` Minimum number of digits. ### `duration` type: `number` · default: `0.9` Per-digit roll duration in seconds. ### `stagger` type: `number` · default: `0.04` Delay between digit places. ### `prefix` type: `string` Text before the value. ### `suffix` type: `string` Text after the value. ### `locale` type: `boolean` Adds locale group separators. ### `blur` type: `boolean` · default: `false` Adds a small blur during rolls. --- ## Shared Layout Background > Move one hover background between related rows. Source: https://www.honestui.com/docs/animated/shared-layout-bg Markdown: https://www.honestui.com/docs/animated/shared-layout-bg.md ```tsx import { ArrowUpRight } from "honestui/icons"; import { SharedLayoutBg } from "@/registry/default/animated/shared-layout-bg"; const links = ["Guides", "Templates", "Changelog"]; export function SharedLayoutBgDemo() { return ( {links.map((label) => ( {label} ))} ); } ``` ## Overview [#overview] Shared Layout Background adds a single animated pill behind hovered children. Use it to connect a small list of related navigation or action rows without changing their semantics. ## Installation [#installation] ## Usage [#usage] ```tsx import { SharedLayoutBg } from "@/components/animated/shared-layout-bg" {links.map((link) => )} ``` ## Accessibility [#accessibility] The moving background is decorative. Children must provide their own focus indicators, labels, roles, and activation behavior. Reduced motion swaps the background without traveling between rows. ## API Reference [#api-reference] ### `children` type: `ReactNode` Rows that receive hover tracking. ### `pillClassName` type: `string` Class applied to the moving pill. ### `inset` type: `number` · default: `20` Horizontal pill inset relative to each row, in pixels. --- ## Text Reveal > Reveal text by word or character with stagger, blur, and spring motion. Source: https://www.honestui.com/docs/animated/text-reveal Markdown: https://www.honestui.com/docs/animated/text-reveal.md ```tsx import { TextReveal } from "@/registry/default/animated/text-reveal"; export function TextRevealDemo() { return ( ); } ``` ## Overview [#overview] Text Reveal introduces one or more lines in reading order. Use word splitting for headings and statements; reserve character splitting for short decorative text. ## Installation [#installation] ## Usage [#usage] ```tsx import { TextReveal } from "@/components/animated/text-reveal" ``` ## Accessibility [#accessibility] The content remains real text in document order. Reduced motion keeps a short opacity transition, and `whileInView` can defer the effect until the text is visible. ## API Reference [#api-reference] ### `text` type: `string | string[]` One line or an ordered list of lines. ### `as` type: `ElementType` · default: `"span"` Rendered element. ### `split` type: `"word" | "char"` · default: `"word"` Animation unit. ### `stagger` type: `number` · default: `0.09` Delay between units. ### `delay` type: `number` · default: `0` Delay before the first unit. ### `blur` type: `number` · default: `12` Starting blur in pixels. ### `yOffset` type: `string | number` · default: `"40%"` Starting vertical offset. ### `whileInView` type: `boolean` · default: `false` Waits for viewport entry. --- ## Text Shimmer > Sweep a theme-aware highlight through a short text label. Source: https://www.honestui.com/docs/animated/text-shimmer Markdown: https://www.honestui.com/docs/animated/text-shimmer.md ```tsx import { TextShimmer } from "@/registry/default/animated/text-shimmer"; export function TextShimmerDemo() { return Preparing your workspace…; } ``` ## Overview [#overview] Text Shimmer is a lightweight CSS effect for short, temporary states such as generation or loading labels. It inherits theme colors and does not require Motion. ## Installation [#installation] ## Usage [#usage] ```tsx import { TextShimmer } from "@/components/animated/text-shimmer" Preparing your workspace… ``` ## Accessibility [#accessibility] Use direct status text and pair ongoing work with `aria-busy` on the affected region when appropriate. The shimmer stops for reduced-motion preferences. ## API Reference [#api-reference] ### `children` type: `ReactNode` Text content. ### `as` type: `ElementType` · default: `"span"` Rendered element. ### `duration` type: `number` · default: `2.5` Sweep duration in seconds. ### `className` type: `string` Text and layout classes. --- ## Tilt Card > Add pointer-responsive perspective and optional glare to a card. Source: https://www.honestui.com/docs/animated/tilt-card Markdown: https://www.honestui.com/docs/animated/tilt-card.md ```tsx import { ArrowUpRight } from "honestui/icons"; import { TiltCard } from "@/registry/default/animated/tilt-card"; export function TiltCardDemo() { return (
Spotlight

Motion systems

A gentle layer of depth that responds as you explore the card.

); } ``` ## Overview [#overview] Tilt Card provides a restrained depth cue for visual or featured content. Its children remain normal document content while the wrapper handles perspective and glare. ## Installation [#installation] ## Usage [#usage] ```tsx import { TiltCard } from "@/components/animated/tilt-card" ``` ## Accessibility [#accessibility] Pointer tilt and glare disable themselves for touch and reduced motion. The card's content must remain understandable and operable without the effect. ## API Reference [#api-reference] ### `children` type: `ReactNode` Card content. ### `max` type: `number` · default: `12` Maximum rotation in degrees. ### `glare` type: `boolean` · default: `true` Shows a pointer-positioned highlight. ### `className` type: `string` Card surface and layout classes. --- ## Chart Config > Define labels, theme colors, and icons for each chart series. Source: https://www.honestui.com/docs/charts/chart-config Markdown: https://www.honestui.com/docs/charts/chart-config.md Every Honest UI chart accepts a `config` prop. Pass it a `ChartConfig` object that maps data keys to visible labels, theme colors, and optional icons used by tooltips and legends. ## Structure [#structure] ```tsx import { type ChartConfig } from "honestui/charts"; const chartConfig = { harvested: { label: "Harvested", icon: WheatIcon, colors: { light: ["#047857"], dark: ["#10b981"], }, }, packed: { label: "Packed", icon: PackageCheckIcon, colors: { light: ["#be123c"], dark: ["#f43f5e"], }, }, } satisfies ChartConfig; ``` Each key, such as `harvested` or `packed`, must match a data key used by the corresponding series. The object has this type: ```tsx type ChartConfig = Record< string, { label?: React.ReactNode; icon?: React.ComponentType; colors?: { light?: string[]; dark?: string[]; }; } >; ``` ## Properties [#properties] ### label [#label] The name shown in tooltips and legends. Use a concise label that makes sense outside the chart. ```tsx const chartConfig = { harvested: { label: "Harvested", // [!code highlight] // ... }, } satisfies ChartConfig; ``` ### colors [#colors] Theme-aware color arrays. Provide at least one theme key (`light` or `dark`); each holds an array of CSS color strings. Use one color per theme for a solid fill: ```tsx colors: { light: ["#047857"], dark: ["#10b981"], } ``` Use multiple colors to create a gradient. The chart distributes the stops across the rendered series: ```tsx colors: { light: ["#a855f7", "#6366f1", "#3b82f6"], dark: ["#f43f5e", "#ec4899", "#a855f7", "#6366f1", "#3b82f6"], } ``` Light and dark themes may define different numbers of color stops. The chart uses the larger count when distributing them. ### icon [#icon] An optional React component that replaces the default color indicator in the tooltip and legend. Icons can reinforce a distinction, but visible labels must still identify each series. ```tsx import { PackageCheck, Wheat } from "honestui/icons"; const chartConfig = { harvested: { label: "Harvested", icon: Wheat, // [!code highlight] [!code word:Wheat] colors: { light: ["#047857"], dark: ["#10b981"] }, }, packed: { label: "Packed", icon: PackageCheck, // [!code highlight] [!code word:PackageCheck] colors: { light: ["#be123c"], dark: ["#f43f5e"] }, }, } satisfies ChartConfig; ``` The icon replaces the color indicator in tooltips and legends. Tooltips render it at `h-2.5 w-2.5`; legends render it at `h-3 w-3`. ## How Colors Work [#how-colors-work] The chart config generates CSS custom properties scoped to each chart instance. A key `harvested` with colors `["#a855f7", "#6366f1"]` produces: ```css --color-harvested-0: #a855f7; --color-harvested-1: #6366f1; ``` Chart components, tooltips, and legends read these variables. When the application changes between the supported light and dark themes, the chart reads the corresponding values. ### Color Distribution [#color-distribution] When you provide fewer colors than segments need, they're **evenly distributed** across slots: * 2 colors for 4 slots: `[red, red, pink, pink]` * 3 colors for 4 slots: `[red, pink, blue, blue]` Use enough stops to communicate the intended progression, then verify the result with representative data in both themes. ## Runtime Validation [#runtime-validation] The config is validated at runtime. An empty `colors` object, or one without a valid theme key, throws a clear error: ```plaintext [Honest UI] Invalid chart config for "harvested": colors object must have at least one theme key (light, dark). Received empty object or invalid keys. ``` ## Examples [#examples] ### Default (Labels + Colors) [#default-labels--colors] Labels and theme-aware colors. The label shows in the tooltip and legend; the colors control the fill. ### With Icons [#with-icons] Pass an `icon` component per entry to replace the default color indicator in the tooltip and legend. ### Gradient Colors [#gradient-colors] Pass multiple colors per theme for gradient fills. Each array value is a stop distributed across the chart elements. ## API Reference [#api-reference] ### `label` type: `React.ReactNode` Name for the data series, shown in tooltips and legends. ### `colors` type: `{ light?: string[]; dark?: string[] }` Theme-aware colors. Include at least one theme key (`light` or `dark`), each mapping to an array of CSS color strings. One color is a solid fill; multiple create a gradient. ### `icon` type: `React.ComponentType` Optional React component rendered in place of the default color indicator in tooltips and legends. --- ## React Charts > Browse composable React chart components built on Apache ECharts, including line, bar, area, pie, scatter, and heatmap charts. Source: https://www.honestui.com/docs/charts Markdown: https://www.honestui.com/docs/charts.md Honest UI charts are composable React chart components built on [Apache ECharts](https://echarts.apache.org/). Install them from `honestui/charts`, then compose the axes, series, legend, tooltip, and controls your visualization needs. Charts are package imports rather than CLI-copied UI components. You can inspect their source in the Honest UI repository and use ECharts options when the component API does not expose a setting directly. ## Choose a chart [#choose-a-chart] | Chart | Use it to | Use something else when | | --------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------- | | [Area chart](/docs/charts/area-chart/static) | Show change over time when magnitude or contribution to a total matters | A filled area would make close comparisons harder | | [Bar chart](/docs/charts/bar-chart/static) | Compare values across categories | The primary question is change over a continuous sequence | | [Line chart](/docs/charts/line-chart/static) | Show trends and rate of change across an ordered dimension | The data is unordered or category comparison matters more | | [Pie or donut chart](/docs/charts/pie-chart/static) | Show a simple part-to-whole relationship with a few categories | Values are close, there are many categories, or precision matters | | [Scatter chart](/docs/charts/scatter-chart/static) | Find relationships, clusters, and outliers across numeric measures | One axis is categorical | | [Heatmap](/docs/charts/heatmap/static) | Find patterns across two categorical dimensions | Readers need frequent point-by-point comparison | ## How composition works [#how-composition-works] The chart root owns the data, theme configuration, loading state, and selection. Add only the parts you need. For example: ```tsx ``` The child components describe ECharts configuration; they do not render separate DOM elements. Omitting a child removes that chart part. Use [Chart Config](/docs/charts/chart-config) to connect data keys to visible labels, colors, and optional icons. ## Canvas and performance [#canvas-and-performance] ECharts renders the plot to canvas, avoiding one DOM element for every bar, point, or cell. That can reduce DOM size for dense visualizations, but performance still depends on the dataset, enabled interactions, animation, device, and surrounding application. Test representative data on the devices your product supports. ## Accessibility responsibilities [#accessibility-responsibilities] A canvas can have an accessible name, but that name does not expose every plotted value or relationship. Provide a nearby summary or data table when people need to understand exact values, trends, outliers, or selections. Tooltips, brushes, and direct mark selection are pointer-operated unless your application supplies an equivalent control. Do not place required information or the only path to an action inside a canvas interaction. ## Get started [#get-started] Follow [Installation](/docs/charts/installation), define labels and colors in [Chart Config](/docs/charts/chart-config), and choose a chart based on the question the data must answer. --- ## Installation > Install Honest UI charts and render a verified first chart. Source: https://www.honestui.com/docs/charts/installation Markdown: https://www.honestui.com/docs/charts/installation.md Honest UI charts are available from the `honestui/charts` package entry point. Unlike CLI-installed UI components, charts remain an `honestui` package dependency. ## Requirements [#requirements] * Node.js 20.18.1 or later. * React 19. * A container with an explicit height or aspect ratio. ## Install [#install] ## Import [#import] Import chart roots, shared types, and chart-specific data types from the same entry point. ```tsx import { BarChart, type ChartConfig } from "honestui/charts" import "honestui/charts.css" ``` Load `honestui/charts.css` once in your application root. It contains only the utilities used by the chart package, so chart layout, loading states, tooltips, and keyboard-operable legends do not depend on your Tailwind content paths. ## Render a chart [#render-a-chart] ```tsx const data = [ { month: "Jan", completed: 18 }, { month: "Feb", completed: 24 }, ] const chartConfig = { completed: { label: "Completed tasks", colors: { light: ["#2563eb"], dark: ["#60a5fa"] }, }, } satisfies ChartConfig export function CompletedTasksChart() { return ( ) } ``` Confirm that the chart has a visible height and that each `dataKey` exists in both the data rows and `chartConfig`. Add a visible summary or table if readers need the plotted values without pointer interaction. ## Next steps [#next-steps] Choose a chart based on the comparison you need to show, then use [Chart Config](/docs/charts/chart-config) to define shared labels, colors, and icons. --- ## Accordion > Show related sections that people can expand and collapse as needed. Source: https://www.honestui.com/docs/components/accordion Markdown: https://www.honestui.com/docs/components/accordion.md ```tsx import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, } from "@/components/honest-ui/ui/accordion" const items = [ { id: "1", title: "What is Base UI?", content: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { id: "2", title: "How do I get started?", content: "Head to the \"Quick start\" guide in the docs. If you've used unstyled libraries before, you'll feel at home.", }, { id: "3", title: "Can I use it for my project?", content: "Of course! Base UI is free and open source.", }, ] export function AccordionDemo() { return ( {items.map((item) => ( {item.title} {item.content} ))} ) } ``` ## Overview [#overview] Use an accordion when the page has several peer sections that people scan by heading first and read selectively: FAQs, settings groups, filter facets, detail panels, and optional sections of long forms. Opening a panel keeps its neighbors visible, so the accordion works best when the headings alone tell people whether a section matters to them. Avoid an accordion when people must compare contents side by side, such as pricing plans or spec sheets, and never put required steps or the only explanation of an error inside a closed panel. If everything should stay visible, use plain headings and spacing instead. See [Don't do this](#dont-do-this) for the failure modes. ## Anatomy [#anatomy] An accordion is made from a root, one or more items, a header-wrapped trigger for each item, and a panel for each item. The trigger names the section as a question or noun phrase; the built-in chevron rotates when the panel opens. The panel content stays short enough that opening one item does not push the rest of the page out of view. Each trigger is a real button with `aria-expanded`, so assistive technology announces both the section name and its open state. The panel is connected to its trigger through ids managed by the component. ## Behavior [#behavior] **Single versus multiple.** Pass `type="single"` when only one panel should be open at a time, and `type="multiple"` when several panels can stay open. In single mode the Honest UI wrapper accepts plain strings for `value`, `defaultValue`, and `onValueChange`, so you never handle arrays by hand. **Collapsible.** By default a single accordion keeps one panel open: collapsing the last open panel is cancelled. Pass `collapsible` when all panels may be closed at once. Multiple accordions are always collapsible. **Controlled state.** Use `value` and `onValueChange` when the open panels need to sync with routing, saved preferences, or another part of the page. See the [controlled example](#controlled-accordion). **Disabled items.** A disabled item's trigger stays reachable by keyboard focus but cannot be activated, and it is dimmed. Explain nearby why the section is locked when the reason is not obvious. ## Accessibility [#accessibility] Every trigger is a real `

Open items: {value.length > 0 ? value.join(", ") : "None"}

) } ``` ### Disabled items [#disabled-items] The locked trigger stays visible and focusable but dimmed, so people can see that premium content exists even though it cannot be opened. ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/honest-ui/ui/accordion" const items = [ { value: "item-1", trigger: "Can I access my account history?", content: "Yes, you can view your complete account history including all transactions, plan changes, and support tickets in the Account History section of your dashboard.", disabled: false, }, { value: "item-2", trigger: "Premium feature information (Locked)", content: "This section contains information about premium features. Upgrade your plan to access this content.", disabled: true, }, { value: "item-3", trigger: "How do I update my email address?", content: "You can update your email address in your account settings. You'll receive a verification email at your new address to confirm the change.", disabled: false, }, ] export function AccordionDisabled() { return (
{items.map((item) => ( {item.trigger} {item.content} ))}
) } ``` ### Inside a Card [#inside-a-card] An accordion pairs well with a card header that frames the topic and a description that sets expectations. ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/honest-ui/ui/accordion"; import { Button } from "@/components/honest-ui/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/honest-ui/ui/card"; import { ArrowUpRight as ArrowUpRightIcon } from "honestui/icons"; const items = [ { value: "installation", trigger: "How do I add a component?", content: ( <>

Start with the installation guide, then add only the components your project uses. The copied source stays in your repository.

), }, { value: "ownership", trigger: "Where does the code live?", content: ( <>

Component source is copied into your project. You can inspect, edit, test, and remove it without relying on a hosted runtime.

), }, { value: "security", trigger: "Does Honest UI secure my application?", content: ( <>

Honest UI components are source code, so your application keeps responsibility for authentication, authorization, storage, and data handling.

Review dependencies and application behavior against your own threat model before shipping.

), }, ]; export function AccordionInCard() { return (
Honest UI basics Common questions about installing and owning the component source {items.map((item) => ( {item.trigger} {item.content} ))}
); } import Link from "next/link"; ``` ### Leading icon [#leading-icon] Swap the trailing chevron for a leading indicator that rotates from right to down as panels open. ```tsx import { ChevronRight as ChevronRightIcon } from "honestui/icons" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/honest-ui/ui/accordion" const items = [ { value: "item-1", trigger: "Can I use this for my project?", content: "Yes, you can use Honest UI for any of your personal or commercial projects. The library is distributed under the MIT license.", }, { value: "item-2", trigger: "Is there a Figma file available?", content: "We are currently working on a comprehensive Figma design system that will be released soon to all Honest UI users.", }, { value: "item-3", trigger: "How do I contribute to Honest UI?", content: "You can contribute by reporting bugs, suggesting features, or submitting pull requests on our GitHub repository.", }, ] export function AccordionLeadingIcon() { return (
{items.map((item) => ( {item.trigger} {item.content} ))}
) } ``` ### Product FAQ [#product-faq] Short question-and-answer pairs where the headings do the scanning work. ```tsx import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, } from "@/components/honest-ui/ui/accordion" const questions = [ ["Plan limits", "Seats, projects, and storage can be adjusted from billing settings."], ["Exports", "Workspace admins can export reports as CSV at any time."], ["Support", "Priority support is included on every team plan."], ] export function AccordionProductFaq() { return ( {questions.map(([title, content]) => ( {title} {content} ))} ) } ``` ## API reference [#api-reference] The Honest UI wrapper normalizes single and multiple selection on top of Base UI's Accordion Root. Parts forward their matching Base UI props. | Prop | Values | Default | | --------------- | --------------------------------------- | -------- | | `type` | `"single"`, `"multiple"` | multiple | | `collapsible` | boolean | `false` | | `value` | string (single) or string\[] (multiple) | — | | `defaultValue` | string (single) or string\[] (multiple) | — | | `onValueChange` | `(value, eventDetails) => void` | — | With `type="single"`, `value`, `defaultValue`, and the `onValueChange` argument are plain strings, and collapsing the last open panel is cancelled unless `collapsible` is set. With `type="multiple"`, values are arrays and panels toggle freely. Base UI props such as `disabled`, `orientation`, `loopFocus`, `keepMounted`, and `hiddenUntilFound` pass through to the root; `hiddenUntilFound` lets browser page search expand a closed panel when it finds matching text inside. `AccordionItem` accepts `disabled` and `open` state attributes. `AccordionContent` is an alias of `AccordionPanel`. See the [Base UI Accordion API](https://base-ui.com/react/components/accordion#api-reference). --- ## Alert Dialog > Interrupt work with a modal decision that must be answered before anything else can continue. Source: https://www.honestui.com/docs/components/alert-dialog Markdown: https://www.honestui.com/docs/components/alert-dialog.md ```tsx "use client" import * as React from "react" import { Alert, AlertDescription, AlertDialog, AlertDialogBody, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger, AlertTitle, } from "@/components/honest-ui/ui/alert" import { Button } from "@/components/honest-ui/ui/button" export function AlertDialogDemo() { const [deleted, setDeleted] = React.useState(false) const resultRef = React.useRef(null) React.useEffect(() => { if (deleted) resultRef.current?.focus() }, [deleted]) if (deleted) { return ( Workspace deleted The Design workspace and its sample data were removed. ) } return (

Delete workspace

Permanently remove the Design workspace and its data.

}> Delete Design workspace Delete Design workspace? This permanently deletes the workspace and its sample data. This action cannot be undone. }> Keep workspace setDeleted(true)} /> } > Delete workspace
) } ``` ## Overview [#overview] Use an Alert Dialog when the person must make an explicit decision before the application can continue: deleting data, discarding unsaved changes, acknowledging a blocking error. The popup takes over the screen, and every path back to the task runs through the choice it presents. Alert Dialog is deliberately more restrictive than [Dialog](/docs/components/dialog). A Dialog is a focused workspace — forms, detail views, short tasks. An Alert Dialog is a checkpoint — a question with consequences. If either button can be clicked without real consequence, or if the content needs inputs, scrolling, or several steps, use a Dialog instead. ## Anatomy [#anatomy] An alert dialog has a trigger, popup, header, title, description, footer, body, and close actions. The title states the decision as a question naming the affected object ("Delete Design workspace?"). The description spells out what is lost and whether it can be undone. The footer holds exactly two kinds of action: the safe choice and the risky one, visually distinguished so they cannot be confused at a glance. The parts live in `alert.tsx` (re-exported by `alert-dialog.tsx`) because inline Alerts and the modal family share their status tokens. ## Differences from Dialog [#differences-from-dialog] Under the hood, Alert Dialog is Base UI's Dialog with the escape hatches removed. Three differences follow from that, each verified in Base UI's source: * **Always modal.** The regular Dialog accepts a `modal` prop; Alert Dialog does not. Focus is always trapped inside the popup and the page behind it is inert. * **Outside clicks do not dismiss.** A regular Dialog closes when you click its backdrop. Alert Dialog hard-codes pointer dismissal off, so clicking outside does nothing — the decision cannot be escaped by accident. * **Announced as a decision.** The popup renders `role="alertdialog"` rather than `role="dialog"`, so assistive technology introduces it as something requiring a response. Escape still works: closing via keyboard remains available even though pointer dismissal is disabled, so the dialog stays operable without a mouse. ## Behavior [#behavior] **Opening.** Focus moves into the popup, the backdrop dims the page, and scrolling locks. Place initial focus on the safe action rather than the destructive one using `initialFocus` — see the [Dialog guidance](/docs/components/dialog#behavior), which applies unchanged here. **While open.** Tab and Shift+Tab cycle between the two actions only. There is nothing else to explore; that is the point. Escape activates the safe outcome of leaving without deciding. **Deciding.** Both buttons close the dialog through `AlertDialogClose`. Wire the consequence to the risky button's own handler. After a destructive action completes, show the new state on the page — a success Alert where the deleted object used to be — so the result is visible without another announcement channel. Keep these dialogs rare. A product that interrupts constantly teaches people to confirm without reading, which defeats the protection on the one occasion it matters. ## Accessibility [#accessibility] Always provide `AlertDialogTitle`; it names the popup for assistive technology. Add `AlertDialogDescription` when consequences need spelling out — for destructive actions, always spell them out. Make the two choices unmistakable in words as well as color: "Delete workspace" beats "Confirm", and "Keep workspace" beats "Cancel" when cancel could be misread. Style the risky action with the destructive variant and start focus on the safe one, so Enter pressed in haste does the least harm. Because pointer dismissal is off, people who click outside get no accidental outcomes; the only exits are the labeled buttons and Escape. Focus returns to the trigger when the popup closes. Colors come from theme tokens, so the backdrop dim and popup surface adapt to dark mode automatically. Titles wrap within the popup instead of overflowing at 200% zoom, and the footer stacks its buttons full-width on small screens so both remain reachable with a thumb. ## Installation [#installation] Install the following dependencies: ```bash npm install @base-ui-components/react ``` Copy and paste the following code into your project. ### components/ui/alert-dialog.tsx ```tsx "use client" export { AlertDialog, AlertDialogPortal, AlertDialogBackdrop, AlertDialogBackdrop as AlertDialogOverlay, AlertDialogTrigger, AlertDialogPopup, AlertDialogPopup as AlertDialogContent, AlertDialogHeader, AlertDialogBody, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogClose, } from "@/components/honest-ui/ui/alert" ``` Update the import paths to match your project setup. ## Usage [#usage] ```tsx import { AlertDialog, AlertDialogBody, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; ``` ```tsx Delete workspace Delete Design workspace? This permanently deletes the workspace and its sample data. This action cannot be undone. }> Keep workspace }> Delete workspace ``` Both footer buttons use `AlertDialogClose`, which guarantees the popup closes whichever way the decision goes; attach side effects to the rendered button's handler. ## Don't do this [#dont-do-this] ### Focus landing on the destructive button [#focus-landing-on-the-destructive-button] ```tsx // Bad {/* ... */} Cancel }> Delete everything ``` ```tsx // Good }> Keep workspace }> Delete workspace ``` Without direction, focus lands on the first tabbable element — often the destructive action itself. One reflexive Enter confirms an irreversible loss. Start focus on the safe action (`initialFocus={safeRef}` on the popup) so the destructive path requires deliberate movement toward it. ### Using Alert Dialog for information [#using-alert-dialog-for-information] ```tsx // Bad Export complete Your file finished exporting. OK ``` ```tsx // Good toastManager.add({ type: "success", title: "Export complete" }); ``` A modal that announces good news and offers exactly one button steals attention and gives nothing to decide. Match the component to the interaction shape: completion notices are Toasts, conditions needing visibility are inline Alerts, and only genuine blocking decisions earn an Alert Dialog. ### Vague labels on irreversible actions [#vague-labels-on-irreversible-actions] ```tsx // Bad No }>OK ``` ```tsx // Good Keep workspace }> Delete workspace ``` "OK" on a red button answers nothing: OK what? Generic pairs like No/OK force people to reread the description under stress and invite wrong-order clicks. Name both buttons after what they do to the affected object. ## Examples [#examples] ### Destructive confirmation [#destructive-confirmation] The full pattern in context: settings page, confirmation naming the object, then a visible success state replacing what was deleted. ```tsx "use client" import * as React from "react" import { Alert, AlertDescription, AlertDialog, AlertDialogBody, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, AlertDialogTrigger, AlertTitle, } from "@/components/honest-ui/ui/alert" import { Button } from "@/components/honest-ui/ui/button" export function AlertDialogDemo() { const [deleted, setDeleted] = React.useState(false) const resultRef = React.useRef(null) React.useEffect(() => { if (deleted) resultRef.current?.focus() }, [deleted]) if (deleted) { return ( Workspace deleted The Design workspace and its sample data were removed. ) } return (

Delete workspace

Permanently remove the Design workspace and its data.

}> Delete Design workspace Delete Design workspace? This permanently deletes the workspace and its sample data. This action cannot be undone. }> Keep workspace setDeleted(true)} /> } > Delete workspace
) } ``` ### Close confirmation [#close-confirmation] When closing would discard work, interrupt a process, or hide important state, ask before continuing. ```tsx "use client" import * as React from "react" import { AlertDialog, AlertDialogBody, AlertDialogClose, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPopup, AlertDialogTitle, } from "@/components/honest-ui/ui/alert" import { Button } from "@/components/honest-ui/ui/button" import { Dialog, DialogBody, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPopup, DialogTitle, DialogTrigger, } from "@/components/honest-ui/ui/dialog" import { Field } from "@/components/honest-ui/ui/field" import { Form } from "@/components/honest-ui/ui/form" import { Textarea } from "@/components/honest-ui/ui/textarea" export function DialogCloseConfirmationDemo() { const [dialogOpen, setDialogOpen] = React.useState(false) const [confirmOpen, setConfirmOpen] = React.useState(false) const [value, setValue] = React.useState("") return ( { if (!o && value) { setConfirmOpen(true) } else { setDialogOpen(o) } }} > }> Compose New message Type something and try closing.
{ event.preventDefault() // Close the dialog when submitting setDialogOpen(false) }} >