Netlify Create /

Global styles

Enable content editors to set site-wide styling options in Netlify Create.

"Global styles" isn't a hard-coded feature - it is a popular pattern you can implement to allow content editors control of site-wide visual styling, from a centralized configuration.

Modifying colors via a global styles content object.

# How it works

Here's a high-level overview of implementation. A concrete example follows below.

  1. First, define a content model with fields for all site-wide styling options you want editors to control. We offer various field types suited for editing visual properties (e.g. color fields and customized enum fields).
  2. Create one instance (i.e. one content item) of this model type to hold current values.
  3. Add a sidebar button for quick access to this content object (by its model name or its content ID).
  4. The final step differs by your CSS framework and the CMS of choice:
  5. Tweak the configuration file of the CSS framework you're using (e.g. tailwind.config.js) to read the values in the content object, rather than hard-coded values.
  6. If you're using Git CMS, the framework's config file can read the content file directly.
  7. If you're using an API-based headless CMS, a piece of code should be added to write up-to-date style values from CMS to a local file - making it readable from the framework's config file.

The key to implementing global styles is a JavaScript-based CSS pipeline, where a content file (JSON, Markdown, etc.) can be imported into a main CSS configuration file that is used to set global style rules.

While you can roll your own DIY solution for this, note that nowadays the major CSS frameworks like Tailwind CSS (and MUI, Chakra, etc.) already work this way.

# Example with Tailwind CSS

Assume a content file that has editor-configurable values. We'll get to how this file is created in a minute, depending on your content source of choice.

{
  "primaryColor": "#f6f1ed"
  // ...
}

Tailwind uses a tailwind.config.js file to set default values for your styles. Here's a simple example of reading a value from your content file into Tailwind's configuration:

// tailwind.config.js
const globalStyles = require("./content/data/style.json");

module.exports = {
  theme: {
    colors: {
      primary: globalStyles.primaryColor
      // ...
    }
  }
  // ...
};

Now that you've added a custom color to your Tailwind theme, this color is immediately usable with Tailwind utility classes, e.g. text-primary or bg-primary.

You can also use Tailwind's @apply directive to apply these classes to other CSS classes:

.button {
  @apply text-primary;
}

# The global styles content model

This model can be designed just like any other model with Netlify Create, with the nuances for particular content sources noted below.

# Use files as content source

If using Git CMS (file-based content), define the content model as a JS/TS object.

Here's an example model definition for allowing an editor to switch between light & dark modes, plus specifying primary and secondary colors:

// .stackbit/models/GlobalsStyles.js
export const GlobalStyles = {
  type: "data",
  label: "Global styles",
  file: "style.json",
  fields: [
    {
      type: "enum",
      name: "mode",
      controlType: "button-group",
      options: [
        { label: "Light", value: "light" },
        { label: "Dark", value: "dark" }
      ],
      default: "light"
    },
    { type: "color", name: "primaryColor", label: "Primary color" },
    { type: "color", name: "secondaryColor", label: "Secondary color" }
  ]
};

This model should be added to the models array property in your configuration file:

// stackbit.config.ts
// imports ...
import { GlobalStyles } from "./.stackbit/models/ThemeStyle";

const sbConfig = {
  stackbitVersion: "~0.6.0",
  contentSources: [
    new GitContentSource({
      rootPath: __dirname,
      contentDirs: ["content"],
      models: [
        GlobalStyles
        // other models ...
      ]
    })
  ]
};

export default sbConfig;

Note the following in the model definition above:

  1. The model type is set to data (meaning non-page standalone content).
  2. The file property is set to a fixed location where this data is found, as we only want to have a single instance of this model. Having a known location makes it easy to load the file in your CSS configuration code (see tailwind.config.js example above).

Typically, sites running in development mode also listen for any file changes, so any updates made to the file will immediately result in a hot module reloading - including the CSS framework's configuration code being re-run and grabbing up-to-date values from file.

In production, the latest version of the file is statically included in each deployment.

For a complete example that you can explore and run locally, see our Typescript starter.

# Use a headless CMS

With a headless CMS, the model can be defined just like any other model in your CMS, but there is a bit of extra code needed to synchronize the up-to-date values in the content object to a file:

  1. Add a helper function to fetch the global styles content object from the CMS and write it to a local file with a fixed name.
  2. For your production build, make this code run once as part of the build process, so the resulting file is baked into the deployment. Any further style changes by editors will not be reflected in the production site until the changes are published & the site redeployed.
  3. When your site runs in development mode (which is also how we run your code in a managed Netlify Create project), add a listener to content changes in your site, so that any change to the style values by anyone will trigger writing the local file again. Each CMS has its own mechanism for receiving content updates which you can use.

If your CMS supports it, it's recommended to make the model a singleton model so that only one instance can ever be created.

# Add a sidebar button for styles

For quick access to the styles content object, you can add a button to the left sidebar in your configuration file:

// stackbit.config.js

export default {
  // other config properties ...
  sidebarButtons: [
    {
      type: "model",
      label: "Global styles",
      icon: "style",
      modelName: "GlobalStyles"
    }
  ]
};