Components Reference
React component library for EmberDocs documentation sites.
ThemeToggle Component
Toggle between light and dark mode themes. Supports three themes: dark (default), light, and monochrome.
Usage
typescript
import { ThemeToggle } from '@/components/ThemeToggle';
export function Header() {
return (
<header>
<ThemeToggle />
</header>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| None | - | - | Component takes no props |
Behavior
- Respects server-side theme set via
EMBERDOCS_THEMEenvironment variable - If monochrome theme is set via environment, toggle is hidden
- Automatically detects system theme preference on first load (if no server theme)
- Persists user selection to localStorage with key "theme"
- Updates
data-themeattribute on document root - Toggles between light and dark modes (monochrome is environment-only)
Notes
- The component is hidden if
EMBERDOCS_THEMEis set tomonochrome - Theme toggle only switches between light and dark (monochrome requires environment variable)
- Component prevents hydration mismatches by not rendering until mounted
Breadcrumbs Component
Display navigation breadcrumb trail showing document hierarchy.
Usage
typescript
import { Breadcrumbs } from '@/components/Breadcrumbs';
export function DocPage() {
const breadcrumbs = [
{ href: '/docs', label: 'Docs' },
{ href: '/docs/guides', label: 'Guides' },
{ href: '/docs/guides/advanced', label: 'Advanced' },
];
return <Breadcrumbs items={breadcrumbs} />;
}Props
typescript
interface BreadcrumbsProps {
items: BreadcrumbItem[];
}
interface BreadcrumbItem {
href: string;
label: string;
}Styling
Uses CSS custom properties:
--textfor active (last) breadcrumb--mutedfor inactive breadcrumbs--accentfor hover state on links
TableOfContents Component
Sticky table of contents with active heading highlighting.
Usage
typescript
import { TableOfContents } from '@/components/TableOfContents';
const toc = [
{ slug: 'overview', title: 'Overview', level: 2 },
{ slug: 'installation', title: 'Installation', level: 2 },
{ slug: 'setup', title: 'Setup', level: 3 },
];
export function Layout() {
return <TableOfContents toc={toc} />;
}Props
typescript
interface TableOfContentsProps {
toc: TocEntry[];
}
interface TocEntry {
slug: string;
title: string;
level: number; // 1-6 for H1-H6
}Features
- Uses IntersectionObserver for active heading detection
- Smooth scroll to heading on click
- Visual hierarchy via nested indentation
- Sticky positioning (top: 80px offset for header)
- Automatic active state highlighting
CodeBlock Component
Syntax-highlighted code blocks with copy functionality.
Usage
typescript
import { CodeBlock } from '@/components/CodeBlock';
export function Example() {
return (
<CodeBlock
code="const greeting = 'Hello, World!'"
language="typescript"
highlightedHtml={/* pre-highlighted HTML */}
/>
);
}Props
typescript
interface CodeBlockProps {
code: string; // Original code text
language: string; // Language identifier (typescript, bash, etc)
highlightedHtml?: string; // Optional pre-highlighted HTML (not currently used)
}Features
- Displays language label in top-left
- Copy button with visual feedback
- Horizontal scroll for long lines
- Proper code formatting with monospace font
- Dark mode syntax highlighting compatible
SearchPalette Component
Modal search interface for documentation search.
Usage
typescript
import { SearchPalette } from '@/components/SearchPalette';
import { Header } from '@/components/Header';
import { useCallback, useState } from 'react';
export function Layout() {
const [isOpen, setIsOpen] = useState(false);
const openSearch = useCallback(() => setIsOpen(true), []);
const closeSearch = useCallback(() => setIsOpen(false), []);
const toggleSearch = useCallback(() => setIsOpen((prev) => !prev), []);
return (
<>
<Header onSearchClick={openSearch} />
{/* Page content */}
<SearchPalette isOpen={isOpen} onClose={closeSearch} onToggle={toggleSearch} />
</>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | - | Whether the search palette is visible |
onClose | () => void | - | Called when the palette should close (Escape, backdrop click, close button) |
onToggle | () => void | - | Called on Cmd+K / Ctrl+K to toggle open/close |
Features
- Opens via header search button (when integrated) and responds to Cmd+K (macOS) or Ctrl+K (Windows/Linux)
- Navigate results with arrow keys
- Select with Enter key
- Close with Escape key
- Real-time search results from pre-built index
- Result highlighting with visual feedback
- Keyboard shortcuts hint display
Keyboard Shortcuts
| Key | Action |
|---|---|
Cmd+K / Ctrl+K | Toggle search (calls onToggle) |
↑ / ↓ | Navigate results |
Enter | Select highlighted result |
Escape | Close search |
Custom Component Example
Creating custom components for your documentation:
typescript
interface AlertProps {
type: 'info' | 'warning' | 'error';
children: React.ReactNode;
}
export function Alert({ type, children }: AlertProps) {
const styles = {
info: 'bg-blue-100 text-blue-900 border-blue-300',
warning: 'bg-yellow-100 text-yellow-900 border-yellow-300',
error: 'bg-red-100 text-red-900 border-red-300',
};
return (
<div className={`p-4 rounded border ${styles[type]}`}>
{children}
</div>
);
}Styling Guidelines
All components use CSS custom properties for theming:
css
--bg: Background color
--text: Text color
--muted: Muted/secondary text
--accent: Primary accent color
--border: Border color
--surface: Secondary backgroundThis allows seamless dark/light mode switching without component modifications.
Testing Components
Unit test example using React Testing Library:
typescript
import { render, screen } from '@testing-library/react';
import { Breadcrumbs } from './Breadcrumbs';
describe('Breadcrumbs', () => {
it('renders all breadcrumb items', () => {
const items = [
{ href: '/docs', label: 'Docs' },
{ href: '/docs/guides', label: 'Guides' },
];
render(<Breadcrumbs items={items} />);
expect(screen.getByText('Docs')).toBeInTheDocument();
expect(screen.getByText('Guides')).toBeInTheDocument();
});
it('highlights last breadcrumb as active', () => {
const items = [
{ href: '/docs', label: 'Docs' },
{ href: '/docs/guides', label: 'Guides' },
];
render(<Breadcrumbs items={items} />);
const lastItem = screen.getByText('Guides');
expect(lastItem.parentElement).toHaveClass('font-medium');
});
});Accessibility
All components follow WCAG guidelines:
- ThemeToggle:
aria-labeldescribes toggle action - Breadcrumbs:
aria-label="Breadcrumb"on nav element - TableOfContents: Semantic nav with proper heading hierarchy
- CodeBlock: Language label visible, syntax highlighting for distinction
- SearchPalette: ARIA labels on buttons, keyboard navigation
Next Steps
- Review Configuration for theme setup
- Check Utilities for helper functions
- See Advanced Features for custom component patterns