Skip to content

Commit b1eeb4d

Browse files
chore: add mermaid integration to docs, create architecture (#1221)
1 parent c708325 commit b1eeb4d

8 files changed

+4369
-3955
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { ExpressiveCodePlugin } from '@expressive-code/core';
2+
3+
export function pluginLanguageClass(): ExpressiveCodePlugin {
4+
return {
5+
name: 'PluginLanguageClass',
6+
hooks: {
7+
postprocessRenderedBlock: (opts) => {
8+
if (opts.codeBlock.language === 'mermaid') {
9+
const pre = opts.renderData.blockAst.children.find(
10+
(child) => child.type === 'element' && child.tagName === 'pre',
11+
);
12+
if (pre && pre.type === 'element') {
13+
// add the mermaid language class so mermaid can pick this up
14+
pre.properties = {
15+
class: `${opts.codeBlock.language} hidden`,
16+
};
17+
console.log(pre);
18+
19+
// replace the AST that expressive code has made and put back the simple text
20+
// otherwise mermaid gets confused by all of the generated HTML syntax
21+
pre.children = [
22+
{
23+
value: opts.codeBlock.code,
24+
type: 'text',
25+
},
26+
];
27+
}
28+
}
29+
},
30+
},
31+
};
32+
}

docs/src/components/sd-playground.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ class SdPlayground extends LitElement {
235235
</sl-radio-button>
236236
`,
237237
)}
238-
<sl-radio-button value="eject" aria-label="Eject" title="Eject" @click=${this.ejectHandler}>
238+
<sl-radio-button value="eject" label="Eject" title="Eject" @click=${this.ejectHandler}>
239239
<svg
240+
aria-label="Eject"
240241
width="20px"
241242
height="20px"
242243
viewBox="0 0 24 24"

docs/src/content/docs/info/architecture.md

+49-15
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,45 @@ sidebar:
44
order: 2
55
---
66

7-
This is how Style Dictionary works under the hood.
8-
9-
![build structure](../../../assets/build-diagram.png)
7+
This is how Style Dictionary works under the hood:
8+
9+
```mermaid
10+
flowchart LR
11+
subgraph global
12+
direction TB
13+
Z[Config] -->|Parse config| X
14+
X[Parsed Config] -->|Run| A
15+
A[Token Files] -->|Parsers| B
16+
B[JavaScript Objects] -->|Combine| C
17+
C[Dictionary] -->|Preprocessors| C
18+
click X "#1-parse-the-config" "Explanation about parsing config"
19+
click A "#2-find-all-token-files" "Explanation about finding tokens files"
20+
click B "#3-parse-token-files" "Explanation about parsing tokens files"
21+
click C "#4-deep-merge-token-files" "Explanation about deep merging tokens into a dictionary"
22+
end
23+
24+
subgraph platform
25+
direction TB
26+
D[Dictionary] -->|Preprocessors| D
27+
D -->|Transforms| E
28+
E[Transformed Dictionary] -->|Resolve references| F
29+
F[Resolved Dictionary] -->|Transitive transforms| E
30+
click D "#5-run-preprocessors-over-the-dictionary" "Explanation about preprocessing the dictionary"
31+
click E "#6-transform-the-tokens" "Explanation about transforming tokens"
32+
click F "#7-resolve-aliases--references-to-other-values" "Explanation about token references resolution"
33+
end
34+
35+
subgraph files
36+
direction TB
37+
G[Resolved Dictionary] --> |Filters| H
38+
H[Filtered Dictionary] --> |Formats| I
39+
H[Filtered Dictionary] --> |File headers| I
40+
I[Platform output] --> |Actions| J[Actions output]
41+
click I "#8-format-the-tokens-into-files" "Explanation about formatting tokens to output files"
42+
click J "#9-run-actions" "Explanation about running actions"
43+
end
44+
global --> platform --> files
45+
```
1046

1147
Let's take a closer look into each of these steps.
1248

@@ -18,37 +54,35 @@ Style Dictionary is a configuration based framework, you tell it what to do in a
1854

1955
In your [config](/reference/config) file can define `include` and `source`, which are arrays of file path globs. These tell Style Dictionary where to find your token files. You can have them anywhere and in any folder structure as long as you tell Style Dictionary where to find them.
2056

21-
If there are [custom parsers](/reference/hooks/parsers) defined and applied in the config, Style Dictionary will run those on files the applied parsers match.
57+
## 3. Parse token files
58+
59+
If there are [custom parsers](/reference/hooks/parsers) defined and applied in the config, Style Dictionary will run those on files the applied parsers match. For JSON or JavaScript token files, those are parsed automatically through built-in parsers.
2260

23-
## 3. Deep merge token files
61+
## 4. Deep merge token files
2462

2563
Style Dictionary takes all the files it found and performs a deep merge. This allows you to split your token files in any way you like, without worrying about accidentally overriding groups of tokens. This gives Style Dictionary a single, complete token object to work from.
2664

27-
## 4. Run preprocessors over the dictionary
65+
## 5. Run preprocessors over the dictionary
2866

2967
Allows users to configure [custom preprocessors](/reference/hooks/preprocessors), to process the merged dictionary as a whole, rather than per token file individually.
3068
These preprocessors have to be applied in the config, either on a global or platform level.
69+
Platform level preprocessors run once you get/export/format/build a platform, at the very start.
3170

32-
## 5. Iterate over the platforms
33-
34-
For each platform defined in your [config](/reference/config), Style Dictionary will do a few steps to get it ready to be consumed on that platform. You don't need to worry about one platform affecting another because everything that happens in a platform is non-destructive.
35-
Platform-applied preprocessors run now instead of beforehand in step 4.
36-
37-
## 5a. Transform the tokens
71+
## 6. Transform the tokens
3872

3973
Style Dictionary now traverses over the whole token object and looks for design tokens. It does this by looking for anything with a `value` key. When it comes across a design token, it then performs all the [transforms](/reference/hooks/transforms) defined in your [config](/reference/config) in order.
4074

4175
Value transforms, transforms that modify a token's value, are skipped if the token references another token. Starting in 3.0, you can define a [transitive transform](/reference/hooks/transforms#transitive-transforms) that will transform a value that references another token after that reference has been resolved.
4276

43-
## 5b. Resolve aliases / references to other values
77+
## 7. Resolve aliases / references to other values
4478

4579
After all the tokens have been transformed, it then does another pass over the token object looking for aliases, which look like `"{size.font.base.value}"`. When it finds these, it then replaces the reference with the transformed value. Because Style Dictionary merges all token files into a single object, aliases can be in any token file and still work.
4680

47-
## 5c. Format the tokens into files
81+
## 8. Format the tokens into files
4882

4983
Now all the design tokens are ready to be written to a file. Style Dictionary takes the whole transformed and resolved token object and for each file defined in the platform it [formats](/reference/hooks/formats) the token object and write the output to a file. Internally, Style Dictionary creates a flat array of all the design tokens it finds in addition to the token object. This is how you can output a flat SCSS variables file.
5084

51-
## 5d. Run actions
85+
## 9. Run actions
5286

5387
[Actions](/reference/hooks/actions) are custom code that run in a platform after the files are generated. They are useful for things like copying assets to specific build directories or generating images.
5488

docs/src/setup.ts

+85-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import dark from '@shoelace-style/shoelace/dist/themes/dark.css?raw' assert { type: 'css' };
22
import light from '@shoelace-style/shoelace/dist/themes/light.css?raw' assert { type: 'css' };
3+
import mermaid from 'mermaid';
34
import { registeredComponents } from './components/sd-playground.ts';
45

56
type Theme = 'dark' | 'light';
@@ -18,11 +19,43 @@ sheets.light.replaceSync(light);
1819
sheets.dark.theme = true;
1920
sheets.light.theme = true;
2021

22+
// 77rem
23+
const VP_THRESHOLD = 1231;
24+
const getViewportWidth = () =>
25+
Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
26+
27+
async function renderMermaid() {
28+
const theme = getSelectedTheme();
29+
const direction = getViewportWidth() > VP_THRESHOLD ? 'LR' : 'TB';
30+
mermaid.initialize({
31+
startOnLoad: false,
32+
theme,
33+
});
34+
const elements = [...document.querySelectorAll('.mermaid')] as HTMLPreElement[];
35+
await Promise.all(
36+
[...elements].map((el) => {
37+
// we're storing the original graph definition as a data attribute
38+
// because mermaid's render will change the innerText
39+
// and we need to be able to re-render on theme swap or window resize
40+
const graphDefinition = el.getAttribute('data-mermaid-graph-definition') ?? el.innerText;
41+
el.setAttribute('data-mermaid-graph-definition', graphDefinition);
42+
return mermaid
43+
.render('graphDiv', graphDefinition.replace('flowchart LR', `flowchart ${direction}`))
44+
.then(({ svg }) => {
45+
el.innerHTML = svg;
46+
});
47+
}),
48+
);
49+
[...elements].forEach((el) => {
50+
el.classList.remove('hidden');
51+
});
52+
}
53+
2154
function getSelectedTheme() {
2255
return document.documentElement.getAttribute(themeAttr) as Theme;
2356
}
2457

25-
function swapTheme(theme: Theme) {
58+
async function swapTheme(theme: Theme) {
2659
currTheme = theme;
2760
// shoelace theme class
2861
document.documentElement.classList.add(`sl-theme-${theme}`);
@@ -38,36 +71,59 @@ function swapTheme(theme: Theme) {
3871
comp.editor._themeService.setTheme(`my-${theme}-theme`);
3972
});
4073
});
74+
await renderMermaid();
4175
}
4276

43-
// initial
44-
swapTheme(getSelectedTheme());
77+
function handleThemeChange() {
78+
// MutationObserver that watches the starlight theme attribute for changes, which is handled by the theme toggler
79+
const themeObserver = new MutationObserver(() => {
80+
const selectedTheme = getSelectedTheme();
81+
if (currTheme !== selectedTheme && (selectedTheme === 'dark' || selectedTheme === 'light')) {
82+
swapTheme(selectedTheme);
83+
}
84+
});
85+
themeObserver.observe(document.documentElement, {
86+
attributes: true,
87+
attributeFilter: [themeAttr],
88+
});
89+
}
4590

46-
// MutationObserver that watches the starlight theme attribute for changes, which is handled by the theme toggler
47-
const themeObserver = new MutationObserver(() => {
48-
const selectedTheme = getSelectedTheme();
49-
if (currTheme !== selectedTheme && (selectedTheme === 'dark' || selectedTheme === 'light')) {
50-
swapTheme(selectedTheme);
51-
}
52-
});
53-
themeObserver.observe(document.documentElement, {
54-
attributes: true,
55-
attributeFilter: [themeAttr],
56-
});
91+
function lazilyLoadCEs(CEs: string[]) {
92+
CEs.forEach((CE) => {
93+
const firstInstance = document.querySelector(CE);
94+
if (firstInstance) {
95+
const observer = new IntersectionObserver((entries) => {
96+
entries.forEach((entry) => {
97+
if (entry.isIntersecting) {
98+
// Conditionally load the Web Component definition if we find an instance of it.
99+
import(`./components/${CE}.ts`);
100+
}
101+
});
102+
});
103+
observer.observe(firstInstance);
104+
}
105+
});
106+
}
57107

58-
const CEs = ['sd-playground', 'sd-dtcg-convert'];
108+
function handleResize() {
109+
let prevVPWidth = getViewportWidth();
110+
window.addEventListener('resize', () => {
111+
const currVWWidth = getViewportWidth();
112+
if (
113+
(prevVPWidth >= VP_THRESHOLD && currVWWidth < VP_THRESHOLD) ||
114+
(prevVPWidth <= VP_THRESHOLD && currVWWidth > VP_THRESHOLD)
115+
) {
116+
renderMermaid();
117+
}
118+
prevVPWidth = currVWWidth;
119+
});
120+
}
59121

60-
CEs.forEach((ce) => {
61-
// Conditionally load the sd-playground Web Component definition if we find an instance of it.
62-
const firstEl = document.querySelector(ce);
63-
if (firstEl) {
64-
const observer = new IntersectionObserver((entries) => {
65-
entries.forEach((entry) => {
66-
if (entry.isIntersecting) {
67-
import(`./components/${ce}.ts`);
68-
}
69-
});
70-
});
71-
observer.observe(firstEl);
72-
}
73-
});
122+
async function setup() {
123+
handleThemeChange();
124+
lazilyLoadCEs(['sd-playground', 'sd-dtcg-convert']);
125+
handleResize();
126+
// initial
127+
await swapTheme(getSelectedTheme());
128+
}
129+
setup();

docs/src/styles.css

+12
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,15 @@ a {
7979
grid-template-columns: 8fr 4fr;
8080
}
8181
}
82+
.expressive-code pre.mermaid *:not(path) {
83+
all: revert-layer;
84+
}
85+
86+
#graphDiv .clickable:hover .label-container {
87+
stroke: #3fc6bf;
88+
stroke-width: 3px;
89+
}
90+
91+
.hidden {
92+
display: none !important;
93+
}

docs/starlight-config.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import type { StarlightUserConfig } from '@astrojs/starlight/types';
2+
import { pluginLanguageClass } from './expressive-code-plugin-language-class.ts';
23

34
export default {
5+
expressiveCode: {
6+
plugins: [
7+
// Call the plugin initialization function inside the `plugins` array
8+
pluginLanguageClass(),
9+
],
10+
styleOverrides: {
11+
textMarkers: {
12+
defaultLuminance: ['15%', '85%'],
13+
},
14+
},
15+
},
416
title: 'Style Dictionary',
517
description:
618
'Export your Design Tokens to any platform. iOS, Android, CSS, JS, HTML, sketch files, style documentation, or anything you can think of. Forward-compatible with Design Token Community Group spec.',
@@ -149,13 +161,6 @@ export default {
149161
},
150162
],
151163
customCss: ['./src/styles.css'],
152-
expressiveCode: {
153-
styleOverrides: {
154-
textMarkers: {
155-
defaultLuminance: ['15%', '85%'],
156-
},
157-
},
158-
},
159164
components: {
160165
Head: './src/components/Head.astro',
161166
},

0 commit comments

Comments
 (0)