Styling

How to approach styling with Radix Themes.

Introduction

Radix Themes does not come with a built-in styling system. There's no css or sx prop, and it does not use any styling libraries internally. Under the hood, it's built with vanilla CSS.

There's no overhead when it comes to picking a styling technology for your app.

What You Get

The components in Radix Themes are relatively closed - they come with a set of styles that aren't always easily overridden. They are customizable within what's allowed by their props and the theme configuration.

However, you also get access to the same CSS variables that power the Radix Themes components. You can use these tokens to create custom components that naturally feel at home in the original theme. Changes to the token system are treated as breaking.

For more information on specific tokens, refer to the corresponding guides in the Theme section.

Overriding Styles

Beyond simple style overrides, we recommend using the components as-is, or create your own versions using the same building blocks.

Most components do have class and style props, but if you find yourself needing to override a lot of styles, it's a good sign that you should either:

  • Try to achieve what you need with the existing props and theme configuration.
  • See whether you can achieve your design by tweaking the underlying token system.
  • Create your own component using lower-level building blocks, such as Primitives and Colors.
  • Reconsider whether Radix Themes is the right fit for your project.

Tailwind

Tailwind is great. Yet, if you plan to use Radix Themes with Tailwind, keep in mind how its ergonomics may encourage you to create complex styles on the fly, sometimes reaching into the component internals without friction.

Tailwind is a different styling paradigm, which may not mix well with the idea of a closed component system where customization is achieved through props, tokens, and creating new components on top of a shared set of building blocks.

Custom Components

If you need to create a custom component, use the same building blocks that Radix Themes uses:

  • Theme tokens that power the components
  • Radix Primitives, a library of accessible, unstyled components
  • Radix Colors, a color system for building beautiful websites and apps

Feel free to explore the source code of Radix Themes to see how it is built.

Common Issues

Z-Index Conflicts

Out of the box, portalled Radix Themes components can be nested and stacked in any order without conflicts. For example, you can open a popover that opens a dialog, which in turn opens another popover. They all stack on top of each other in the order they were opened:

TODO: demo

When building your own components, use the following rules to avoid z-index conflicts:

  • Don't use z-index values other than auto, 0, or -1 in rare cases.
  • Render the elements that should stack on top of each other in portals.

Your main content and portalled content are separated by the stacking context that the styles of the root Theme component create. This allows you to stack portalled content on top of the main content without worrying about z-indices.

Tailwind Base Styles

As of Tailwind v3, styles produced by the @tailwind directive are usually appended after any imported CSS, no matter the original import order. In particular, Tailwind's button reset style may interfere with Radix Themes buttons, rendering certain buttons without a background color.

Workarounds:

  • Don't use @tailwind base.
  • Set up separate CSS layers for Tailwind and Radix Themes.
  • Set up postcss-import and manually import Tailwind base styles via @import tailwindcss/base before Radix Themes styles. Example setup.

Missing Styles in Portals

When you render a custom portal in a Radix Themes project, it will naturally appear outside of the root Theme component, which means it won't have access to most of the theme tokens and styles. To fix that, wrap the portal content with another Theme.

Components like Dialog and Popover in Radix Themes already handle this for you, so this is only necessary when creating your own portalled components.

// Implementation example of a custom dialog using the low-level Dialog primitive
// Refer to https://radix.rustforweb.org/primitives/components/dialog.html
use leptos::*;
use radix_leptos_dialog::*;
use radix_leptos_themes::Theme;

#[component]
fn MyCustomDialog() -> impl IntoView {
    view! {
        <Dialog>
            <DialogTrigger>Open</DialogTrigger>
            <DialogPortal>
                <Theme>
                    <DialogOverlay />
                    <DialogContent>
                        <DialogTitle />
                        <DialogDescription />
                        <DialogClose />
                    </DialogContent>
                </Theme>
            </DialogPortal>
        </Dialog>
    }
}

Complex CSS Precedence

Usually, you'd want your custom CSS to override Radix Themes styles. However, there are cases when it is natural to expect the opposite.

Consider a simple paragraph style that just resets the browser's default margin:

.my-paragraph {
    margin: 0;
}

And the styles are imported as follows:

@import '@radix-ui/themes/styles.css';
@import './my-styles.css';

You might apply the margin prop from a Box onto your custom paragraph via as_child:

use leptos::*;
use radix_leptos_dialog::*;

#[component]
fn MyApp() -> impl IntoView {
    view! {
        // TODO
    }
}

Yet, this won't work intuitively. The custom styles are imported after Radix Themes styles, so they will override the margin prop. As a workaround, Radix Themes provides separate tokens.css, components.css, and utilities.css files that the original styles.css is built upon:

@import '@radix-ui/themes/tokens.css';
@import '@radix-ui/themes/components.css';
@import '@radix-ui/themes/utilities.css';

You can import utilities.css after your custom styles to ensure that the layout props work as expected with your custom styles.

If you use standalone layout components, split CSS files are also available for them:

@import '@radix-ui/themes/layout/tokens.css';
@import '@radix-ui/themes/layout/components.css';
@import '@radix-ui/themes/layout/utilities.css';

See Also