Capabilities
Client Library
Color Theme Compliance (Beta)
UI Functions

Using Atlassian Design Tokens in your Power-Up

Whether you are trying to migrate an existing Power-Up or creating a new one, this guide will help you understand how to use Atlassian Design Tokens. Using Atlassian Design Tokens in your Power-Up is the easiest way to make your Power-Up color theme compliant, so your Power-Up can look its best regardless if your user is in light mode or dark mode.

Make sure you're familiar with the Style Guide before moving on.

What are Atlassian Design Tokens?

Atlassian design tokens represent colors that you can use in your css to make your Power-Up color theme compliant. Each token has a name and two colors: the light mode version and the dark mode version.

A list of all the design tokens can be found on the Atlassian Design Tokens reference page.

Color tokens are often named based off of the intent of the component it’s trying to color, not the necessarily actual name of the color. Here are some examples of color tokens:

  1. --ds-text (or color.text in JS syntax) is used for normal text. It’s a dark gray in light mode, but a lighter gray in dark mode.
  2. --ds-link (or color.link in JS syntax) is used for links. It’s a dark blue in light mode, but a light blue in dark mode.
  3. --ds-background-success is used for backgrounds communicating a favorable outcome, such as success message. There is also --ds-background-success-hovered, which is the “hovered” color of this background. Success colors are typically green.

Choosing the Right Token

Here is the Atlassian Design Tokens reference page, which allows you to search for tokens. It also has a “Token Picker” which acts as a decision flowchart to help you land on the right color token for the component you are trying to color.

For best usage, switch the syntax type to “CSS syntax” (instead of JavaScript syntax).

Using the Token in your CSS

Once you’ve chosen the token that you want, it’s off to the races! Let’s say we’re working on a card-back-section, and we’ve chosen these color tokens:

  1. --ds-text (or color.text in JS syntax) for our text color.
  2. --ds-background-neutral (or color.background.neutral in JS syntax) for our background color

Head over to your css file and use the "var” css syntax to use the token.

1
2
/* card-back-section.css */
.my-component {
  color: var(--ds-text);
  background-color: var(--ds-background-neutral);
}

Now, in the HTML of your card back section, make sure your sheet is linked like so:

1
2
<!-- card-back-section.html -->
<html>
  <head>
    <link rel="stylesheet" href="https://p.trellocdn.com/power-up.min.css" />
    <link rel="stylesheet" href="./css/card-back-section.css" />
    <script src="https://p.trellocdn.com/power-up.min.js"></script>
  </head>
  <body>
    <div class="my-component">...</div>
  </body>
  <script src="./js/card-back-section.js"></script>
</html>

And finally, inside your card-back-section.js, make sure to call TrelloPowerUp.iframe(), which will initialize your iframe’s color theme listener, so it stay up to date with the user’s current theme setting.

1
2
// card-back-section.js
const t = window.TrelloPowerUp.iframe();

And you’re done! Now try looking at the your Power-Up’s card back section in both light and dark modes in Trello. In dark mode, the component should turn darker and the text inside turns lighter.

What is this “JS Syntax”?

For the most part, we’ve been using CSS syntax for color tokens, which always start with --ds. However, each token also has a JS syntax, which starts with color. These are used in javascript code to represent color tokens.

How do I get the value of a token in JavaScript?

The "JS Syntax" for tokens is used in JS code together with t.getColorToken and t.getComputedColorToken which are special utility functions that allow you to get the value for the CSS variable or the actual computed value of a token, accordingly. Let's take a look at some examples:

1
2
// card-back-section.js
const t = window.TrelloPowerUp.iframe();

const myElement = document.getElementById('my-element');

const infoBgColor = t.getColorToken(
  'color.background.information',
  'lightblue'
);

// var(--ds-background-information, red)
console.log(infoBgColor);

myElement.style.backgroundColor = infoBgColor;

What if I need the actual computed value of a token and not just the CSS variable?

