17 KiB
17 KiB
IGNY8 Frontend Component System
Last Updated: January 3, 2026
Version: 1.3.2
🔒 ENFORCED BY ESLINT - Violations will trigger warnings/errors during build. This document is the single source of truth for all UI components.
Quick Reference Card
| Element | Component | Import Path |
|---|---|---|
| Button | Button |
components/ui/button/Button |
| Icon Button | IconButton |
components/ui/button/IconButton |
| Button Group | ButtonGroup |
components/ui/button-group/ButtonGroup |
| Text Input | InputField |
components/form/input/InputField |
| Checkbox | Checkbox |
components/form/input/Checkbox |
| Radio | Radio |
components/form/input/Radio |
| File Upload | FileInput |
components/form/input/FileInput |
| Textarea | TextArea |
components/form/input/TextArea |
| Dropdown | Select |
components/form/Select |
| Searchable Dropdown | SelectDropdown |
components/form/SelectDropdown |
| Multi-Select | MultiSelect |
components/form/MultiSelect |
| Toggle Switch | Switch |
components/form/switch/Switch |
| Badge | Badge |
components/ui/badge/Badge |
| Card | Card |
components/ui/card/Card |
| Modal | Modal |
components/ui/modal |
| Alert | Alert |
components/ui/alert/Alert |
| Spinner | Spinner |
components/ui/spinner/Spinner |
| Tabs | Tabs |
components/ui/tabs/Tabs |
| Tooltip | Tooltip |
components/ui/tooltip/Tooltip |
| Calendar Tooltip | CalendarItemTooltip |
components/ui/tooltip/CalendarItemTooltip |
| Toast | useToast |
components/ui/toast/ToastContainer |
| Icons | *Icon |
icons (e.g., ../../icons) |
1. BUTTONS
Button (Standard)
import Button from '../../components/ui/button/Button';
<Button
variant="primary" // primary | secondary | outline | ghost | gradient
tone="brand" // brand | success | warning | danger | neutral
size="md" // xs | sm | md | lg | xl | 2xl
shape="rounded" // rounded | pill
startIcon={<Icon />} // Icon before text
endIcon={<Icon />} // Icon after text
onClick={handler}
disabled={false}
fullWidth={false}
type="button" // button | submit | reset
className="" // Additional classes
>
Button Text
</Button>
Common Patterns:
// Primary action
<Button variant="primary" tone="brand">Save</Button>
// Success/confirm
<Button variant="primary" tone="success">Approve</Button>
// Danger/destructive
<Button variant="primary" tone="danger">Delete</Button>
// Secondary/cancel
<Button variant="outline" tone="neutral">Cancel</Button>
// With icon
<Button startIcon={<PlusIcon className="w-4 h-4" />}>Add Item</Button>
IconButton (Icon-only)
import IconButton from '../../components/ui/button/IconButton';
<IconButton
icon={<CloseIcon />} // Required: Icon component
variant="ghost" // solid | outline | ghost
tone="neutral" // brand | success | warning | danger | neutral
size="sm" // xs | sm | md | lg
shape="rounded" // rounded | circle
onClick={handler}
disabled={false}
title="Close" // Required: Accessibility label
aria-label="Close" // Optional: Override aria-label
/>
Common Patterns:
// Close button
<IconButton icon={<CloseIcon />} variant="ghost" tone="neutral" title="Close" />
// Delete button
<IconButton icon={<TrashBinIcon />} variant="ghost" tone="danger" title="Delete" />
// Edit button
<IconButton icon={<PencilIcon />} variant="ghost" tone="brand" title="Edit" />
// Add button (circular)
<IconButton icon={<PlusIcon />} variant="solid" tone="brand" shape="circle" title="Add" />
2. FORM INPUTS
InputField (Text/Number/Email/Password)
import InputField from '../../components/form/input/InputField';
<InputField
type="text" // text | number | email | password | date | time
label="Field Label"
placeholder="Enter value..."
value={value}
onChange={(e) => setValue(e.target.value)}
id="field-id"
name="field-name"
disabled={false}
error={false} // Shows error styling
success={false} // Shows success styling
hint="Helper text" // Text below input
min="0" // For number inputs
max="100" // For number inputs
step={1} // For number inputs
/>
TextArea (Multi-line)
import TextArea from '../../components/form/input/TextArea';
<TextArea
placeholder="Enter description..."
rows={4}
value={value}
onChange={(value) => setValue(value)}
disabled={false}
error={false}
hint="Helper text"
/>
Checkbox
import Checkbox from '../../components/form/input/Checkbox';
<Checkbox
label="Accept terms"
checked={checked}
onChange={(checked) => setChecked(checked)}
id="checkbox-id"
disabled={false}
/>
Radio
import Radio from '../../components/form/input/Radio';
<Radio
id="radio-option-1"
name="radio-group"
value="option1"
label="Option 1"
checked={selected === 'option1'}
onChange={(value) => setSelected(value)}
/>
Select (Dropdown)
import Select from '../../components/form/Select';
<Select
options={[
{ value: 'opt1', label: 'Option 1' },
{ value: 'opt2', label: 'Option 2' },
]}
placeholder="Select..."
defaultValue=""
onChange={(value) => setValue(value)}
className=""
/>
SelectDropdown (Searchable)
import SelectDropdown from '../../components/form/SelectDropdown';
<SelectDropdown
label="Select Item"
options={options}
value={value}
onChange={(value) => setValue(value)}
placeholder="Search..."
searchable={true}
/>
Switch (Toggle)
import Switch from '../../components/form/switch/Switch';
<Switch
label="Enable feature"
checked={enabled} // Controlled mode
onChange={(checked) => setEnabled(checked)}
disabled={false}
color="blue" // blue | gray
/>
FileInput
import FileInput from '../../components/form/input/FileInput';
<FileInput
onChange={(files) => handleFiles(files)}
accept=".csv,.json"
multiple={false}
/>
3. DISPLAY COMPONENTS
Badge
import Badge from '../../components/ui/badge/Badge';
<Badge
tone="success" // brand | success | warning | danger | info | neutral | purple | indigo | pink | teal | cyan | blue
variant="soft" // solid | soft | outline | light
size="sm" // xs | sm | md
startIcon={<Icon />}
endIcon={<Icon />}
>
Label
</Badge>
Status Badge Patterns:
<Badge tone="success" variant="soft">Active</Badge>
<Badge tone="warning" variant="soft">Pending</Badge>
<Badge tone="danger" variant="soft">Failed</Badge>
<Badge tone="info" variant="soft">Draft</Badge>
<Badge tone="neutral" variant="soft">Archived</Badge>
Card
import { Card, CardTitle, CardContent, CardDescription, CardAction, CardIcon } from '../../components/ui/card/Card';
<Card
variant="surface" // surface | panel | frosted | borderless | gradient
padding="md" // none | sm | md | lg
shadow="sm" // none | sm | md
>
<CardIcon><Icon /></CardIcon>
<CardTitle>Title</CardTitle>
<CardDescription>Description text</CardDescription>
<CardContent>Main content</CardContent>
<CardAction onClick={handler}>Action</CardAction>
</Card>
Alert
import Alert from '../../components/ui/alert/Alert';
<Alert
variant="success" // success | error | warning | info
title="Alert Title"
message="Alert message text"
showLink={false}
linkHref="#"
linkText="Learn more"
/>
Modal
import { Modal } from '../../components/ui/modal';
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
showCloseButton={true}
isFullscreen={false}
>
<div className="p-6">
Modal content
</div>
</Modal>
Spinner
import { Spinner } from '../../components/ui/spinner/Spinner';
<Spinner
size="md" // sm | md | lg
color="primary" // primary | success | error | warning | info
/>
Tooltip
import { Tooltip } from '../../components/ui/tooltip/Tooltip';
<Tooltip text="Tooltip text" placement="top">
<span>Hover target</span>
</Tooltip>
Toast (Notifications)
import { useToast } from '../../components/ui/toast/ToastContainer';
const toast = useToast();
// Show notifications
toast.success('Success', 'Operation completed');
toast.error('Error', 'Something went wrong');
toast.warning('Warning', 'Please review');
toast.info('Info', 'Here is some information');
4. ICONS
Importing Icons
// Always import from central icons folder
import { PlusIcon, CloseIcon, CheckCircleIcon } from '../../icons';
// Use consistent sizing
<PlusIcon className="w-4 h-4" /> // Small (in buttons, badges)
<PlusIcon className="w-5 h-5" /> // Medium (standalone)
<PlusIcon className="w-6 h-6" /> // Large (headers, features)
Available Icons
Core Icons:
PlusIcon,CloseIcon,CheckCircleIcon,AlertIcon,InfoIcon,ErrorIconBoltIcon,ArrowUpIcon,ArrowDownIcon,ArrowRightIcon,ArrowLeftIconPencilIcon,TrashBinIcon,DownloadIcon,CopyIconEyeIcon,EyeCloseIcon,LockIcon,UserIconFolderIcon,FileIcon,GridIcon,ListIconChevronDownIcon,ChevronUpIcon,ChevronLeftIcon,ChevronRightIconAngleDownIcon,AngleUpIcon,AngleLeftIcon,AngleRightIcon
Module Icons:
TaskIcon,PageIcon,TableIcon,CalendarIconPlugInIcon,DocsIcon,MailIcon,ChatIconPieChartIcon,BoxCubeIcon,GroupIconShootingStarIcon,DollarLineIcon
Aliases (for compatibility):
TrashIcon→TrashBinIconXIcon/XMarkIcon→CloseIconSearchIcon→GridIconSettingsIcon→BoxCubeIconFilterIcon→ListIcon
Adding New Icons
- Add SVG file to
src/icons/ - Export in
src/icons/index.ts:
import { ReactComponent as NewIcon } from "./new-icon.svg?react";
export { NewIcon };
5. FOLDER STRUCTURE
src/
├── components/
│ ├── ui/ # UI Components (display/interaction)
│ │ ├── accordion/
│ │ ├── alert/
│ │ │ ├── Alert.tsx
│ │ │ └── AlertModal.tsx
│ │ ├── avatar/
│ │ ├── badge/
│ │ │ └── Badge.tsx
│ │ ├── breadcrumb/
│ │ ├── button/
│ │ │ ├── Button.tsx # ← Standard button
│ │ │ ├── IconButton.tsx # ← Icon-only button
│ │ │ └── ButtonWithTooltip.tsx
│ │ ├── button-group/
│ │ ├── card/
│ │ ├── dataview/
│ │ ├── dropdown/
│ │ ├── list/
│ │ ├── modal/
│ │ ├── pagination/
│ │ ├── progress/
│ │ ├── ribbon/
│ │ ├── spinner/
│ │ ├── table/
│ │ ├── tabs/
│ │ ├── toast/
│ │ ├── tooltip/
│ │ └── videos/
│ │
│ └── form/ # Form Components (inputs)
│ ├── input/
│ │ ├── InputField.tsx # ← Text/number/email inputs
│ │ ├── Checkbox.tsx # ← Checkbox
│ │ ├── Radio.tsx # ← Radio button
│ │ ├── RadioSm.tsx # ← Small radio button
│ │ ├── FileInput.tsx # ← File upload
│ │ └── TextArea.tsx # ← Multi-line text
│ ├── switch/
│ │ └── Switch.tsx # ← Toggle switch
│ ├── Select.tsx # ← Dropdown select
│ ├── SelectDropdown.tsx # ← Searchable dropdown
│ ├── MultiSelect.tsx # ← Multi-select dropdown
│ ├── Label.tsx # ← Form labels
│ ├── Form.tsx # ← Form wrapper
│ └── date-picker.tsx # ← Date picker
│
├── icons/ # All SVG icons
│ ├── index.ts # ← Export all icons from here
│ ├── plus.svg
│ ├── close.svg
│ └── ... (50+ icons)
│
└── styles/
└── design-system.css # ← Global design tokens
6. ESLINT ENFORCEMENT
Rules File Location
eslint-plugin-igny8-design-system.cjs (project root)
Active Rules
| Rule | Severity | Description |
|---|---|---|
no-raw-button |
warn | Use Button or IconButton instead of <button> |
no-raw-input |
warn | Use InputField, Checkbox, Radio, FileInput instead of <input> |
no-raw-select |
warn | Use Select or SelectDropdown instead of <select> |
no-raw-textarea |
warn | Use TextArea instead of <textarea> |
no-restricted-imports |
error | Block imports from @heroicons/*, lucide-react, @mui/icons-material |
Running Lint Check
npm run lint
Viewing Violations
npm run lint 2>&1 | grep "igny8-design-system"
7. MIGRATION EXAMPLES
Raw Button → Button Component
// ❌ BEFORE
<button
className="px-4 py-2 bg-brand-500 text-white rounded hover:bg-brand-600"
onClick={handleClick}
>
Save
</button>
// ✅ AFTER
<Button variant="primary" tone="brand" onClick={handleClick}>
Save
</Button>
Raw Button → IconButton
// ❌ BEFORE
<button
className="p-1 hover:bg-gray-100 rounded"
onClick={onClose}
>
<CloseIcon className="w-4 h-4" />
</button>
// ✅ AFTER
<IconButton
icon={<CloseIcon />}
variant="ghost"
tone="neutral"
size="sm"
onClick={onClose}
title="Close"
/>
Raw Input → InputField
// ❌ BEFORE
<input
type="text"
className="border rounded px-3 py-2 focus:ring-2"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter name"
/>
// ✅ AFTER
<InputField
type="text"
label="Name"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter name"
/>
Raw Select → Select Component
// ❌ BEFORE
<select
className="border rounded px-3 py-2"
value={status}
onChange={(e) => setStatus(e.target.value)}
>
<option value="">Select status</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
// ✅ AFTER
<Select
options={[
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
]}
placeholder="Select status"
defaultValue={status}
onChange={(value) => setStatus(value)}
/>
External Icon → Internal Icon
// ❌ BEFORE
import { XIcon } from '@heroicons/react/24/outline';
<XIcon className="w-5 h-5" />
// ✅ AFTER
import { CloseIcon } from '../../icons';
<CloseIcon className="w-5 h-5" />
8. SITE-SPECIFIC COMPONENTS (v1.3.2)
SiteInfoBar
Reusable site info header component for site-specific pages.
import { SiteInfoBar } from '../../components/common/SiteInfoBar';
<SiteInfoBar
site={currentSite} // Site object with name, domain, etc.
onSiteChange={handleSiteChange} // Optional: Callback when site changes
showSelector={true} // Whether to show site selector dropdown
/>
OnboardingWizard Components
Located in components/onboarding/:
import { OnboardingWizard } from '../../components/onboarding/OnboardingWizard';
import {
Step1Welcome,
Step2AddSite,
Step3ConnectIntegration,
Step4AddKeywords,
Step5Complete,
} from '../../components/onboarding/steps';
CalendarItemTooltip
Tooltip specifically designed for calendar items.
import { CalendarItemTooltip } from '../../components/ui/tooltip';
<CalendarItemTooltip
item={contentItem} // Content object
onView={handleView} // Optional: View callback
onRemove={handleRemove} // Optional: Remove callback
/>
9. LIVE REFERENCE
View all components with live examples at: /ui-elements
This page shows every component with all prop variations.
10. AI AGENT INSTRUCTIONS
When working on this codebase, AI agents MUST:
- Never use raw HTML elements (
<button>,<input>,<select>,<textarea>) - Import icons only from
src/icons- never from external libraries - Follow the import paths specified in this document
- Check ESLint after making changes:
npm run lint - Use semantic color tokens - never hardcoded hex values or Tailwind defaults
Color Rules
// ✅ CORRECT - Use semantic tokens
className="bg-brand-500 text-gray-900 border-success-500"
// ❌ WRONG - Tailwind defaults (DISABLED)
className="bg-blue-500 text-slate-900 border-green-500"
// ❌ WRONG - Hardcoded hex
className="bg-[#0077B6]"
style={{ color: '#DC2626' }}
- Reference this document for correct component usage
- Use consistent icon sizing:
className="w-4 h-4"for small,w-5 h-5for medium
If a component doesn't exist, create it in components/ui/ or components/form/ first.