How to Customize the Look and Feel of Payload with CSS

Custom CSS

One of the hidden gems in Payload is its Custom CSS option.

Its potential is completely limitless and lets you design / customize the appearance of every element of the Payload dashboard in a way that better represents your brand.

Styling a CMS can be a pain and is usually limited. While the defaults out of the box are great, some projects deserve more finesse. Payload gives you that freedom.

Our Custom CSS feature is a poster child for this approach. Wanna tweak the platform’s look? Go for it. Turn it into a kaleidoscope if you want (please don’t)... Because at the end of the day, your CMS should be a seamless extension of your brand—bonus points for making your marketing team happy.

Block, Element, Modifier (BEM) Naming convention

The entirety of the Payload admin UI utilizes the BEM (Block, Element, Modifier) naming convention. This widespread approach mitigates the usual challenges posed by CSS’s cascading nature, making your CSS more manageable and scalable. It also maintains a reliable selector convention, ensuring that they won’t unpredictably change on you.

Demo

The first step involves providing your base Payload config with a path to your custom stylesheet, ensuring that Payload can recognize and apply your custom styles.

import { buildConfig } from 'payload/config';
import path from 'path';
const config = buildConfig({
admin: {
css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
},
});

Once you’ve set the path, you’ll need to add your stylesheet to your directory. This will serve as the space where you define your custom styles.

First up, we’ve got a snapshot of our standard Payload Admin UI - unopinionated out of the box but highly customizable.

Before styles

The image below showcases the same UI, now elegantly enhanced with some custom CSS. I’ve updated a few small things like fonts and colors to maybe match an existing brand more closely. With minimal changes, you can see the admin panel take on a different look and feel.

After styles

How did I do this? Starting with the browser's inspector tool, I pinpointed elements I planned to modify and inserted the necessary classes and styles into my custom stylesheet.

I took a hard look at the UI’s current color theme. It was doing the job, but it wasn’t in sync with the brand image I was after. So, I changed it. By adjusting a few css variables, I’m able to change the look and feel of the dashboard to match any brand.

Swapping out the font styles is easy, you simply just need to set them in your css file. Finally, I made a few adjustments to the colors of the button and the banner backgrounds, creating a more prominent contrast.

@import '~payload/scss';
html {
&[data-theme=dark] {
--theme-elevation-0: rgb(24, 20, 48);
--theme-elevation-50: rgb(37, 33, 61);
--theme-elevation-100: rgb(50, 46, 74);
--theme-elevation-150: rgb(63, 59, 87);
--theme-elevation-200: rgb(76, 72, 100);
--theme-elevation-250: rgb(89, 85, 113);
--theme-elevation-300: rgb(102, 98, 126);
--theme-elevation-350: rgb(115, 111, 139);
--theme-elevation-400: rgb(128, 124, 152);
--theme-elevation-450: rgb(141, 137, 165);
--theme-elevation-550: rgb(154, 150, 178);
--theme-elevation-600: rgb(167, 163, 191);
--theme-elevation-650: rgb(180, 176, 204);
--theme-elevation-700: rgb(193, 189, 217);
--theme-elevation-750: rgb(206, 202, 230);
--theme-elevation-800: rgb(219, 215, 243);
--theme-elevation-850: rgb(232, 228, 255);
--theme-elevation-900: rgb(255, 255, 255);
--theme-elevation-950: rgb(255, 255, 255);
--theme-elevation-1000: rgb(255, 255, 255);
--theme-bg: rgb(24, 20, 48);
}
}
body, h2, h5 {
font-family: monospace;
font-weight: bold;
}
.btn {
&--style-primary {
background-color: rgb(0, 213, 110);
color: var(--theme-elevation-900);
}
&--style-primary.btn--disabled {
background-color: rgb(102, 255, 212);
}
&--disabled {
background-color: rgb(102, 255, 212);
}
}
.login .logo path {
fill: rgb(0, 213, 110);
}
.template-default {
.nav {
&__brand .icon path {
fill: rgb(0, 213, 110);
}
}
.before-dashboard {
.banner {
// BEM modifier selector (.banner--type-success)
&--type-success {
background: rgb(0, 213, 110);
color: white;
}
// BEM element selector (.banner__content)
&__content svg {
background-color: #2A2544;
}
}
}
}

A quick note: see how we are nesting the targeted elements inside its parents selectors, this is what allows us to override their styles. CSS specificity is key here, it gives you complete control of the styles.

For instance, consider our button styles. By employing a combined class selector, .btn--style-primary.btn--disabled, we increased specificity to target only the disabled primary buttons—a testament to the power of CSS specificity used in tandem with BEM.

If you want to target something else with more specificity, I recommend first inspecting the DOM. This helps you identify the selectors currently controlling the style. With this knowledge, you can craft a more specific selector to accurately target and style your desired elements, utilizing CSS specificity to your advantage.

Wrap up

The Custom CSS feature in Payload extends beyond being a simple customization tool–it’s a pathway to enrich UX and seamlessly integrate your CMS with your brand aesthetics.

We invite you to explore this feature, adapt it, and truly make it your own!

Learn More

To learn more about Payload and Custom CSS, take a look at the following resources:

Like what we're doing? Give us a star on GitHub

We're trying to change the CMS status quo by delivering editors with a great experience, but first and foremost, giving developers a CMS that they don't absolutely despise working with. All of our new features are meant to be extensible and work simply and sanely.