If you need the actual computed value of a token (i.e. the HEX value for a given token), you can use the t.getComputedColorToken function. Just like the t.getColorToken function, it takes a token name in "JS syntax" and a fallback value as arguments, and returns the actual computed value of the token for the current theme. For example:

1
2
// card-back-section.js
const t = window.TrelloPowerUp.iframe();

const myElement = document.getElementById('my-element');

// infoBgColor will be '#082145' for dark mode and '#E9F2FF' for light mode
const infoBgColor = t.getComputedColorToken(
  'color.background.information',
  'lightblue'
);

myElement.style.backgroundColor = infoBgColor;

For more examples on how to use Atlassian Design Tokens in javascript code, refer to the Atlassian design examples page.

The token function you see in the Atlassian design examples page is analogous to the t.getColorToken function in the example above.

Retrieving the current theme

While developing your Power-Up there may be times where you need to know what the current theme is, for example, if you are using a CSS-in-JS library, you may need to tell it what the current theme is so that it can generate the correct CSS for your Power-Up. To do this, you can use t.getContext() which will return an object containing the theme property indicating the current theme. Here's an example:

1
2
// card-back-section.js
const t = window.TrelloPowerUp.iframe();

...

const theme = t.getContext().theme;

console.log(theme); // null | 'light' | 'dark'

You may notice that the theme property can be null when t.getContext() is called right after the Power-Up has been initialized, this can happen because the theme is still being loaded into your Power-Up. If your use case requires you to know the theme at the time the Power-Up is initialized there's a few things you can do:

  1. If your use case is a one time thing that doesn't need to know when the theme changes and doesn't rely on ADS tokens being available, e.g. you want to show a banner to the user if they are using a specific theme but you don't want spam them with the banner every time the theme changes, then you can use the initialTheme property of the t.getContext() object which will contain the theme that was used to initialize the Power-Up.
  2. If your use case requires you to know the theme at the time the Power-Up is initialized and you need to know when the theme changes, then it is important that you use the t.subscribeToThemeChanges function to subscribe to theme changes so that your Power-Up can react accordingly when the theme is loaded. See the section below for more information.

Ensuring your Power-Up JS Code is always in sync with Trello's theme

Whether you need to tell your CSS-in-JS library what Trello's current theme is or you simply want to get the computed value for a given token, it is always a good idea to make sure your Power-Up is always in sync with Trello's theme by making use of the t.subscribeToThemeChanges function, this function allows your Power-Up to stay in sync with Trello's theme in case a user has set his prefered theme as auto which the theme may change while the user is active on Trello and potentially interacting with your Power-Up.

The t.subscribeToThemeChanges utility function takes a callback as an argument and will return a function that you can call to unsubscribe from theme changes. Here's an example:

1
2
// card-back-section.js
const t = window.TrelloPowerUp.iframe();

const myElement = document.getElementById('my-element');

const unsubThemeChangeListener = t.subscribeToThemeChanges((theme) => {
  console.log('Recalculating color.background.information for theme: ', theme);

  const infoBgColor = t.getComputedColorToken(
    'color.background.information',
    'lightblue'
  );

  myElement.style.backgroundColor = infoBgColor;
});

...

// After we're done listening to theme changes, we can unsubscribe
unsubThemeChangeListener();

Now that's a very basic example, so, for a more realistic one we can use React to look at how to keep our app up to date with Trello's theme:

1
2
// React >=18
import { createContext, useSyncExternalStore, useContext } from 'react';

const t = window.TrelloPowerUp.iframe({
  // Some config ...
});

const ThemeContext = createContext();

const subscribe = (cb) => {
  const unsubThemeChangeListener = t.subscribeToThemeChanges(cb);

  return () => {
    unsubThemeChangeListener();
  };
};

const getSnapshot = () => t.getContext().theme;

