Last updated Nov 29, 2023

Prepare your Data Center app for the dark theme

This guide will help you prepare a Data Center app for correct rendering in dark theme.

Overview

Theming in Data Center products is supported with the help of design tokens. Design tokens are CSS variables that represent design decisions like elevation, spacing, or — most importantly for the current purpose — colors.

Learn more about design tokens

Data Center products with support for light and dark themes

Currently, Bitbucket 8.16 is the first and only publicly available Data Center product with full support for light and dark themes.

Support for the new themes in the first Early Access Program (EAP) releases of Jira Software 10.0 and Jira Service Management 6.0 is only partially implemented. However, we still encourage you to follow the instructions in this guide to prepare your Data Center app for the upcoming changes.

We're also working on bringing support for light and dark themes to Confluence 9.0 and Bamboo 10.0.

Before you begin

We assume that:

Background

Here are some topics relevant to theming in Atlassian products. The section describing what themes should your app support is essential. You can skip the others on first reading.

What themes should my app support?

Products will allow switching between three themes: Original, Light, and Dark.

Original theme has the same colors as before the conversion. Teams may want to continue to use this as they wait for all apps in their environment to transition to the design token themes. Additionally, some users are resistant to change, and this option will ease the pain of the transition. As a transitional measure, original theme will be removed at some point after the release of theming.

Light and Dark themes use design tokens. In fact, any given place uses a particular token that has one value in the Light theme and another value in the Dark theme.

The theme switcher will have the fourth option — Match system. It will enable either Light or Dark theme depending on the user’s browser reports.

Let’s consider an example:

CSSJavaScript
1
2
.info-field {
  color: var(--ds-text, black);
}
1
2
import { token } from '@atlaskit/tokens';
const infoField = css({
  color: token('color.text', 'black')
})

In this example, --ds-text and 'color.text' represent the same token. Learn more about available tokens

How is theme switching implemented?

In short, Data Center products include the Atlassian User Interface library (AUI) which provides the CSS variable definitions for each token.

In general, the details shouldn’t matter to an app developer. What matters is that you define your UI’s colors in terms of the design tokens and have escape hatches to handle more involved scenarios. Loading the correct theme will be handled by the product.

However, if your app can be run in a standalone mode for testing, independent of a product, you might want to implement a theme switcher inside this mode.

If this is the case, you’ll have to decide which path to follow:

What is the current theme?

Sometimes your code will need to apply different styles, most commonly, it occurs when there are two different images that represent the same thing (for example, a screenshot of a particular piece of functionality).

It's best to build for whole color modes (light or dark) rather than specific themes, because there can be multiple themes per color mode.

During the initial render

Check the data-color-mode attribute on the html element.

CSSJavaScript
1
2
/* Original and Light */
.panel {
  background-image: url(images/bg-light.png);
}
/* Dark */
html[data-color-mode='dark'] .panel {
  background-image: url(images/bg-dark.png);
}
1
2
const colorMode = document.documentElement.getAttribute('data-color-mode')
if (colorMode === 'light') {
  // Light mode
} else if (colorMode === 'dark') {
  // Dark mode
}

In some cases, you might also want to check the specific theme being rendered to cater for it. The data-theme attribute on the html element can be used. Learn more about the HTML API

React to the theme change

In certain cases — for example, when rendering something on a Canvas or replacing an image rendered with JavaScript — you might need to track when a theme is changed.

To do this, use ThemeMutationObserver as in this example:

1
2
import { ThemeMutationObserver } from '@atlaskit/tokens';

const observer = new ThemeMutationObserver((newTheme) => {
  console.log(newTheme.colorMode);
});

observer.observe();
observer.disconnect();

Learn more about the ThemeMutationObserver function

The adaptation process at a glance

The process of adapting your app to Dark theme looks like this:

  1. Include the tokens library.
  2. Make sure the Atlassian (Atlaskit and AUI) components you include are using design tokens.
  3. Make sure colors in your app are using design tokens: in code and in images.
  4. Test your changes.
  5. Introduce lint rules.

Depending on your app’s functionality, some steps won’t be applicable, or there might be more steps in your specific case. We trust your judgment.

Step-by-step instructions for updating your app

Use the tokens library

Add the library to your project:

1
2
npm install @atlaskit/tokens

You can skip this step if your app’s colors are defined only in CSS files. In this case, you’ll use only the direct var(--ds-text, black) form, which doesn’t require installing the package.

Update Atlassian components

If you include any Atlaskit components, make sure they are on a version published in October 2022 or later.

If you bundle any AUI components, make sure the AUI version is 9.9.0 or later. Otherwise you can use the version provided by the products (web-resource).

Migrate your app colors: CSS and JavaScript

What we want to achieve

