Skip to content

Instantly share code, notes, and snippets.

@bherbst
Created April 1, 2021 18:14
Show Gist options
  • Save bherbst/1b8c9a66953f25044631db68dd773429 to your computer and use it in GitHub Desktop.
Save bherbst/1b8c9a66953f25044631db68dd773429 to your computer and use it in GitHub Desktop.
Style Dictionary - Compose Dark Mode
package com.example.tokens
import androidx.compose.ui.graphics.Color
class Colors(
val textPrimary: Color,
val textSecondary: Color,
val textInverse: Color,
val textSecondaryInverse: Color,
)
fun lightColors(
textPrimary: Color = Palette.grayDarkest,
textSecondary: Color = Palette.grayDark,
textInverse: Color = Palette.paletteWhite,
textSecondaryInverse: Color = Palette.grayLightest,
): Colors = Colors(
textPrimary,
textSecondary,
textInverse,
textSecondaryInverse,
)
fun darkColors(
textPrimary: Color = Palette.grayLightest,
textSecondary: Color = Palette.grayDark,
textInverse: Color = Palette.paletteWhite,
textSecondaryInverse: Color = Palette.grayLightest,
): Colors = Colors(
textPrimary,
textSecondary,
textInverse,
textSecondaryInverse,
)
object Palette {
val paletteWhite: Color = Color(0xffffffff)
val paletteGray20: Color = Color(0x333333ff)
val paletteGray40: Color = Color(0x666666ff)
val paletteGray84: Color = Color(0xd6d6d6ff)
val paletteGray91: Color = Color(0xe8e8e8ff)
val paletteGray97: Color = Color(0xf7f7f7ff)
val grayLightest: Color = paletteGray97
val grayDark: Color = paletteGray40
val grayDarkest: Color = paletteGray20
}
module.exports = {
source: ["tokens/*.json"],
transform: {
'color/ComposeColor': require('./transformers/composeColor')
},
format: {
androidComposeColors: require('./formats/compose')
},
platforms: {
androidCompose: {
transforms: [
'attribute/cti',
'name/ti/camel',
'color/ComposeColor'
],
buildPath: "build/android-compose/",
files: [{
destination: "Colors.kt",
format: `androidComposeColors`,
packageName: "com.example.tokens",
options: {
outputReferences: true
},
filter: (token) => token.attributes.category === `color`
}]
}
}
}
module.exports = function({ dictionary, options, file }) {
const themeableTokens = dictionary.allProperties.filter(token => { return token.themeable === true || token.darkValue });
return `package ` + file.packageName + `
import androidx.compose.ui.graphics.Color
class Colors(\n` +
themeableTokens.map(token => {
return ` val ${token.name}: Color,`
}).join(`\n`) +
`\n)\n\n` +
// Light mode colors
`fun lightColors(\n` +
themeableTokens.map(token => {
var value = tokenToThemedValue(dictionary, options, themeableTokens, token);
return ` ${token.name}: Color = ${value},`
}).join(`\n`) +
`\n): Colors = Colors(\n` +
themeableTokens.map(token => { return ` ${token.name},` }).join(`\n`) +
`\n)\n\n` +
// Dark mode colors - any color without a defined darkValue property will fall back to the light value
`fun darkColors(\n` +
themeableTokens
.map(token => {
var value = tokenToThemedDarkValue(dictionary, options, themeableTokens, token);
return ` ${token.name}: Color = ${value},`
}).join(`\n`) +
`\n): Colors = Colors(\n` +
themeableTokens.map(token => { return ` ${token.name},` }).join(`\n`) +
`\n)\n\n` +
// This is the color palette that we build our light and dark themes on
`object Palette {\n` +
dictionary.allProperties
// We need to sort based on how deep the reference trail is, because a value needs to be
// defined before we reference it in Kotlin
.sort( (token1, token2) => { return sortByReferenceDepth(dictionary, token1, token2) })
.filter( token => { return !token.themeable && !token.darkValue } )
.map(token => {
var value = tokenToValue(dictionary, options, token);
return ` val ${token.name}: Color = ${value}`
}).join(`\n`) +
`\n}`
}
function tokenToValue(dictionary, options, token) {
if (options.outputReferences && dictionary.usesReference(token.original.value)) {
var reference = dictionary.getReference(token.original.value)
return reference.name;
} else {
return token.value;
}
}
function tokenToThemedValue(dictionary, options, themeableTokens, token) {
if (options.outputReferences && dictionary.usesReference(token.original.value)) {
var reference = dictionary.getReference(token.original.value)
if (themeableTokens.includes(reference)) {
return reference.name;
} else {
return `Palette.${reference.name}`
}
} else {
return token.value;
}
}
function tokenToThemedDarkValue(dictionary, options, themeableTokens, token) {
if (!token.darkValue) {
return tokenToThemedValue(dictionary, options, themeableTokens, token)
}
if (options.outputReferences && dictionary.usesReference(token.original.darkValue)) {
var reference = dictionary.getReference(token.original.darkValue)
if (themeableTokens.includes(reference)) {
return reference.name;
} else {
return `Palette.${reference.name}`
}
} else {
return token.darkValue;
}
}
function sortByReferenceDepth(dictionary, token1, token2) {
return tokenDepth(dictionary, token1) - tokenDepth(dictionary, token2)
}
function tokenDepth(dictionary, token) {
if (dictionary.usesReference(token.original.value)) {
var reference = dictionary.getReference(token.original.value)
return tokenDepth(dictionary, reference) + 1
} else {
return 0
}
}
{
"color": {
"palette": {
"white": {"value":"#ffffff"},
"gray": {
"97": { "value": "#f7f7f7" },
"91" : { "value": "#e8e8e8" },
"84" : { "value": "#d6d6d6" },
"40" : { "value": "#666" },
"20" : { "value": "#333" }
}
},
"gray": {
"lightest": {"value":"{color.palette.gray.97.value}"},
"dark": {"value":"{color.palette.gray.40.value}"},
"darkest": {"value":"{color.palette.gray.20.value}"}
},
"text": {
"primary": {
"themeable": true,
"value": "{color.gray.darkest.value}",
"darkValue": "{color.gray.lightest.value}",
"comment": "Primary textual content"
},
"secondary": {
"themeable": true,
"value": "{color.gray.dark.value}",
"comment": "Secondary content"
},
"inverse": {
"themeable": true,
"value": "{color.palette.white.value}",
"comment": "Text that must contrast negatively against a background. (e.g. white on red)"
},
"secondaryInverse": {
"themeable": true,
"value": "{color.gray.lightest.value}",
"comment": "Secondary text that must contrast negatively against a background."
}
}
}
}
const StyleDictionary = require('style-dictionary');
var Color = require('tinycolor2')
module.exports = {
type: `value`,
matcher: (token) => token.attributes.category === `color`,
transformer: (prop) => {
const hex8 = Color(prop.value).toHex8();
return `Color(0x${hex8})`;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment