Skip to content

Commit

Permalink
Merge branch 'main' into output-references
Browse files Browse the repository at this point in the history
  • Loading branch information
langermank authored Dec 5, 2024
2 parents 4a2b908 + a17e9a7 commit 0f97987
Show file tree
Hide file tree
Showing 25 changed files with 505 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-insects-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/primitives': minor
---

Adding new themeOverride preprocessor
2 changes: 1 addition & 1 deletion contributor-docs/adrs/adr-003-neutral-scales.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Title
# Neutral Scales

## Status

Expand Down
131 changes: 131 additions & 0 deletions contributor-docs/adrs/adr-004-token-overrides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Token overrides

## Status

| Stage | Status |
| -------- | ------ |
| Approved ||
| Adopted | 🚧 |

## Context

When creating tokens, like `fgColor-default` we need to be able to define different color values for different conditions (mostly themes).
For example we use a dark color `#1f2328` in `light` mode and a light color `#f0f6fc` in dark mode.

In some cases we use different files, like `color-light.json5` and `color-dark.json5`, however this is not always practical. Specifically for component tokens, where it can be desired to create one token file per component, separate files for all color modes does not scale well.

## Decision

This ADR provides a solution for thoses cases by allowing to specifiy specific overrides for a token within the `$extensions` property.

```json5
{
"button": {
"default": {
"bgColor": {
$value: "{base.neutral.2}" // default value,
$extensions: {
"org.primer.overrides": {
"dark": "{base.neutral.8}" // value use the dark mode,
}
}
}
}
}
}
```

To allow for more advanced overrides, you can provide an object with multiple properties instead of a value string.

```json5
{
"button": {
"default": {
"bgColor": {
$value: "{base.neutral.2}" // default value,
$extensions: {
"org.primer.overrides": {
"dark": {
$value: "{base.neutral.8}", // value use the dark theme
$description: "A dark theme description", // specific description for dark theme
},
"dark-dimmed": "{base.neutral.9}" // value use the dark-dimmed theme
}
}
}
}
}
}
```