Each color that your app defines should have one of these forms:

  • In CSS files, the var(<CSS syntax>, <fallback>) form:
1
2
.info-field {
  color: var(--ds-text, black);
}
  • In JavaScript or TypeScript files, the token('<JavaScript syntax>', <fallback>) form:
1
2
// Make sure you import the token function
import { token } from '@atlaskit/tokens';
const infoField = css({
  color: token('color.text', 'black')
});

Learn more about available tokens

While it is possible to use the CSS syntax in JavaScript files (for example, const infoField = css({ color: 'var(--ds-text, black)' })), the token() form is preferred because it warns you if a non-existent token is passed in and gracefully handles version mismatches between @atlaskit/tokens and AUI.

Step 1: Apply codemods

To kick-start the process, we suggest using the codemods. Learn more about the @atlaskit/codemod-cli package

The package is a general one for all our codemod needs, but the two specific presets (css-to-design-tokens and theme-to-design-tokens) apply to the dark theme migration. They search for hard-coded colors and replace them with token-using expressions.

The replacements are done in place, overwriting the original files. We assume you store your app’s code in a version control system and thus can see the modifications easily and revert them if needed.

Run these commands in your app’s folder:

1
2
# process the CSS-syntax files
npx @atlaskit/codemod-cli -n css-to-design-tokens -e css,scss,less .

# process the JavaScript-syntax files
npx @atlaskit/codemod-cli -n theme-to-design-tokens -p tsx -e ts,tsx,js,jsx .

Step 2: Review changes for proper meanings

Don’t blindly trust the codemods! Some heuristics are applied to come up with the suggestions, but they don’t have enough context to be correct every time. The following are some traps to watch for.

There are tokens that have the same value but different names (for example, --ds-text and --ds-icon-warning-inverse are both Neutral1000 in the Light theme). Pick the one that most closely matches the intended use. When in doubt, use the Token picker. Refer to the available tokens page

What if there’s no proper token for the intended meaning?

Check out the Get help section

The codemod treats any objects in JavaScript as potentially CSS-in-JS. If you have objects that happen to have CSS-like properties, such as { color: 'red' }, but those aren’t actually passed to a CSS-in-JS compiler, the codemod will still convert the color expressions. You should revert those changes.

In rare cases, the codemod script won’t be able to propose any substitution and will put the "MISSING_TOKEN" value in the code. In those places, you should pick the right replacement value. The Token picker can also help to make the decision.

Migrate images

If your app uses images, you should do some work for them to look well in both themes. Depending on the image type, the approach will differ. The following approaches are the most common ones.

Review icon fonts or inline SVG

Icons implemented as an icon font most likely won’t require rework. Probably, they already have associated CSS rules that are covered before in the “Migrate your app colors” step.

If you define inline SVG images in JSX files, they will also be effectively handled during the color migration. SVG images that are styled with CSS don't require rework either, because the CSS declarations are changed by codemods.

Migrate images in files

Images (PNG, JPG, SVG, and other types) stored as files are more difficult. For each of these images, you can pick an option:

Migrate dynamically-created images (for example, rendered on Canvas)

Use the colors that fit the current theme. You can find more information in the "Background: What is the current theme?" section.

Test your changes visually

Use your regular procedures for testing visual changes. We assume that you’re using a version of our products with support for theming, which provide a UI toggle to switch between themes.

For example, here's what switching between themes looks like in Bitbucket Data Center:

Switch between themes in Bitbucket Data Center

For all the replaced colors, navigate to the corresponding pages and check if they look fine in all three themes: Original, Light, and Dark.

The following sections include specific suggestions for testing your changes.

Automate switching themes with a REST API

The REST API that theme switcher uses under the hood can be reused. See the Atlassian Theme REST API

Check color contrast

Use a browser extension (like axe DevTools) to check color contrast. If you notice an issue, make sure the replaced color is semantically correct for the place. If it is, let us know, and we will try to fix it. For details on how to contact us, check out the Get help section.

Use AUI testing theme

If you use AUI, you might want to use the testing theme. Check out the AUI dark theme documentation

You can enable it in the console of your browser’s developer tools, and it will replace all design tokens with a single color, making everything that’s not using them stand out. This can catch the cases that the codemods missed.

Introduce lint rules

For better maintainability of your app, add these lint rules into your project to warn about colors that aren’t using a token form:

Your app is updated!

Now that your app supports theming:

  • When included in a product that doesn’t have themes yet, the fallback values will ensure it keeps looking as before.
  • When included in a product that has themes, it will look properly in the Original, Light, and Dark themes.

Get help

If you have questions or need help, contact us at The Atlassian Developer Community.

Make sure to include the tag design-tokens.

We’ll do our best to refine the solution based on your feedback.

Rate this page: