Add foundation for exporting tokens from our color schemes (#2588)

We removed our Figma Tokens/Tokens Studio export a while back when we
moved to the theme to ColorSchemes. I'd like to get back to exporting
these so we can be working from up to date themes in Figma, especially
with the large amount of UI design work we'll be doing in the next few
weeks on channels.

This PR adds some basic plumbing to start working through the
theme/colorScheme and export the parts as tokens.

I also discovered that Tokens Studio now publishes their types, so we
can use them directly rather than writing our own:
https://github.com/tokens-studio/types

Pulled those in and started connecting them as well.

Running `npm run build-tokens` will export the tokens for each theme to
`styles/target/tokens`.

Currently only a few element's tokens are exported, will expand this
further as time permits.

Release Notes:

- N/A (No public facing changes)
This commit is contained in:
Nate Butler 2023-06-08 00:53:16 -04:00 committed by GitHub
commit a75e9faa83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 1 deletions

View file

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@tokens-studio/types": "^0.2.3",
"@types/chroma-js": "^2.4.0",
"@types/node": "^18.14.1",
"ayu": "^8.0.1",
@ -53,6 +54,11 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@tokens-studio/types": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@tokens-studio/types/-/types-0.2.3.tgz",
"integrity": "sha512-2KN3V0JPf+Zh8aoVMwykJq29Lsi7vYgKGYBQ/zQ+FbDEmrH6T/Vwn8kG7cvbTmW1JAAvgxVxMIivgC9PmFelNA=="
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -271,6 +277,11 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@tokens-studio/types": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@tokens-studio/types/-/types-0.2.3.tgz",
"integrity": "sha512-2KN3V0JPf+Zh8aoVMwykJq29Lsi7vYgKGYBQ/zQ+FbDEmrH6T/Vwn8kG7cvbTmW1JAAvgxVxMIivgC9PmFelNA=="
},
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",

View file

@ -5,11 +5,13 @@
"main": "index.js",
"scripts": {
"build": "ts-node ./src/buildThemes.ts",
"build-licenses": "ts-node ./src/buildLicenses.ts"
"build-licenses": "ts-node ./src/buildLicenses.ts",
"build-tokens": "ts-node ./src/buildTokens.ts"
},
"author": "",
"license": "ISC",
"dependencies": {
"@tokens-studio/types": "^0.2.3",
"@types/chroma-js": "^2.4.0",
"@types/node": "^18.14.1",
"ayu": "^8.0.1",

39
styles/src/buildTokens.ts Normal file
View file

@ -0,0 +1,39 @@
import * as fs from "fs"
import * as path from "path"
import { ColorScheme, createColorScheme } from "./common"
import { themes } from "./themes"
import { slugify } from "./utils/slugify"
import { colorSchemeTokens } from "./theme/tokens/colorScheme"
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
function clearTokens(tokensDirectory: string) {
if (!fs.existsSync(tokensDirectory)) {
fs.mkdirSync(tokensDirectory, { recursive: true })
} else {
for (const file of fs.readdirSync(tokensDirectory)) {
if (file.endsWith(".json")) {
fs.unlinkSync(path.join(tokensDirectory, file))
}
}
}
}
function writeTokens(colorSchemes: ColorScheme[], tokensDirectory: string) {
clearTokens(tokensDirectory)
for (const colorScheme of colorSchemes) {
const fileName = slugify(colorScheme.name)
const tokens = colorSchemeTokens(colorScheme)
const tokensJSON = JSON.stringify(tokens, null, 2)
const outPath = path.join(tokensDirectory, `${fileName}.json`)
fs.writeFileSync(outPath, tokensJSON)
console.log(`- ${outPath} created`)
}
}
const colorSchemes: ColorScheme[] = themes.map((theme) =>
createColorScheme(theme)
)
writeTokens(colorSchemes, TOKENS_DIRECTORY)

View file

@ -0,0 +1,12 @@
import { ColorScheme } from "../colorScheme"
import { PlayerTokens, players } from "./players"
interface ColorSchemeTokens {
players: PlayerTokens
}
export function colorSchemeTokens(colorScheme: ColorScheme): ColorSchemeTokens {
return {
players: players(colorScheme),
}
}

View file

@ -0,0 +1,28 @@
import { SingleColorToken } from "@tokens-studio/types"
import { ColorScheme, Players } from "../../common"
import { colorToken } from "./token"
export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>
export type PlayerTokens = Record<keyof Players, PlayerToken>
function buildPlayerToken(colorScheme: ColorScheme, index: number): PlayerToken {
const playerNumber = index.toString() as keyof Players
return {
selection: colorToken(`player${index}Selection`, colorScheme.players[playerNumber].selection),
cursor: colorToken(`player${index}Cursor`, colorScheme.players[playerNumber].cursor),
}
}
export const players = (colorScheme: ColorScheme): PlayerTokens => ({
"0": buildPlayerToken(colorScheme, 0),
"1": buildPlayerToken(colorScheme, 1),
"2": buildPlayerToken(colorScheme, 2),
"3": buildPlayerToken(colorScheme, 3),
"4": buildPlayerToken(colorScheme, 4),
"5": buildPlayerToken(colorScheme, 5),
"6": buildPlayerToken(colorScheme, 6),
"7": buildPlayerToken(colorScheme, 7)
})

View file

@ -0,0 +1,14 @@
import { SingleColorToken, TokenTypes } from "@tokens-studio/types"
export function colorToken(name: string, value: string, description?: string): SingleColorToken {
const token: SingleColorToken = {
name,
type: TokenTypes.COLOR,
value,
description,
}
if (!token.value || token.value === '') throw new Error("Color token must have a value")
return token
}

View file

@ -0,0 +1 @@
export function slugify(t: string): string { return t.toString().toLowerCase().replace(/\s+/g, '-').replace(/[^\w\-]+/g, '').replace(/\-\-+/g, '-').replace(/^-+/, '').replace(/-+$/, '') }