| name | syntax-design-system |
| description | Guide for working with Cambly's Syntax design system React components. Use when implementing UI features with Syntax components, converting existing UI to use the design system, fixing component styling or accessibility issues, or reviewing code that uses Syntax. Use when this capability is needed. |
| metadata | {"author":"cambly"} |
Syntax Design System
Use this skill when working with Cambly's Syntax design system components in React applications. Invoke this skill when:
- Implementing new UI features using Syntax components
- Converting existing UI to use Syntax components
- Fixing styling, accessibility, or component usage issues
- Reviewing code that uses Syntax components
Core Principles
When working with Syntax components, you must:
- Always use
Box instead of plain divs for layout and containers
- Never hardcode spacing or colors - use the design system's spacing scale (0-12) and color tokens
- Always provide
accessibilityLabel for IconButton components
- Use semantic HTML via the
as prop (e.g., <Box as="nav">)
- Add
data-testid attributes for all interactive elements in tests
Installation
The Syntax packages required are:
@cambly/syntax-core - Main component library
@cambly/syntax-design-tokens - Design tokens
@cambly/syntax-icons - Icon components
@cambly/syntax-floating-components - Tooltips and popovers with Floating UI
npm install @cambly/syntax-core @cambly/syntax-design-tokens @cambly/syntax-icons @cambly/syntax-floating-components
Component Selection Guide
Use this decision tree to select the right component:
Layout & Containers:
- Layout container →
Box (with flexbox props)
- Content container with elevation →
Card
Buttons & Actions:
- Button with text →
Button
- Button with icon only →
IconButton (requires accessibilityLabel)
- Link styled as button →
LinkButton
- Clickable area without button semantics →
TapArea
- Group related buttons →
ButtonGroup
Typography:
- Body text, labels, captions →
Typography
- Page/section headings →
Heading (with proper as prop: h1-h6)
Form Inputs:
- Single-line text →
TextField
- Multi-line text →
TextArea
- True/false selection →
Checkbox
- One of many options →
RadioButton
- Dropdown selection →
SelectList (native) or RichSelectList (styled)
Feedback & Overlays:
- Blocking dialog →
Modal
- Contextual overlay →
Popover
- Temporary notification →
Toast
- Hover information →
Tooltip
Indicators & Status:
- Small status indicator →
Badge
- User profile image →
Avatar or AvatarGroup
- Filter/tag →
Chip
- Visual separator →
Divider
- Icons →
Icon (use with @cambly/syntax-icons)
Navigation:
- Tab interface →
Tabs with TabButton or TabLink
Required Patterns
Box Component - The Foundation
Basic Layout:
<Box
display="flex"
direction="column"
gap={4}
justifyContent="center"
alignItems="start"
>
{children}
</Box>
Responsive Layout (mobile-first):
<Box
direction="column"
smDirection="row"
lgDirection="row"
padding={2}
smPadding={4}
lgPadding={6}
>
{children}
</Box>
Spacing (0-12 scale):
<Box
margin={4}
marginTop={2}
marginStart={3}
padding={4}
paddingX={6}
paddingY={2}
>
Semantic HTML:
<Box as="section" role="main">
<Box as="nav"> // Renders as <nav>
Size Variants
Buttons/IconButtons/Chips: "sm" | "md" | "lg" (heights: 32px, 48px, 64px)
<Button text="Click me" size="md" />
Typography/Heading: 100 | 200 | 300 | 400 | 500 | 700 | 800 | 900 | 1100
<Typography size={400}>Body text</Typography>
<Heading size={600}>Page title</Heading>
Icon: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 1000
<Icon path={iconPath} size={400} />
Color System
Button Colors:
primary - Main call-to-action (blue)
secondary - Secondary actions (gray)
tertiary - Tertiary actions (minimal)
destructive-primary/secondary/tertiary - Dangerous actions (red)
success-primary/secondary/tertiary - Positive actions (green)
branded - Brand-specific color
<Button text="Delete" color="destructive-primary" />
<Button text="Cancel" color="secondary" />
Background Context (on prop):
<Button text="Click" on="darkBackground" />
<TextField label="Name" on="lightBackground" />
Universal Colors (Box, Icon, Typography):
- Primary:
primary, primary100-primary900
- Semantic:
success100-success900, destructive100-destructive900
- Neutrals:
gray10-gray900, white, black
- Brand:
sky, navy, teal, lilac, pink, cream
<Box backgroundColor="gray10" />
<Typography color="primary700">Text</Typography>
<Icon color="success500" size={300} />
Form Patterns
TextField with validation:
<TextField
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={!!emailError}
errorText={emailError}
helperText="We'll never share your email"
disabled={isSubmitting}
data-testid="email-input"
/>
Checkbox:
<Checkbox
label="I agree to terms"
checked={agreed}
onChange={(e) => setAgreed(e.target.checked)}
error={submitted && !agreed}
size="md"
/>
RadioButton group:
{
options.map((option) => (
<RadioButton
key={option.value}
label={option.label}
checked={selected === option.value}
onChange={() => setSelected(option.value)}
name="options"
/>
));
}
Button Patterns
Button with icons:
import { ChevronRightIcon } from "@cambly/syntax-icons";
<Button text="Continue" endIcon={ChevronRightIcon} color="primary" size="lg" />;
Loading state:
<Button
text="Save"
loading={isSubmitting}
loadingText="Saving..."
disabled={!isDirty}
/>
IconButton (REQUIRED: accessibilityLabel):
import { CloseIcon } from "@cambly/syntax-icons";
<IconButton
icon={CloseIcon}
accessibilityLabel="Close dialog" // REQUIRED!
color="secondary"
size="sm"
onClick={handleClose}
/>;
Typography Patterns
<Heading as="h1" size={800}>Page Title</Heading>
<Heading as="h2" size={600}>Section Heading</Heading>
<Typography size={400}>Regular paragraph text</Typography>
<Typography size={300} color="gray700">Secondary text</Typography>
<Typography
weight="semiBold"
transform="uppercase"
align="center"
lineClamp={2} // Truncate after 2 lines
>
Content
</Typography>
Modal & Dialog Pattern
<Modal isOpen={isOpen} onDismiss={handleClose} title="Confirm Action" size="md">
<Box padding={6}>
<Typography>Are you sure?</Typography>
<Box display="flex" gap={2} marginTop={4}>
<Button text="Cancel" color="secondary" onClick={handleClose} />
<Button text="Confirm" color="primary" onClick={handleConfirm} />
</Box>
</Box>
</Modal>
Tooltip Pattern
<Tooltip text="Additional information">
<IconButton icon={InfoIcon} accessibilityLabel="Info" />
</Tooltip>
Card Pattern
<Card size="medium" backgroundColor="white">
<Box padding={4}>
<Heading size={500}>Card Title</Heading>
<Typography size={300} color="gray700">
Card content goes here
</Typography>
</Box>
</Card>
Accessibility Requirements
Required Props
Form inputs need labels:
<TextField label="Email" />
<Checkbox label="I agree" />
Icon buttons need accessibilityLabel:
<IconButton
icon={TrashIcon}
accessibilityLabel="Delete item"
/>
Use semantic HTML:
<Box as="nav">
<Box as="main">
<Heading as="h1"> // Proper heading hierarchy
Focus Management
- Components automatically handle keyboard focus visibility
- Focus outlines only appear on keyboard navigation (not clicks)
- All interactive elements are keyboard accessible
Form Accessibility
<Box>
<TextField label="Password" aria-describedby="password-help" />
<Typography id="password-help" size={200}>
Must be at least 8 characters
</Typography>
</Box>
Common Mistakes to Avoid
❌ Using plain divs for layout
<div style={{ display: 'flex', gap: '16px' }}>
✅ Use Box component
<Box display="flex" gap={4}>
❌ Missing accessibilityLabel
<IconButton icon={CloseIcon} />
✅ Always include accessibilityLabel
<IconButton icon={CloseIcon} accessibilityLabel="Close" />
❌ Hardcoded pixel values
<Box style={{ marginTop: '24px' }}>
✅ Use spacing scale
<Box marginTop={6}>
❌ Wrong icon imports
import CloseIcon from "@cambly/syntax-icons/CloseIcon";
✅ Named imports from package root
import { CloseIcon } from "@cambly/syntax-icons";
❌ Ignoring background context
<Box backgroundColor="navy">
<Button text="Click me" />
</Box>
✅ Use 'on' prop for contrast
<Box backgroundColor="navy">
<Button text="Click me" on="darkBackground" />
</Box>
❌ Avoid “self-spacing” children
<Box marginBottom={2}>Some text</Box>
<Box marginBottom={2}>Some more text</Box>
✅ Use a parent wrapper to define sibling spacing
<Box display="flex" direction="column" gap={2}>
<Box>Some text</Box>
<Box>Some more text</Box>
</Box>
```
#### When child margins are OK
Use margins on a component only when it is intrinsic to that component, not dependent on neighbors (e.g., internal spacing within the component, or a visual affordance that always exists regardless of placement).
## Import Patterns
```typescript
import { Box, Button, Typography, TextField } from "@cambly/syntax-core";
import { ChevronRightIcon, CloseIcon, CheckIcon } from "@cambly/syntax-icons";
import { FloatingTooltip } from "@cambly/syntax-floating-components";
import { tokens } from "@cambly/syntax-design-tokens";
Testing
Always add data-testid for testable elements:
<Button
text="Submit"
data-testid="submit-button"
onClick={handleSubmit}
/>
<TextField
label="Email"
data-testid="email-input"
value={email}
/>
Theme Provider
Wrap your app root with ThemeProvider:
import { ThemeProvider } from "@cambly/syntax-core";
function App() {
return <ThemeProvider>{/* Your app */}</ThemeProvider>;
}
Review Checklist
When reviewing or writing code with Syntax components, verify:
Additional Resources
- Storybook documentation: Run
pnpm start in the syntax repo
- Component props: TypeScript types provide full documentation
- All components have full TypeScript type definitions
Converted and distributed by TomeVault — claim your Tome and manage your conversions.