3. Integration
📖 Articles about token integration
- How to manage your Design Tokens with Style Dictionary - Cristiano Rastelli
This section will explain how different platforms and languages handle styling and how design tokens can integrate into them. By the end of it each platform (Android, iOS, Web) will have a demo app that imports our design token package into it and uses it. To get started, check out the step-3
branch:
git fetch && git checkout step-3
Or if you haven't checked out the repository yet:
git clone -b step-3 https://github.com/dbanksdesign/clarity-2020-design-token-workshop.git
This will create android, ios, and web folders with demo apps in them.
3.1 Overview
📦 Package Managers
Each platform has one or many package managers, like NPM for web packages. What we will be doing is creating a package that looks like package for each different package manager. That way a single package can be depended on by different platforms.
Are there other ways of publishing your design tokens so that application codebases can consume it? Sure. You could generate code files and store them on S3 and then each application writes a pre-build script to grab that code and include it. Or leave it up to each application and platform to deal with how to
3.2 Web integration
There are A LOT of ways to use design tokens in the web. The most common is to output CSS variables or a pre-processor of CSS like Sass (SCSS), Stylus, or Less. You can also use design tokens in Javascript if you are using a charting library like D3 and want to use your design tokens to style visualizations. Or you could also use it in Javascript-based styling, e.g. CSS-in-JS frameworks like Emotion.
Demo/documentation app
To speed things up, I started a design systems documentation website written in Gatsby and React. In this step we will integrate our design tokens into it. I chose React and Gatsby purely for my familiarity with the two and time constraints. If I had infinite time I would have a demo app for all major front-end frameworks and static site generators like Vue, Angular, Svelte, NextJS, 11ty, etc.
It really is up to how you want to architect your web and documentation code. Monorepos are pretty hot right now and work incredibly well with a design token package. Or you could have separate repositories/packages and have your web app and documentation site depend on your design token package.
cd web/demonpm ci
npm start
Let's take a look at the web/demo folder with some Gatsby code in it.
- web/demo/src/components
- web/demo/src/pages: I chose MDX for the pages in this demo doc site for ease of use
- web/demo/src/styles: I chose e
Note: I purposefully chose different methods for styling components to see different ways to integrate into a web app. You should probably just use one of these methods.
Terminate the process for now by pressing ctrl+c, we will come back to this in a bit.
Let's start by creating some files to use in our web demo. Open up sd.config.js and first let's change the buildPath
of our web platform to web/dist/
. This is where we will output all of our web artifacts to from now on.
We are already generating an SCSS variables file, but what if we need our design tokens in Javascript? Let's add a new platform in our config and call it "js". Give it a transformGroup of "js", buildPath of "web/dist/".
Then add a single file in the files array with the destination of "index.js" and format of "javascript/es6". The js platform in the config should look like this:
js: {transformGroup: `js`,buildPath: `web/dist/`,files: [{destination: `index.js`,format: `javascript/es6`}]}
Platform? What's in a name?
Technically a platform in the config can be anything, not just "web", "android", and "ios". A platform defines the transforms you want to perform and the files you want to build from it. We will be adding more platforms to the configuration soon.Let's run npm run build
and take a look at this new file web/dist/index.js.
Now we are going to do is depend on our tokens package. But before that we need to get our token package ready first. Open up the package.json file in the root of the package, and update the "main" attribute and add a "files" array to look like this:
"main": "web/dist/index.js","files": ["web/dist"],
This tells NPM to only include files in the web/dist directory when publishing to NPM. This way we don't get circular references if we want to depend on this package in our web/demo directory.
Note: this is easier with Yarn workspaces and/or Lerna as a Monorepo.
Now we can create a global npm "link" of our design token package.
npm link
This will install our design token package globally on your system. So any NPM module package on your computer can add it as a dependency. It symlinks the files in this package with the global NPM package install folder so any changes you make here will be available to all other packages. This is where the "files" declaration in the package.json file is important, so we don't symlink all the files in this whole repository.
Now we "install" the linked package in our demo npm package:
cd web/demonpm link clarity-design-tokens
Now we should have access to our design token package just like any other npm dependency!
CSS, Sass, Stylus, and Less
I chose SCSS for this project because it is what I'm the most familiar with, but this should work if you are using Stylus or Less as well.
Open up src/components/Button/Button.scss now let's integrate with our design tokens! First change the import to import the variables from our design token package:
@import "clarity-design-tokens/web/dist/variables.scss";
Then we can go and replace a lot of these styles with the button design tokens we built in the last step. By the end it should look like:
@import "clarity-design-tokens/web/dist/variables.scss";.clarity-button {padding: $component-button-padding;border-radius: $component-button-border-radius;color: $component-button-color;&.primary {background-color: $component-button-primary-background-color;color: $component-button-primary-color;}&.outline {border: $component-button-outline-border-width solid $component-button-outline-border-color;}}
But wait, we can add some :hover
and :active
styles. Open up our button design tokens file tokens/component/button.json
{"component": {"button": {"font-size": { "value": "{size.font.small.value}" },"padding": { "value": "{size.padding.medium.value}" },"color": { "value": "{color.font.interactive.value}" },"border-radius": { "value": "{size.border.radius.small.value}" },"primary": {"background-color": { "value": "{color.brand.primary.value}" },"color": { "value": "{color.font.inverse.value}" },"hover": {"background-color": { "value": "{color.brand.primary-dark.value}" }}},"outline": {"border-width": { "value": "{size.border.width.medium.value}" },"border-color": { "value": "{color.border.primary.value}" },"hover": {"border-color": { "value": "{color.brand.primary-dark.value}" }}}}}}
@import "clarity-design-tokens/web/dist/variables.scss";.clarity-button {padding: $component-button-padding;border-radius: $component-button-border-radius;color: $component-button-color;&.primary {background-color: $component-button-primary-background-color;color: $component-button-primary-color;&:hover {background-color: $component-button-primary-hover-background-color;}}&.outline {border: $component-button-outline-border-width solid $component-button-outline-border-color;&:hover {border-color: $component-button-outline-hover-border-color;}}}
Now for the purposes of this demo and showing other integrations, the Badge component is using styled-components for its styling. Open up src/components/Badge/Badge.js
Javascript
The 2 main reasons you would need design tokens in Javascript are: you are using a data visualization library that is styled in Javascript (like D3), or you are using a CSS-in-JS solution for styling. Personally, I am not that familiar with CSS-in-JS libraries, but it looks like styled-components and emotion are 2 popular ones. For purposes of this demo I chose styled-components arbitrarily. The technique used here should work for any CSS-in-JS library.
npm install case --save-dev
const Case = require('case');module.exports = {type: 'name',transformer: (token) => Case.camel(token.path.slice(2))}
jsComponent: {transforms: [`attribute/cti`,`nameJsComponent`,`size/rem`,`color/css`],buildPath: `web/dist/`,files: [{destination: `badge.js`,format: `javascript/es6`,filter: (token) => token.path[0] === 'component'&& token.path[1] === 'badge'}]}
import * as tokens from 'clarity-design-tokens/web/dist/badge'
Now we should be able to delete this other token object, save it and see our fancy badges, now in color!
While this is in the "web" integration section, because we can generate Javascript (and Javascript natively understands JSON), we can use this for React Native as well. React Native uses plain Javascript objects for styling which fits perfectly.
3.2 Android integration
If you would like to build the Android demo app, install Android Studio and install SDK 29.
Demo App
I have a demo Android app already built so that we don't have to go through all the steps to set one up. Take a look around at how Android apps are built and styled so that we can get an understanding for how design tokens are integrated into Android apps. The Android demo app is located in android/demo.
If you have Android Studio installed, open it and choose "open Android Studio project" and select the android/demo folder in the repository. After it does some working, you should be able to press the play button to build the demo app for an emulator. If you don't have any emulators installed yet, you can click the AVD manager button which looks like a phone with the android logo at the bottom. If you don't have Android studio, you can take a look at the files in VSCode or another IDE.
- android/claritydesigntokens: This is where we will generate our code from our design tokens, this is our Android library we are building.
- android/demo: This is where all the demo app code lives.
- android/build.gradle: Android projects use Gradle as their build system and dependency manager. Gradle is like the NPM for Android. And build.gradle is like the package.json of Android. Technically there are 3 build.gradle files in here because one is for the entire project, one is for the application module, and one is four our library module. It's kind of like having a Monorepo in NPM, you would have multiple package.json files, one for each module.
- android/demo/src/main/res: This is where all the resource XML files are. Take a look in the values directory, this is where all the design tokens are. This area of Android apps is where we will focus on.
- android/demo/app/src/main/java: This is where all the code lives. Android projects can be written in Java or Kotlin. I chose Java because I am more familiar with it, but Kotlin is the new hotness.
Android library
An Android Library is basically the same thing as an Android app, they share the same directory and file structure. This makes it really simple to create an Android library that shares styling like what we are doing with our design token package. The one thing to note here is that Android merges all resources together with the application taking the highest precedence. This means that if you define a color resource with the name "color_font_primary" in both an Android library that the app depends on, and in the app resources, the app resource will override the library resource.
Design tokens actually work great in Android because the Android platform has a built-in notion of resources which are basically just like design tokens but in XML form. You can think of resources as typed CSS or Sass variables. Take a look at android/demo/src/main/res/values/colors.xml file, you should see something like this:
<?xml version="1.0" encoding="utf-8"?><resources><color name="color_background_primary">#ffffffff</color><color name="color_background_secondary">#fff3f4f4</color><color name="color_background_tertiary">#ffdee1e1</color></resources>
You can then use these resources in Layouts which are like HTML files. You describe your interface as a series of nested components which could be other layouts, Java classes, or Android components. Take a look at android/demo/src/main/res/layouts/fragment_home.xml which should look like this:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center_horizontal"tools:context=".HomeFragment"><TextViewandroid:id="@+id/text_home"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="@dimen/size_padding_large"android:textAlignment="center"android:textSize="@dimen/size_font_large"android:textColor="@color/color_font_primary"android:text="@string/home_fragment_welcome" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:contentDescription="logo"android:layout_marginVertical="@dimen/size_padding_small"app:srcCompat="@drawable/logo" /></LinearLayout>
I want to spend a bit of time on Android and how it handles styling because I think it ties in with design tokens very well.
Android styles and themes
Styles and themes in Android are interesting concepts and have a lot of overlap in design tokens. Styles and themes are resources defined in XML as well. A style is a collection of key-value pairs that map attributes to resources.
A style in Android is a collection of traits or properties that apply to a specific view. A theme is a collection of attributes applied to an entire app. A theme is actually like a theme layer in the 3 layers of design token structure we saw in the previous section. Open up android/demo/main/src/res/values/styles.xml and you should see some styles that look like this:
<?xml version="1.0" encoding="utf-8"?><resources><style name="Widget.App.Chip" parent="Widget.MaterialComponents.Chip.Entry"><item name="chipIconTint">@color/component_badge_color</item><item name="chipBackgroundColor">@color/component_badge_background_color</item><item name="chipStrokeWidth">0dp</item><item name="closeIconTint">@color/component_badge_color</item><item name="android:textColor">@color/component_badge_color</item><item name="checkedIconVisible">false</item><item name="shapeAppearanceOverlay">@null</item></style></resources>
We can then apply that style to a view:
<com.google.android.material.chip.Chipandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:closeIconVisible="false"style="@style/Widget.App.Chip"android:text="Default" />
A style is like a mixin in Sass, it allows you to define a re-usable set of styles.
Both styles and themes can inherit (or extend) from a parent style or theme.
Technically, both styles and themes use the <style>
XML tag. I know, kinda confusing, way to go Android. They differ in how they are used. A style is used with the style
attribute in an XML file on a view like above. A theme is applied to a android:theme
attribute gets applied to all child views. Here are some resources about themes and styles:
- exhaustive list of everything you can define in a theme
- Android guide on themes
- Here is are styles for the material badge
Now that we have a general lay of the land, let's start building some Android resource files from our design tokens!
You could also generate a Java or Kotlin file with some static strings and numbers on it and then use it code in your application. Although that is generally not how Android apps are built. And if you remember that large text above: design tokens should be integrated into platform conventions so that developers can interact with the tokens how they would normally style an app.
Let's start by adding an Android platform in our sd.config.js file. Give it a transformGroup of "android", a buildPath of "android/claritydesigntokens/src/main/res/values/"
android: {transformGroup: "android",buildPath: "android/claritydesigntokens/src/main/res/values/",},
Now let's add the files we want to build. Generally Android groups resources by type, so all colors go in a single resource file. Technically you don't need to do this, but we will follow it for Android's sake.
files: [{destination: "design_token_colors.xml",format: "android/colors"},{destination: "design_token_font_dimens.xml",format: "android/fontDimens"},{destination: "design_token_dimens.xml",format: "android/dimens"}]
Really the file names don't matter. Android merges all resource files together, kine of like Style Dictionary.
Save that and run npm run build
then take a look at those new files we generated.
Hmmm we seem to be missing our component tokens...
That is because the Android formats are filtering our tokens by type, so only colors go into our color resource file. Because our component tokens don't follow the same CTI object path as our other design tokens, they are not being categorized properly. So let's fix that. We can extend and override the built-in attribute/cti
transform to handle component tokens.
Create a new file in our transforms/ directory called attributeCTI.js
const originalTransform = require('style-dictionary').transform['attribute/cti'];var cssToCTIMap = {'width': {category: 'size', type: 'dimension'},'min-width': {category: 'size', type: 'dimension'},'max-width': {category: 'size', type: 'dimension'},'height': {category: 'size', type: 'dimension'},'min-height': {category: 'size', type: 'dimension'},'max-height': {category: 'size', type: 'dimension'},'border-width': {category: 'size', type: 'border', item: 'width'},'border-radius': {category: 'size', type: 'border', item: 'radius'},'border-color': {category: 'color', type: 'border'},'background-color': {category: 'color', type: 'background'},'color': {category: 'color', type: 'font'},'padding': {category: 'size', type: 'padding'},'padding-vertical': {category: 'size', type: 'padding'},'padding-horizontal': {category: 'size', type: 'padding'},'font-size': {category: 'size', type: 'font'},}module.exports = {type: 'attribute',name: 'attribute/cti',transformer: function(prop) {if (prop.path[0] === 'component') {return cssToCTIMap[prop.name] || {};} else {return originalTransform.transformer(prop);}}};
Back in our sd.config.js file we can add a custom transform in the transform
object:
'attribute/cti': require('./transforms/attributeCTI'),
This will override the 'attribute/cti' transform so the 'android' transform group doesn't need to be changed. Save that file and run npm run build
and verify our component tokens are now showing up!
If we re-build the Android app it looks like some of our design tokens have taken effect, but not our component ones...
That is because Android is merging all resources together and our demo module is defining resources with the same name, so it is overriding our design token resources. Open up android/demo/src/main/res/values/colors.xml and delete or comment out all the resources. Do the same with android/demo/src/main/res/dimens.xml. Then rebuild the app and take a look at the button and badge pages!
3.3 iOS integration
I have worked with Android and iOS a bit and I have to say, I like the resource XML files Android has. They can be a bit confusing with all of the XML tags they have, but it is at least some structure to styling an application. The reason I bring this up is that iOS does not have anything like that unfortunately. The closest thing iOS has are Color assets...but we will get to those later maybe.
iOS integration is a bit chaotic and there are many ways you can do it. You can really build it however makes sense for your iOS app. For example, my organization at Amazon we output Objective-C macros as our design token integration in our iOS app. Objective-C is a compiled language and macros are instructions to the compiler. It is pretty much like an automated find-and-replace when the code gets compiled.
The most simple way is to have a static struct, enum, or class that has static properties on with the name and value of the design tokens. In Swift it would look something like this:
public enum DesignTokens {static let sizeFontBase = CGFloat(16.00)static let colorFontPrimary = UIColorUIColor(red: 0.922, green: 0.976, blue: 0.922, alpha:1)}
Then you can reference those tokens like so:
DesignTokens.sizeFontBase
In this workshop I am going to focus on Swift because it is generally easier to understand for newcomers and is a bit simpler and more like other programming languages you might encounter. Also it is the newer language and SwiftUI is the cool new way to build interfaces.
Demo App
To start, lets just run the demo app and take a look around. You can open the Xcode project with the terminal command open
and then the path to an .xcodeproj or .xcworkspace file. In the root of the project run this command and it should open Xcode for you:
open ios/demo/ClarityDesignTokensDemo.xcodeproj
Now you can run the project by pressing the play button in the upper left area of Xcode. Fingers crossed it should build...
You can poke around the demo app, nothing too fancy and very similar to our Android demo app. Take a look around the code for the demo app:
- ios/demo/ClarityDesignTokensDemo: This is where all the demo app code lives
- ios/demo/ClarityDesignTokens/ContentView.swift:
- ios/demo/ClarityDesignTokens/Components/ClarityBadge.swift:
- ios/demo/ClarityDesignTokens/Tokens.swift:
CocoaPods
CocoaPods is the most common package manager for iOS packages. Because iOS keeps a tight hold on their build tools (Xcode is the only thing that can actuall compile iOS packages), CocoaPods doesn't actually compile anything.
Here are some guides on creating CocoaPod libraries if you are interested:
- https://guides.cocoapods.org/making/making-a-cocoapod.html
- https://medium.com/flawless-app-stories/create-your-own-cocoapods-library-da589d5cd270
Let's make our design token package a CocoaPod package. First make sure you have CocoaPods installed, if you haven't you can install it with this command:
sudo gem install cocoapods
Now we will initialize our CocoaPod. In the root of the package run
pod spec create ClarityDesignTokens
This will create the file ClarityDesignTokens.podspec. Open it up and we will make some changes. Using the pod spec create
command you have to edit some fields in the podspec file or else it won't build. We have to update the description, license, and source. Then we will update the source_files, exclude_files, and resources
spec.description = <<-DESCClarity design tokens!DESCspec.license = "MIT"spec.source = { :git => "https://github.com/dbanksdesign/clarity-2020-design-token-workshop.git", :tag => "#{spec.version}" }spec.source_files = "ios/dist/**/*.{h,m,swift}"spec.exclude_files = "ios/demo"# ...spec.resources = "ios/dist/DesignTokens.xcassets"
Right now if we tried to use this pod, nothing would be included because we haven't built any files for it. So let's build some files! Open up the sd.config.js file and let's add a new platform called "ios". Give it a transformGroup of "ios-swift", buildPath of "ios/dist/",
ios: {transformGroup: `ios-swift`,buildPath: `ios/dist/`,files: [{destination: `ClarityDesignTokens.swift`,className: `ClarityDesignTokens`,format: `ios-swift/enum.swift`}]}
Notice the className
property on the file object here. It has nothing to do with React's className. Formats are configurable so if you wanted to generate different swift classes you can use the same format and give each a different className!
Let's rebuild our style dictionary and see the file it generated
npm run build
Open ios/dist/ClarityDesignTokens.swift. I'm not a Swift expert, or even that competent with Swift, but I can understand what this is doing. We are creating a public class that has a bunch of static properties on it. So we should be able to use the design tokens like so: ClarityDesignTokens.colorBackgroundPrimary
.
pod init
This will create a Podfile where we can put our dependencies. Let's add a dependency on our newly created pod. Because this is a sample application we can use a local path:
pod 'ClarityDesignTokens', :path => '../../'
Now we can run the install command:
pod install
Note: if you ever add any new files or rename files in your cocoapod you will need to re-run pod install
to pick up the new files.
This will do all the CocoaPod magic. And now when opening the demo application we use the Xcode workspace file instead of the project file:
open ClarityDesignTokensDemo.xcworkspace
Now for some reason Xcode likes to cache CocoaPod files so if you make a change and it doesn't show up when you build the demo app. Go to Product > Schemes > EditScheme > Build > + and then add your development pod framework. I honestly spent a few hours trying to figure this out, and I finally did thanks to this Stack Overflow answer.
Ok, now we can start using our design tokens in our iOS app now!
Because there is no separate styling language like CSS for web or Resources for Android, styling components in iOS is actually kind of like CSS-in-JS. Why don't we do what we did for the badge Javascript integration here for swift?
iosSeparate: {buildPath: `ios/dist/`,transforms: StyleDictionary.transformGroup[`ios-swift`].concat(`nameComponentCamel`),files: [{destination: `BadgeTokens.swift`,className: `BadgeTokens`,format: `ios-swift/enum.swift`,filter: (token) => token.path[0] === 'component'&& token.path[1] === 'badge'}]}
Run npm run build
and make sure there is a ios/dist/BadgeTokens.swift file being generated and it looks good.
Any time you add or remove files in your local CocoaPod you will need to quit Xcode and re-install the pod in the demo app. cd
into the ios/demo directory and run pod install
Now open the ios/demo/ClarityDesignTokens.xcworkspace file in Xcode. Let's edit our badge component to use this new class:
//// ClarityBadge.swift// ClarityDesignTokensDemo//// Created by Banks, Daniel on 8/24/20.// Copyright © 2020 Amazon. All rights reserved.//import SwiftUIimport ClarityDesignTokensstruct ClarityBadge: View {enum BadgeType {casedanger,warning,success,nonefunc getBackgroundColor() -> Color {switch self {case .danger:return Color(BadgeTokens.dangerBackgroundColor)case .warning:return Color(BadgeTokens.warningBackgroundColor)case .success:return Color(BadgeTokens.successBackgroundColor)case .none:return Color(BadgeTokens.backgroundColor)}}func getTextColor() -> Color {switch self {case .danger:return Color(BadgeTokens.dangerColor)case .warning:return Color(BadgeTokens.warningColor)case .success:return Color(BadgeTokens.successColor)case .none:return Color(BadgeTokens.color)}}}var text: String = ""var color: Color?var type: BadgeType = .nonepublic var body: some View {HStack {Text(text).foregroundColor(type.getTextColor())}.padding(.vertical, 5).padding(.horizontal, 10).background(color ?? type.getBackgroundColor()).cornerRadius(20)}}struct ClarityBadge_Previews: PreviewProvider {static var previews: some View {HStack(spacing: 10) {ClarityBadge(text: "hello", type: .warning)ClarityBadge(text: "hello", type: .danger)ClarityBadge(text: "default")ClarityBadge(text: "custom", color: Color.green)}}}
Color Assets
I don't know
3.4 BONUS! Other integrations
As I have said previously, you can create anything with design tokens. The sky is the limit. When we want to integrate design tokens into application code we need to create code it can understand. While there are many pre-built formatsA format is a blah in Style Dictionary and Theo, you can define your own to suite your needs. What I like to do is work backwards from what developers of that platform want. For example, go to an iOS developer and ask how they want to consume and use design tokens. Or start with a dummy file and work backwards from that.
:root {--color-background-primary: #fff;--color-background-secondary: #ccc;}
`:root {` +dictionary.allProperties.map(token => {return ` --${token.name}: ${token.value};`}).join(`\n`) +`}`
Generating files is relatively inexpensive so you can generate as many types of files as you need. An application using the design token package can import only what they need.
Any type of file you can create, you can create with design tokens. I mean any type of file. Don't judge but my team at Amazon builds Java classes from our design tokens. That's right, Java. If you need to access your design tokens in any programming language or file type, you can. Some other examples are Alexa Presentation Language (APL), which is a JSON-based language to display interfaces on Alexa devices. You know what else you can use design tokens for? Building IDE themes!
Need some examples that have custom formats and transforms to show how they work.
Theo has a valueTransform that is the same as Style Dictionary's transform, which you can define custom ones with theo.registerValueTransform()
and styleDictionary.registerTransform()
. The method signatures of both are similar,
const theo = require('theo');const styleDictionary = require('style-dictionary');// these are roughly equivalenttheo.registerValueTransform('myTransform',(token) => token.get('type') === 'size',(token) => token.value * 2,);styleDictionary.registerTransform({name: `myTransform`,type: `value`, // can be value, name or attributematcher: (token) => token.attributes.type === 'size',transformer: (token) => token.value * 2,});// these are roughly equivalenttheo.registerTransform("web", ["myTransform"]);styleDictionary.registerTransformGroup({name: "web",transforms: ["myTransform"]});
3.5 Make some changes!
Let's see our design tokens in action now that they are integrated into our demo apps.
For starters let's see what happens when we change the brand color. Open up tokens/theme/brand.json and change the primary brand colors from teal to purple and the secondary brand colors from pink to blue.
After we rebuild the style dictionary package, if you go over to Android studio it should show the updated colors and even updated logo!
There is A LOT that we could cover about each of these platforms. This is only the tip of the iceberg but should be a good foundation to start with. What is most important is talking to the devs on each platform to see how they want to work with styling in their applications. Developers on each platform shouldn't fundamentally change how they work to use design tokens. Design tokens should seamlessly integrate with each platform.