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.
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:
--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.--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.--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.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).
Prefer to look at a practical example? Check out our sample glitch project showcasing color theme compliance in Power-Ups! You may need to remix the project in order to view it.
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:
--ds-text
(or color.text in JS syntax) for our text color.--ds-background-neutral
(or color.background.neutral in JS syntax) for our
background colorHead 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.
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.
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;
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.
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:
initialTheme
property of the
t.getContext()
object which will contain the theme that was used to
initialize the Power-Up.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.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(); }; }, []);
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>
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 2const 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: