dsai-components — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dsai-components (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Components are installed via the dsai CLI — not npm/yarn/pnpm install.
dsai add button # Single component
dsai add button modal card # Multiple components
dsai add --all # All components
dsai add --type hook # All hooksThis copies source files into the consumer project at src/client/components/ui/<name>/. Components are local source code that you own and can modify.
The registry resolves dependencies via BFS + topological sort, transforms relative imports to path aliases (@/), and writes to configured alias directories (components/ui/, hooks/, utils/). Existing files are never overwritten unless the --overwrite flag is passed.
After installing a component with dsai add, import it, render it, and customize:
// 1. Import the component from your local source
import { Button } from '@/components/ui/button';
// 2. Render it
function App() {
return (
<Button variant="primary" size="md" onClick={() => alert('Clicked!')}>
Get Started
</Button>
);
}
// 3. Customize with DSAI tokens (see dsai-styling skill)
// Components use Bootstrap 5 classes + --dsai-* CSS custom properties
// Use cn() for conditional classes — NOT tailwind-merge or clsxComponents — import from @/components/ui/<name>:
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { Modal } from '@/components/ui/modal';Type imports — import from the .types file:
import type { ButtonProps } from '@/components/ui/button/Button.types';
import type { CardProps } from '@/components/ui/card/Card.types';
import type { ModalProps } from '@/components/ui/modal/Modal.types';Hooks — import from @/hooks/:
import { useControllableState } from '@/hooks/useControllableState';
import { useFocusTrap } from '@/hooks/useFocusTrap';
import { useDarkMode } from '@/hooks/useDarkMode';Utilities — import from @/utils/ or @/lib/:
import { cn } from '@/utils/cn';
import { sanitizeHtml } from '@/utils/sanitize';Note:@dsai-io/reactis the internal monorepo package. Consumer projects import from@/components/ui/<name>— never from@dsai-io/react.
Styling: Components use Bootstrap 5 classes and--dsai-*CSS custom properties. See the dsai-styling skill for token usage,cn()patterns, and dark mode.
Every component follows forwardRef with an explicit displayName:
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) {
/* ... */
}
);
Button.displayName = 'Button';For performance-optimized components: memo(forwardRef(function Name(...))) + displayName.
Each component directory contains:
| File | Purpose |
|---|---|
ComponentName.tsx | Main component with forwardRef |
ComponentName.types.ts | TypeScript prop interfaces (always separate) |
ComponentName.fsm.ts | Finite State Machine (interactive components) |
ComponentName.test.tsx | Unit tests (Jest 30 + React Testing Library) |
ComponentName.a11y.test.tsx | Accessibility tests (jest-axe) |
index.ts | Barrel exports |
#### Accordion Collapsible sections.
#### Breadcrumb / BreadcrumbItem Navigation breadcrumbs.
#### Card (compound) Sub-components: Card.Header, Card.Body, Card.Footer, Card.Image, Card.Title, Card.Subtitle, Card.Text, Card.Link, Card.ImgOverlay
| Prop | Type | Notes | ||
|---|---|---|---|---|
variant | `'elevated' \ | 'outlined' \ | 'ghost'` | Visual style |
color | SemanticColorVariant | Semantic color | ||
size | `'sm' \ | 'md' \ | 'lg'` | Component size |
horizontal | boolean | Horizontal layout | ||
interactive | boolean | Hover/active states | ||
href | string | Makes card a link |
<Card variant="elevated" color="primary">
<Card.Header>Title</Card.Header>
<Card.Body>Content</Card.Body>
<Card.Footer>Actions</Card.Footer>
</Card>#### CardList List of cards.
#### Carousel Sub-components: CarouselItem, CarouselControl, CarouselIndicators, CarouselCaption, CarouselPauseButton
#### Container Layout container.
#### Modal (compound) Sub-components: Modal.Header, Modal.Body, Modal.Footer, Modal.Title, Modal.Description
Portal rendering, focus trap, scroll lock, FSM-driven animations.
| Prop | Type | Notes | ||||
|---|---|---|---|---|---|---|
isOpen | boolean | Controlled open state | ||||
onClose | () => void | Close callback | ||||
size | `'sm' \ | 'md' \ | 'lg' \ | 'xl' \ | 'fullscreen'` | Modal size |
centered | boolean | Vertically centered | ||||
backdrop | `boolean \ | 'static'` | Backdrop behavior | |||
closeOnEscape | boolean | ESC key closes modal | ||||
initialFocusRef | RefObject | Focus target on open |
<Modal isOpen={open} onClose={close}>
<Modal.Header closeButton>Edit Profile</Modal.Header>
<Modal.Body>Form content</Modal.Body>
<Modal.Footer>
<Button onClick={close}>Save</Button>
</Modal.Footer>
</Modal>#### Navbar Navigation bar.
#### Sheet Side panel overlay.
#### Tabs (compound) Sub-components: Tab, TabList, TabPanel
| Prop | Type | Notes | ||
|---|---|---|---|---|
variant | `'underline' \ | 'pills' \ | 'tabs'` | Visual style |
orientation | `'horizontal' \ | 'vertical'` | Tab direction | |
activationMode | `'automatic' \ | 'manual'` | Focus vs click activation | |
lazyMount | boolean | Lazy render panels | ||
unmountOnExit | boolean | Unmount inactive panels |
<Tabs variant="pills" orientation="horizontal">
<TabList aria-label="Settings">
<Tab id="general">General</Tab>
<Tab id="security">Security</Tab>
</TabList>
<TabPanel id="general">General settings</TabPanel>
<TabPanel id="security">Security settings</TabPanel>
</Tabs>#### TabsPro Enhanced tabs with closable and addable tabs.
#### Button FSM-based interactive component.
| Prop | Type | Notes | ||||
|---|---|---|---|---|---|---|
variant | `SemanticColorVariant \ | 'outline-*' \ | 'subtle-*' \ | 'ghost' \ | 'link'` | Visual style |
size | `'sm' \ | 'md' \ | 'lg' \ | 'icon'` | Button size | |
loading | boolean | Shows spinner, disables interaction | ||||
error | boolean | Error visual state | ||||
disabled | boolean | Disabled state | ||||
startIcon | ReactNode | Icon before label | ||||
endIcon | ReactNode | Icon after label | ||||
announceText | string | Screen reader announcement | ||||
as | ElementType | Polymorphic — renders as button, a, or custom |
FSM states: idle → hovered → focused → pressed → disabled → loading → error
FSM events: HOVER, BLUR, FOCUS, PRESS, RELEASE, DISABLE, ENABLE, START_LOADING, STOP_LOADING, ERROR, CLEAR_ERROR
#### Checkbox / CheckboxGroup Check controls.
#### Input Text input with built-in validation.
| Prop | Type | Notes | ||||||
|---|---|---|---|---|---|---|---|---|
type | `'text' \ | 'email' \ | 'password' \ | 'number' \ | 'tel' \ | 'url' \ | 'search'` | Input type |
size | `'sm' \ | 'md' \ | 'lg'` | Input size | ||||
label | string | Associated label | ||||||
helperText | string | Help text below input | ||||||
error | `string \ | boolean` | Error message or state | |||||
success | `string \ | boolean` | Success state | |||||
prefix | ReactNode | Left addon | ||||||
suffix | ReactNode | Right addon | ||||||
clearable | boolean | Show clear button | ||||||
showCount | boolean | Character count | ||||||
floating | boolean | Floating label | ||||||
plaintext | boolean | Read-only plain text |
#### Radio / RadioGroup Radio controls.
#### Select Generic typed dropdown select.
| Prop | Type | Notes |
|---|---|---|
SelectProps<T> | Generic | Typed to option shape |
multiple | boolean | Multi-select |
searchable | boolean | Filterable options |
clearable | boolean | Clear selection |
loading | boolean | Loading state |
renderOption | (option: T) => ReactNode | Custom option rendering |
renderValue | (value: T) => ReactNode | Custom value rendering |
#### Switch Toggle switch.
#### SearchSelectField Searchable select variant.
#### SelectableCard Card that acts as a selection control.
#### Alert Notification banners.
#### Avatar / AvatarGroup User avatars.
#### Badge / BadgeWrapper Status indicators.
#### Display Large display text.
#### Heading h1–h6 elements.
#### Text Paragraph text.
#### Typography Combined typography component.
#### ListGroup / ListGroupItem List displays.
#### Pagination Page navigation.
#### Popover (compound) Sub-components: PopoverHeader, PopoverBody, PopoverCloseButton
#### Progress Progress bars.
#### Scrollspy Scroll-based navigation highlighting.
#### Spinner Loading indicators.
#### Table Data tables.
#### Toast / ToastContainer / ToastProvider Toast notifications.
#### Tooltip / TooltipProvider / TooltipGroup Tooltips.
#### Dropdown Dropdown menus.
Defined in types/primitives.ts:
type SemanticColorVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark';
type ComponentSize = 'sm' | 'md' | 'lg';
type ExtendedSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'xxl';
type FeedbackVariant = 'success' | 'error' | 'warning' | 'info' | 'default';
type Alignment = 'start' | 'center' | 'end';
type Orientation = 'horizontal' | 'vertical';type PolymorphicComponentProps<C extends React.ElementType, Props> =
Props &
Omit<React.ComponentPropsWithRef<C>, keyof Props> &
{ as?: C };Allowed: className, style, id, title, tabIndex, data-testid, role, and ARIA attributes.
Blocked: onClick, onMouseOver, onLoad, dangerouslySetInnerHTML, and all event handlers.
Components define their own safe event handler props explicitly. Never spread unknown props.
interface FSMStateBase { status?: string; }
interface FSMEventBase { type: string; }
type FSMReducer<State, Event> = (state: State, event: Event) => State;
interface VisualStateBase { state: string; shouldRender: boolean; modifiers?: string[]; }Interactive components use useReducer with a finite state machine:
const [fsmState, dispatch] = useReducer(
buttonFSMReducer,
initialProps,
createInitialButtonFSMState
);Events dispatched: HOVER, BLUR, FOCUS, PRESS, RELEASE, DISABLE, ENABLE, START_LOADING, STOP_LOADING, ERROR, CLEAR_ERROR
Parent and children communicate via React Context:
<Card variant="elevated" color="primary">
<Card.Header>Title</Card.Header>
<Card.Body>Content</Card.Body>
<Card.Footer>Actions</Card.Footer>
</Card>Components support both modes via the useControllableState hook:
// Controlled
<Select value={selected} onChange={setSelected} options={options} />
// Uncontrolled
<Select defaultValue="option1" options={options} />Simple class name composition (NOT tailwind-merge). Uses Bootstrap 5 class naming:
cn('btn', variant && `btn-${variant}`, size === 'lg' && 'btn-lg', className)
// Filters falsy values, joins with spaceuseFocusTrap — Trap focus within a containeruseRovingFocus — Arrow-key focus navigationuseReducedMotion — Detect prefers-reduced-motionuseMediaQuery — Match media queriesuseIsMobile / useIsTablet / useIsDesktop / useIsLargeDesktop — Breakpoint hooksuseScrollLock — Prevent body scrolluseDarkMode — Dark mode stateuseControllableState — Controlled/uncontrolled stateusePrevious — Previous valueuseLocalStorage / useSessionStorage — Persisted stateuseDebounce / useThrottle — TiminguseField — Field-level state and validationuseForm — Form state managementuseClickOutside — Detect clicks outside elementuseKeyPress — Key event handlinguseHover — Hover detectionuseIntersectionObserver — Visibility detectionuseResizeObserver — Size change detectionuseAsync — Async operation stateuseCallbackRef — Stable callback referenceuseId — Unique ID generationuseMounted — Mount state tracking| Category | Functions |
|---|---|
| Core | cn(), generateId, clamp, isBrowser, mergeRefs, prefersReducedMotion |
| Accessibility | announceToScreenReader, focusableSelectors, trapFocus, getAnimationDuration, shouldAnimate |
| Keyboard | isEnterKey, isEscapeKey |
| DOM | mergeRefs |
| Objects | deepMerge, omit, pick |
| String | capitalize, slugify, truncate, getVariantClass |
| Timing | debounce, throttle |
| Safety | sanitizeHtml, sanitizeUrl, generateCryptoId, copyToClipboard |
| Validation | isValidEmail, isValidHref, isSafeHref, isValidUrl, isExternalUrl |
| Color | getContrastRatio, hexToRgb, rgbToHsl, hslToHex, meetsWCAG |
| Forms | validateField, validateForm, parseFormData, serializeForm |
| Misc | mapPlacement, normalizeTriggers, getSafeInputProps, ClearIcon |
forwardRef + displayName — not optional.*.types.ts files.SemanticColorVariant for colors — never hardcode hex/rgb values.ComponentSize for sizing (sm | md | lg).useReducer) for state management.SafeHTMLAttributes — never pass event handlers through prop spread; define them explicitly.cn() for class composition — Bootstrap 5 classes, not Tailwind.jest-axe (a11y) and React Testing Library.@/ not @dsai-io/react or any npm package.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.