This feature is implemented using a [`preprocessor`](https://styledictionary.com/reference/hooks/preprocessors/) for style dictionary. To enable this, you need to make two changes in any [`platform`](https://styledictionary.com/reference/config/#platform) configuration that should have this feature enabled:

Add the `themeOverrides` preprocessor to the platform config:

```diff
{
prefix,
buildPath,
+ preprocessors: ['themeOverrides'],
transforms: [
'name/pathToKebabCase',
'color/hex',
//...
]
}
```

Pass the current theme to the platform config options:

```diff
{
prefix,
buildPath,
preprocessors: ['themeOverrides'],
transforms: [
'name/pathToKebabCase',
'color/hex',
//...
],
options: {
basePxFontSize: 16,
+ themeOverrides: {
+ theme: "dark",
+ },
},
}
```

### Options

It is possible to change the `$extensions` property name and the default `value` property using the platform config options:

```diff
{
prefix,
buildPath,
preprocessors: ['themeOverrides'],
transforms: [
'name/pathToKebabCase',
'color/hex',
//...
],
options: {
basePxFontSize: 16,
themeOverrides: {
theme: "dark",
+ extensionProp: "theme", // defaults to 'org.primer.overrides'
+ valueProp: "value", // defaults to "$value"
},
},
}
```

### Impact

- we can now move to dedicated component files in `primitives`, making editing a lot easier
- `primer/brand` can now integrate more of our workflows and tooling as they already use a "one file per component" model

### Alternatives

Creating dedicated theme files for each component, e.g. `button-light.json5`, `button-dark.json5`, `button-light-high-contrast.json5`, ...
34 changes: 21 additions & 13 deletions scripts/buildFigma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
const baseScales = [
{
name: 'light',
theme: 'light',
source: [`src/tokens/base/color/light/light.json5`, `src/tokens/base/color/light/display-light.json5`],
},
{
name: 'light-high-constrast',
theme: 'light-high-constrast',
source: [
`src/tokens/base/color/light/light.json5`,
`src/tokens/base/color/light/display-light.json5`,
Expand All @@ -24,9 +26,11 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
},
{
name: 'dark',
theme: 'dark',
source: [`src/tokens/base/color/dark/dark.json5`, `src/tokens/base/color/dark/display-dark.json5`],
},
{
theme: 'dark-high-constrast',
name: 'dark-high-constrast',
source: [
`src/tokens/base/color/dark/dark.json5`,
Expand All @@ -35,6 +39,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
],
},
{
theme: 'dark-dimmed',
name: 'dark-dimmed',
source: [
`src/tokens/base/color/dark/dark.json5`,
Expand Down Expand Up @@ -62,7 +67,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
include,
platforms: {
figma: figma(`figma/themes/${filename}.json`, buildOptions.prefix, buildOptions.buildPath, {
mode: filename.replaceAll('-', ' '),
theme: filename.replaceAll('-', ' '),
}),
},
})
Expand Down Expand Up @@ -118,7 +123,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light',
theme: 'light',
},
{
name: 'light-high-contrast',
Expand All @@ -130,7 +135,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light high contrast',
theme: 'light high contrast',
},
{
name: 'light-colorblind',
Expand All @@ -142,7 +147,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light colorblind',
theme: 'light colorblind',
},
{
name: 'light-tritanopia',
Expand All @@ -154,7 +159,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/light/primitives-light.json5`,
`src/tokens/functional/color/light/patterns-light.json5`,
],
mode: 'light tritanopia',
theme: 'light tritanopia',
},
{
name: 'dark',
Expand All @@ -165,7 +170,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark',
theme: 'dark',
},
{
name: 'dark-high-contrast',
Expand All @@ -177,7 +182,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark high contrast',
theme: 'dark high contrast',
},
{
name: 'dark-dimmed',
Expand All @@ -189,7 +194,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark dimmed',
theme: 'dark dimmed',
},
{
name: 'dark-colorblind',
Expand All @@ -201,7 +206,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark colorblind',
theme: 'dark colorblind',
},
{
name: 'dark-tritanopia',
Expand All @@ -213,16 +218,16 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
`src/tokens/functional/color/dark/primitives-dark.json5`,
`src/tokens/functional/color/dark/patterns-dark.json5`,
],
mode: 'dark tritanopia',
theme: 'dark tritanopia',
},
]
//
for (const {name, source, include, mode} of shadowFiles) {
for (const {name, source, include, theme} of shadowFiles) {
const extended = await PrimerStyleDictionary.extend({
source,
include,
platforms: {
figma: figma(`figma/shadows/${name}.json`, buildOptions.prefix, buildOptions.buildPath, {mode}),
figma: figma(`figma/shadows/${name}.json`, buildOptions.prefix, buildOptions.buildPath, {theme}),
},
})

Expand All @@ -247,6 +252,7 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
group: string
name: string
}> = files.flatMap(filePath => JSON.parse(fs.readFileSync(filePath, 'utf8')))

// create a list of groups with collections and modes
const collections: Record<
string,
Expand All @@ -257,6 +263,9 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
> = {}

for (const {collection, mode, group} of tokens) {
if (!collection) {
continue
}
if (!(collection in collections)) {
collections[collection] = {
modes: [],
Expand All @@ -271,7 +280,6 @@ const buildFigma = async (buildOptions: ConfigGeneratorOptions): Promise<void> =
collections[collection].groups.push(group)
}
}

// define the order of the modes
// we inverse it to deal with the -1 of the indexOf if item is not found in the array: basically anything that is not in the list should come last
const modeOrder = [
Expand Down
17 changes: 12 additions & 5 deletions scripts/buildTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,16 @@ const getStyleDictionaryConfig: StyleDictionaryConfigGenerator = (
},
platforms: Object.fromEntries(
Object.entries({
css: css(`css/${filename}.css`, options.prefix, options.buildPath, {themed: options.themed}),
docJson: docJson(`docs/${filename}.json`, options.prefix, options.buildPath),
styleLint: styleLint(`styleLint/${filename}.json`, options.prefix, options.buildPath),
css: css(`css/${filename}.css`, options.prefix, options.buildPath, {
themed: options.themed,
theme: options.theme,
}),
docJson: docJson(`docs/${filename}.json`, options.prefix, options.buildPath, {
theme: options.theme,
}),
styleLint: styleLint(`styleLint/${filename}.json`, options.prefix, options.buildPath, {
theme: options.theme,
}),
fallbacks: fallbacks(`fallbacks/${filename}.json`, options.prefix, options.buildPath),
...platforms,
}).filter((entry: [string, unknown]) => entry[1] !== undefined),
Expand Down Expand Up @@ -71,14 +78,14 @@ export const buildDesignTokens = async (buildOptions: ConfigGeneratorOptions): P
* Colors, shadows & borders
* ----------------------------------- */
try {
for (const {filename, source, include} of themes) {
for (const {filename, source, include, theme} of themes) {
// build functional scales
const extendedSD = await PrimerStyleDictionary.extend(
getStyleDictionaryConfig(
`functional/themes/${filename}`,
source,
include,
{...buildOptions, themed: true},
{...buildOptions, themed: true, theme},
// disable fallbacks for themes
{fallbacks: undefined},
),
Expand Down
Loading

0 comments on commit 0f97987

Please sign in to comment.