Best Practices
NewStylus is a design system based on Radix UI and Tailwind. Stylus components are used throughout the Scribe codebase to create new features.
This guide is written for employees at Scribe using Stylus. It can also help to point LLMs to this file (search for best-practices.md
).
Using Stylus
Prefer props to classNames
Avoid writing too many Tailwind styles. This keeps the code lean and makes it easier to update styles in the future. If a designer has proposed a component with slight changes from what already exists, ask if it’s possible to use the existing styles.
<Button className="bg-red-500">
<Button variant="danger">
Prefer handling spacing via the parent element
Avoid setting margin
on children where possible. Working with margins on children makes it more difficult to reason about conditional components. When laying out components in a group, prefer a flex or grid container with gap
styles.
<div> <Child /> <Child className="ml-2" /></div>
<div className="flex gap-2"> <Child /> <Child /></div>
Always provide accessible descriptions
To ensure that screen reader users can access Scribe, always provide text descriptions to icon-only buttons and non-decorative images.
<Button icon={faUserPlus} />
<Button icon={faUserPlus} screenReaderLabel="Add user" />
<Tooltip> <TooltipTrigger asChild> <Button icon={faUserPlus} /> </TooltipTrigger> <TooltipContent>Add user</TooltipContent></Tooltip>
Avoid an excessive DOM size
Attempt to layout most things without adding additional div
wrappers. Per Google:
A large DOM tree can slow down page performance in multiple ways. […] In general, look for ways to create DOM nodes only when needed, and destroy nodes when they’re no longer needed.
Components without any content should generally return null
rather than an empty DOM node.
Adding To Stylus
Only add components or props with sufficient need
In general, new props or components should not be added until there are three or more places they are in use. A strong contender for addition to the design system will be able to work well in multiple locations and across multiple features. One-off growth experiments or feature-specific UI additions are unlikely to be added to Stylus.
Favor composition over configuration
When building complex components, prefer composing smaller components rather than adding many configuration props.
<ComplexCard hasHeader headerTitle="Title" headerAction={<Button>Edit</Button>} footer={<Button>Save</Button>} />
<Card> <CardHeader> <CardTitle>Title</CardTitle> <Button>Edit</Button> </CardHeader> <CardContent>...</CardContent> <CardFooter> <Button>Save</Button> </CardFooter></Card>
Follow consistent naming conventions
- Use PascalCase for component names:
UserProfile
,NavigationBar
- Use camelCase for props:
isLoading
,onValueChange
- Prefix boolean props with
is
,has
, orshould
:isDisabled
,hasError
,shouldAutoFocus
- Use Tailwind for CSS classes, and use class-variance-authority for organizing and defining variant styles
Maintain consistent prop patterns
- Use
onValueChange
instead ofonChange
for controlled components - Use
defaultValue
for uncontrolled components - Use
asChild
pattern for polymorphic components (following Radix patterns)
Test accessibility
Stylus components should be accessible by default.
- Text descriptions and appropriate
aria
roles should be supplied where necessary. - Prefer semantic HTML elements like
section
,aside
, andheader
to usingdiv
. - Always ensure that interactive elements have keyboard focus styles using
focus-visible
. - With very few exceptions, do not put
onClick
handlers ondiv
—use an HTMLbutton
.
Write tests that focus on behavior
Test what users do, not implementation details.
// Good: Tests user behaviortest('submits form when user clicks submit button', async () => { render(<ContactForm onSubmit={mockSubmit} />);
await user.click(screen.getByRole('button', { name: /submit/i }));
});
Use the scaffolding script for new components
You can scaffold new components from stylus-ui
using yarn scaffold ComponentName
ex. yarn scaffold SearchHeader
. This command will scaffold a new directory containing the component, barrel file, test file and Storybook file, along with documentation in /packages/stylus-docs
.
Document and test all components
- Write basic unit tests to validate functionality.
- Always document components inside the
stylus-docs
package. If props or styles change, update the docs to reflect the changes. - Write basic Storybook stories for each component.