Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/yogendrarana/craftdotui/llms.txt

Use this file to discover all available pages before exploring further.

Craft UI is built on top of Base UI from React, which provides comprehensive accessibility features out of the box. All components follow WAI-ARIA guidelines and include full keyboard navigation support.

Foundation: Base UI

Craft UI leverages @base-ui/react as its foundation, which means every component comes with:
  • Full keyboard navigation
  • Proper ARIA attributes
  • Screen reader support
  • Focus management
  • Semantic HTML structure
You don’t need to add accessibility features manually - they’re built in from the start.

Keyboard Navigation

All interactive components support standard keyboard navigation patterns:
import { Button } from "@craftdotui/baseui/components/button";

<Button>Click Me</Button>
// ✓ Focusable with Tab
// ✓ Activates with Enter or Space
// ✓ Shows focus indicator

Dialogs

import {
  Dialog,
  DialogTrigger,
  DialogPortal,
  DialogBackdrop,
  DialogViewport,
  DialogPopup,
} from "@craftdotui/baseui/components/dialog";

<Dialog>
  <DialogTrigger render={(props) => <Button {...props}>Open</Button>} />
  <DialogPortal>
    <DialogBackdrop />
    <DialogViewport>
      <DialogPopup>{/* Content */}</DialogPopup>
    </DialogViewport>
  </DialogPortal>
</Dialog>
// ✓ Opens with Enter/Space on trigger
// ✓ Closes with Escape key
// ✓ Traps focus inside dialog
// ✓ Returns focus to trigger on close

Tooltips

import {
  Tooltip,
  TooltipTrigger,
  TooltipPortal,
  TooltipPositioner,
  TooltipPopup,
} from "@craftdotui/baseui/components/tooltip";

<Tooltip>
  <TooltipTrigger>Hover or Focus</TooltipTrigger>
  <TooltipPortal>
    <TooltipPositioner>
      <TooltipPopup>Helpful information</TooltipPopup>
    </TooltipPositioner>
  </TooltipPortal>
</Tooltip>
// ✓ Shows on hover and focus
// ✓ Dismisses with Escape
// ✓ Properly announced by screen readers

Form Controls

import { Checkbox, CheckboxIndicator } from "@craftdotui/baseui/components/checkbox";

<Checkbox>
  <CheckboxIndicator />
</Checkbox>
// ✓ Toggles with Space
// ✓ Proper checked/unchecked states
// ✓ Supports indeterminate state

ARIA Attributes

Craft UI components automatically include appropriate ARIA attributes:

Dialog Component

The Dialog component uses proper ARIA roles and attributes:
// Automatically applied attributes:
// role="dialog"
// aria-labelledby="dialog-title"
// aria-describedby="dialog-description"
// aria-modal="true"

<Dialog>
  <DialogTrigger render={(props) => <Button {...props}>Open</Button>} />
  <DialogPortal>
    <DialogBackdrop />
    <DialogViewport>
      <DialogPopup>
        <DialogTitle>Title</DialogTitle> {/* Linked via aria-labelledby */}
        <DialogDescription>Description</DialogDescription> {/* Linked via aria-describedby */}
      </DialogPopup>
    </DialogViewport>
  </DialogPortal>
</Dialog>

Checkbox Component

// Automatically applied:
// role="checkbox"
// aria-checked="true" | "false" | "mixed"
// tabindex="0"

<Checkbox checked={true}>
  <CheckboxIndicator />
</Checkbox>

Button States

Buttons automatically handle loading and disabled states:
<Button loading={true}>
  Submit
</Button>
// Sets: aria-busy="true"
// Sets: disabled={true}

<Button disabled={true}>
  Disabled
</Button>
// Sets: aria-disabled="true"
// Prevents interaction

Focus Management

Craft UI components handle focus properly in all scenarios:

Focus Indicators

All interactive components have visible focus indicators:
// Button focus styles (from button/index.tsx:18)
<Button className="focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
  Visible Focus Ring
</Button>

Focus Trapping

Modal components like Dialog automatically trap focus:
1

Dialog Opens

Focus moves to the first focusable element inside the dialog.
2

Tab Navigation

Tab cycles through focusable elements within the dialog only.
3

Dialog Closes

Focus returns to the trigger element that opened the dialog.

Programmatic Focus

You can manually manage focus when needed:
import { useRef } from "react";
import { Input } from "@craftdotui/baseui/components/input";
import { Button } from "@craftdotui/baseui/components/button";

function SearchForm() {
  const inputRef = useRef<HTMLInputElement>(null);

  return (
    <div>
      <Input ref={inputRef} type="search" />
      <Button onClick={() => inputRef.current?.focus()}>
        Focus Search
      </Button>
    </div>
  );
}

Screen Reader Support

Components provide meaningful information to screen readers:

Semantic HTML

All components use semantic HTML elements:
// Button uses <button> element
<Button>Click</Button>

// Input uses <input> element with proper type
<Input type="email" />

// Checkbox uses proper checkbox role
<Checkbox>
  <CheckboxIndicator />
</Checkbox>

Labels and Descriptions

Always provide labels for form controls:
import { Field } from "@craftdotui/baseui/components/field";
import { Input } from "@craftdotui/baseui/components/input";

<Field>
  <label htmlFor="email">Email Address</label>
  <Input id="email" type="email" />
  <p className="text-sm text-muted-foreground">We'll never share your email.</p>