const ThemeProvider = ({ children }) => {
  const theme = useSyncExternalStore(subscribe, getSnapshot);

  // By "re-exporting" and consuming the token functions from
  // the theme context, we can ensure that the functions will
  // be re-run whenever the theme changes
  const value = {
    theme,
    getColorToken: t.getColorToken,
    getComputedColorToken: t.getComputedColorToken,
  };

  return <ThemeContext.Provider
value={value}>{children}</ThemeContext.Provider>;
};

const useTheme = () => {
  const context = useContext(ThemeContext);

  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }

  return context;
};

const SomeComponent = () => {
  const { theme, getComputedColorToken } = useTheme();

  return (
  <div>
    <h1>
      The computed value for the <code>color.background.information<code> token
in {theme} mode is: {getComputedColorToken('color.background.information',
'lightblue')}
    </h1>
  </div>
  );
};

const App = () => {
  return (
    <ThemeProvider>
      <SomeComponent />
    </ThemeProvider>
  );
};

For React versions 17.X.X and below you can replace the useSyncExternalStore hook with a useState and an useEffect hook:

1
2
// In ThemeProvider
const [theme, setTheme] = useState(t.getContext().theme);

useEffect(() => {
  const unsubThemeChangeListener = t.subscribeToThemeChanges((theme) =>
    setTheme(theme)
  );

  return () => {
    unsubThemeChangeListener();
  };
}, []);

Getting rid of theme flashing

In rare cases you may be able to perceive a "flash" of the old Trello theme before one of the new ADS themes is applied. This is due to the asynchronous nature of how we load themes which makes it impossible to guarantee that the new theme will be applied before the browser paints the contents of your Power-Up, even if you call TrelloPowerUp.iframe() in a <script> within the <head> of your Power-Up's HTML file.

If all you're seeing is t.getComputedColorToken returning the wrong value (fallback), then please make sure you're using the t.subscribeToThemeChanges function as described in Ensuring your Power-Up JS Code is always in sync with Trello's theme.

To get rid of the theme flashing you'll need to consider that you will be trading off initial bundle size for a better user experience. Right now there is only 2 ADS themes available, totalling ~4.1kB so while the trade off may seem small right now it may not be in the future, that being said, if you're willing to make the trade off, all you need to do is add the following to your Power-Up's HTML file:

1
2
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
      <!-- 
        1. We import the atlaskit themes stylesheet to make sure the tokens are 
          loaded before the browser paints the content 
      !-->
    <link
      rel="stylesheet"
      href="https://p.trellocdn.com/@atlaskit-themes.min.css"
    />
    <link rel="stylesheet" href="https://p.trellocdn.com/power-up.min.css" />
      <!-- 
        2. Make sure to import the power-up.min.js script before trying to 
          initialize the iframe class  
      !-->
    <script src="https://p.trellocdn.com/power-up.min.js"></script>
      <!--
        3. By initializing the iframe class here we make sure the html data 
           attributes required for themes to work are set before the browser 
           paints the iframe's content.

           Note: You don't need to call TrelloPowerUp.iframe() again in your
           script files, you can just use the t variable defined here but if you
           prefer to do so you can do it without having to pass the config
           object again.
      !-->
    <script>
      window.t = TrelloPowerUp.iframe({
        // Some config...
      });
    </script>
  </head>
  <body>
    <script src="my-script.js"></script>
  </body>
</html>

Opting out of loading Atlassian Design Tokens for your Power-Up

If for some reason you don't want to load Atlassian Design Tokens in your Power-Up, you can opt out of loading them by setting the useADSTokens option to false when initializing your Power-Up iframe:

If you need to listen to Trello's theme changes, you can still use the t.subscribeToThemeChanges function as described in Ensuring your Power-Up JS Code is always in sync with Trello's theme.

1
2
const t = window.TrelloPowerUp.iframe({
  useADSTokens: false,
  // Rest of the config ...
});

By opting out of using Atlassian Design Tokens no CSS variables will be loaded into your iframe's document, and so the t.getColorToken and t.getComputedColorToken functions will no longer work.

Rate this page: