-
Notifications
You must be signed in to change notification settings - Fork 11
Styling
We use Tailwind and CSS Modules for styling the frontend. Tailwind is our preferred styling solution, however, CSS modules is also available for use cases where it's not possible to style with Tailwind.
Tailwind is a CSS framework for generating utility CSS class that style the markup. For example, a centered div with a blue background would look like:
<div className="flex items-center justify-center bg-blue-500">
<p>Hello, World!</p>
</div>
The primary advantages of using Tailwind are for:
- Developer Efficiency: Using utility classes allow us to keep styling and markup close together, reducing the need to switch between editing TSX and CSS files.
- Performance: The Tailwind compiler only generates classnames for whatever is used in the codebase.
Tailwind can be configured in the tailwind.config.ts file.
In some cases when Tailwind doesn't work, we also support CSS Modules for
styling components. There may be a variety of reasons why you may want to use
CSS modules, but the most I've seen it applicable is when styling deeply nested
elements, or using CSS syntax that is not easily replicated in Tailwind like
@keyframes
.
To use a CSS module, you'll need to:
/* Example.module.css */
.component {
background: red;
/* You can still use Tailwind classes too if you need to */
@apply p-3;
/* And you can reference the theme too */
color: theme(colors.blue.500);
/* And you can use breakpoints defined in Tailwind too */
@media screen(md) {
background: theme(colors.blue.500);
color: red;
}
}
This will also generate the following type definitions using the typed-css-modules package:
// Example.module.css.d.ts
declare const styles: {
readonly example: string
}
export = styles
With the above type definition, it'll be possible to apply strict type CSS module classes and ensure we only use classes that exist:
// Example.tsx
import styles from './Example.module.css'
export function Example() {
return (
<div className={styles.component}>
<p>Hello, World!</p>
</div>
)
}
Sometimes we'll need to combine class names from various sources or conditionally show a class name. To do this, we can use the cns() utility function:
import { cns } from 'app/utils/cns'
// ...
return (
<div
className={cns(
'flex items-center justify-center',
// conditionally apply hidden class if `isHidden` prop is true
isHidden && 'hidden',
// Style based on multiple conditions
textColor && [
textColor === 'blue' && 'text-blue-500',
textColor === 'red' && 'text-red-500',
],
// Pass `className` prop last to allow components to override styles
className,
)}
>
<p>Hello, World!</p>
</div>
)
This is based on clsx with the added functionality of merging class names using tailwind-merge.
We use tailwind-merge
to handle conflicts when merging class names. This
ensures that any class names passed by consuming components override the styles.
While not recommended, in some cases, you may not want to merge class names. To
do that, you can use the cnsNoMerge()
function to merge class names without
tailwind-merge
.