</Field>

Accessible Icon Buttons

When using icon-only buttons, provide accessible labels:
<Button size="icon" aria-label="Close dialog">
  <svg>...</svg>
</Button>

// Or use visually hidden text
<Button size="icon">
  <svg aria-hidden="true">...</svg>
  <span className="sr-only">Close dialog</span>
</Button>

Best Practices

Color Contrast

Craft UI’s default theme meets WCAG AA standards for color contrast. When customizing colors, ensure you maintain at least a 4.5:1 contrast ratio for text.
// Good: Uses semantic color tokens with proper contrast
<Button variant="default">Submit</Button>

// Check contrast when using custom colors
<Button className="bg-[oklch(0.5_0.2_280)] text-white">
  Custom Color
</Button>

Text Alternatives

Always provide text alternatives for non-text content:
// Images
<img src="/logo.png" alt="Company Logo" />

// Decorative images (empty alt)
<img src="/decoration.png" alt="" aria-hidden="true" />

// SVG icons
<svg aria-labelledby="icon-title">
  <title id="icon-title">Settings Icon</title>
  <path d="..." />
</svg>

Form Validation

Provide clear, accessible error messages:
import { Field } from "@craftdotui/baseui/components/field";
import { Input } from "@craftdotui/baseui/components/input";
import { useState } from "react";

function EmailField() {
  const [error, setError] = useState("");

  return (
    <Field>
      <label htmlFor="email">Email</label>
      <Input
        id="email"
        type="email"
        aria-invalid={!!error}
        aria-describedby={error ? "email-error" : undefined}
      />
      {error && (
        <span id="email-error" className="text-destructive text-sm" role="alert">
          {error}
        </span>
      )}
    </Field>
  );
}

Loading States

Indicate loading states clearly:
<Button loading={true}>
  Saving...
</Button>
// Automatically sets aria-busy="true"
// Shows loading spinner
// Disables interaction

Responsive Design

Ensure components work at different zoom levels and viewport sizes:
// Use relative units
<Button className="text-base px-4 py-2">
  Scales with user preferences
</Button>

// Avoid fixed pixel heights for text containers
<div className="min-h-[3rem]" /* instead of h-12 */>
  Content
</div>

Testing Accessibility

Keyboard Testing

1

Tab Through Interface

Press Tab to navigate through all interactive elements. Ensure:
  • All interactive elements are reachable
  • Focus order is logical
  • Focus indicators are visible
2

Test Keyboard Actions

  • Buttons activate with Enter or Space
  • Dialogs close with Escape
  • Dropdowns open with Arrow keys
  • Forms submit with Enter
3

Check Focus Trapping

In dialogs and modals:
  • Focus stays within the modal
  • Tab cycles through modal elements
  • Focus returns to trigger on close

Screen Reader Testing

Test with popular screen readers:
  • NVDA (Windows, free)
  • JAWS (Windows, commercial)
  • VoiceOver (macOS/iOS, built-in)
  • TalkBack (Android, built-in)
Each component in Craft UI is built on Base UI’s accessible primitives, which are thoroughly tested with screen readers.

Automated Testing

Use tools to catch common accessibility issues:
# Install axe-core for accessibility testing
npm install --save-dev @axe-core/react
import React from 'react';

if (process.env.NODE_ENV !== 'production') {
  import('@axe-core/react').then((axe) => {
    axe.default(React, ReactDOM, 1000);
  });
}

Common Patterns

Accessible Form

import { Field, Fieldset } from "@craftdotui/baseui/components/field";
import { Input } from "@craftdotui/baseui/components/input";
import { Button } from "@craftdotui/baseui/components/button";
import { Checkbox, CheckboxIndicator } from "@craftdotui/baseui/components/checkbox";

function AccessibleForm() {
  return (
    <form onSubmit={(e) => e.preventDefault()}>
      <Fieldset>
        <legend className="text-lg font-semibold mb-4">Contact Information</legend>
        
        <Field className="mb-4">
          <label htmlFor="name">Full Name</label>
          <Input id="name" type="text" required />
        </Field>

        <Field className="mb-4">
          <label htmlFor="email">Email Address</label>
          <Input id="email" type="email" required />
          <p className="text-sm text-muted-foreground">We'll never share your email.</p>
        </Field>

        <Field className="mb-4">
          <label className="flex items-center gap-2">
            <Checkbox>
              <CheckboxIndicator />
            </Checkbox>
            I agree to the terms and conditions
          </label>
        </Field>

        <Button type="submit">Submit</Button>
      </Fieldset>
    </form>
  );
}

Accessible Navigation

import { Button } from "@craftdotui/baseui/components/button";

function Navigation() {
  return (
    <nav aria-label="Main navigation">
      <ul className="flex gap-4">
        <li>
          <Button
            variant="ghost"
            render={(props) => <a href="/" {...props} />}
          >
            Home
          </Button>
        </li>
        <li>
          <Button
            variant="ghost"
            render={(props) => <a href="/about" {...props} />}
          >
            About
          </Button>
        </li>
        <li>
          <Button
            variant="ghost"
            render={(props) => <a href="/contact" {...props} />}
            aria-current="page"
          >
            Contact
          </Button>
        </li>
      </ul>
    </nav>
  );
}

Resources

Next Steps

  • Theming - Ensure your custom theme maintains accessibility
  • Customization - Preserve accessibility when customizing components