Customization
Learn how to customize the appearance of all components
Globals
You can easily customize the appearance of components by modifying the CSS variables found in globals.css
.
/* ... */
:root {
/* ... */
--border: theme('colors.gray.200');
--ring: theme('colors.brand.500');
--muted: theme('colors.gray.500');
--muted-highlight: theme('colors.gray.700');
--paper: theme('colors.white');
--subtle: rgba(0, 0, 0, 0.04);
--subtle-highlight: rgba(0, 0, 0, 0.08);
.dark, [data-mode="dark"] {
/* ... */
}
}
/* ... */
Chalk UI uses these variables without shortcuts. For example, text-[--blue]
instead of text-blue
. This is a deliberate choice to easily identify them in the code and allow for easy refactoring.
Anatomy
Most components are styles are defined using the defineStyleAnatomy
helper. This helper returns an object containing each part of the component's anatomy.
Each part is represented by a distinct class-variance-authority function.
For example, the PageHeader
component has several parts such as body
, title
, description
, etc..., each with its own styling.
CVA offers features like variants
and compoundVariants
. Learn more about CVA.
This approach gives you a lot of flexibility when customizing components.
For example, you can easily locate the part you want to customize by looking at the anatomy.
You can also use the variants
and compoundVariants
features to effortlessly create new variants or customize the appearance of a component based on its props.
Additionally, you can share anatomy styles between multiple components, allowing you to create a consistent design system without having to duplicate code.
Thanks to the ComponentAnatomy
helper type, class
props are automatically inferred from the anatomy, allowing targeted customization using Tailwind classes.
For example, you can easily change the font weight of the title
part of a specific PageHeader
component, without affecting other PageHeader
components by passing a titleClass
prop.
// @/components/ui/page-header/page-header.tsx
export const PageHeaderAnatomy = defineStyleAnatomy({
// ...
title: cva("UI-PageHeader__title font-bold text-gray-900 dark:text-gray-200", {
variants: {
size: { sm: "text-lg sm:text-xl", md: "text-xl sm:text-2xl", lg: "text-2xl sm:text-3xl", xl: "text-2xl sm:text-4xl" },
},
defaultVariants: { size: "md" },
}),
// ...
})
export type PageHeaderProps = React.ComponentPropsWithoutRef<"div"> &
ComponentAnatomy<typeof PageHeaderAnatomy> & // Infers class props (in this case, `titleClass`)
VariantProps<typeof PageHeaderAnatomy.title> & // Infers variants (in this case, `size`)
{ /* ... */ }
export const PageHeader = ({ title, size, titleClass }: PageHeaderProps) => {
return (
<div> {/*...*/}
<h1 className={cn(PageHeaderAnatomy.title({ size }), titleClass)}>{title}</h1> {/* The styles are merged */}
{/*...*/}
</div>
)
}
import { PageHeader } from "@/components/ui/page-header"
export const DashboardPageHeader = () => {
return (
<PageHeader
title="Dashboard"
size="xl" // Change `size` variant
titleClass="font-medium" // Overwrite `title` font weight
/>
)
}
Good to know
root
is a special part that does not produce arootClass
prop. Instead, it is merged with the component's mainclassName
.