@wordpress/theme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +788 -0
- package/README.md +67 -0
- package/bin/build-tokens.js +83 -0
- package/bin/generate-primitive-tokens/index.ts +115 -0
- package/bin/terrazzo-plugin-ds-tokens-docs/index.ts +103 -0
- package/bin/terrazzo-plugin-figma-ds-token-manager/index.ts +210 -0
- package/bin/terrazzo-plugin-figma-ds-token-manager/lib.ts +1 -0
- package/bin/terrazzo-plugin-known-wpds-css-variables/index.ts +72 -0
- package/build/color-ramps/index.js +132 -0
- package/build/color-ramps/index.js.map +7 -0
- package/build/color-ramps/lib/cache-utils.js +57 -0
- package/build/color-ramps/lib/cache-utils.js.map +7 -0
- package/build/color-ramps/lib/constants.js +105 -0
- package/build/color-ramps/lib/constants.js.map +7 -0
- package/build/color-ramps/lib/find-color-with-constraints.js +141 -0
- package/build/color-ramps/lib/find-color-with-constraints.js.map +7 -0
- package/build/color-ramps/lib/index.js +264 -0
- package/build/color-ramps/lib/index.js.map +7 -0
- package/build/color-ramps/lib/ramp-configs.js +315 -0
- package/build/color-ramps/lib/ramp-configs.js.map +7 -0
- package/build/color-ramps/lib/taper-chroma.js +159 -0
- package/build/color-ramps/lib/taper-chroma.js.map +7 -0
- package/build/color-ramps/lib/types.js +17 -0
- package/build/color-ramps/lib/types.js.map +7 -0
- package/build/color-ramps/lib/utils.js +106 -0
- package/build/color-ramps/lib/utils.js.map +7 -0
- package/build/context.js +34 -0
- package/build/context.js.map +7 -0
- package/build/index.js +29 -0
- package/build/index.js.map +7 -0
- package/build/lock-unlock.js +35 -0
- package/build/lock-unlock.js.map +7 -0
- package/build/prebuilt/js/design-tokens.js +135 -0
- package/build/prebuilt/js/design-tokens.js.map +7 -0
- package/build/prebuilt/json/figma.json +1317 -0
- package/build/prebuilt/ts/design-tokens.js +354 -0
- package/build/prebuilt/ts/design-tokens.js.map +7 -0
- package/build/private-apis.js +36 -0
- package/build/private-apis.js.map +7 -0
- package/build/style.module.css.js +2 -0
- package/build/theme-provider.js +92 -0
- package/build/theme-provider.js.map +7 -0
- package/build/types/css-modules.d.js +2 -0
- package/build/types/css-modules.d.js.map +7 -0
- package/build/types.js +17 -0
- package/build/types.js.map +7 -0
- package/build/use-theme-provider-styles.js +230 -0
- package/build/use-theme-provider-styles.js.map +7 -0
- package/build-module/color-ramps/index.js +95 -0
- package/build-module/color-ramps/index.js.map +7 -0
- package/build-module/color-ramps/lib/cache-utils.js +31 -0
- package/build-module/color-ramps/lib/cache-utils.js.map +7 -0
- package/build-module/color-ramps/lib/constants.js +63 -0
- package/build-module/color-ramps/lib/constants.js.map +7 -0
- package/build-module/color-ramps/lib/find-color-with-constraints.js +112 -0
- package/build-module/color-ramps/lib/find-color-with-constraints.js.map +7 -0
- package/build-module/color-ramps/lib/index.js +235 -0
- package/build-module/color-ramps/lib/index.js.map +7 -0
- package/build-module/color-ramps/lib/ramp-configs.js +290 -0
- package/build-module/color-ramps/lib/ramp-configs.js.map +7 -0
- package/build-module/color-ramps/lib/taper-chroma.js +125 -0
- package/build-module/color-ramps/lib/taper-chroma.js.map +7 -0
- package/build-module/color-ramps/lib/types.js +1 -0
- package/build-module/color-ramps/lib/types.js.map +7 -0
- package/build-module/color-ramps/lib/utils.js +84 -0
- package/build-module/color-ramps/lib/utils.js.map +7 -0
- package/build-module/context.js +10 -0
- package/build-module/context.js.map +7 -0
- package/build-module/index.js +5 -0
- package/build-module/index.js.map +7 -0
- package/build-module/lock-unlock.js +10 -0
- package/build-module/lock-unlock.js.map +7 -0
- package/build-module/prebuilt/js/design-tokens.js +115 -0
- package/build-module/prebuilt/js/design-tokens.js.map +7 -0
- package/build-module/prebuilt/json/figma.json +1317 -0
- package/build-module/prebuilt/ts/design-tokens.js +334 -0
- package/build-module/prebuilt/ts/design-tokens.js.map +7 -0
- package/build-module/private-apis.js +12 -0
- package/build-module/private-apis.js.map +7 -0
- package/build-module/style.module.css.js +1 -0
- package/build-module/theme-provider.js +58 -0
- package/build-module/theme-provider.js.map +7 -0
- package/build-module/types/css-modules.d.js +1 -0
- package/build-module/types/css-modules.d.js.map +7 -0
- package/build-module/types.js +1 -0
- package/build-module/types.js.map +7 -0
- package/build-module/use-theme-provider-styles.js +200 -0
- package/build-module/use-theme-provider-styles.js.map +7 -0
- package/build-style/style.css +3 -0
- package/build-types/color-ramps/index.d.ts +44 -0
- package/build-types/color-ramps/index.d.ts.map +1 -0
- package/build-types/color-ramps/lib/cache-utils.d.ts +22 -0
- package/build-types/color-ramps/lib/cache-utils.d.ts.map +1 -0
- package/build-types/color-ramps/lib/constants.d.ts +38 -0
- package/build-types/color-ramps/lib/constants.d.ts.map +1 -0
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts +37 -0
- package/build-types/color-ramps/lib/find-color-with-constraints.d.ts.map +1 -0
- package/build-types/color-ramps/lib/index.d.ts +11 -0
- package/build-types/color-ramps/lib/index.d.ts.map +1 -0
- package/build-types/color-ramps/lib/ramp-configs.d.ts +7 -0
- package/build-types/color-ramps/lib/ramp-configs.d.ts.map +1 -0
- package/build-types/color-ramps/lib/taper-chroma.d.ts +32 -0
- package/build-types/color-ramps/lib/taper-chroma.d.ts.map +1 -0
- package/build-types/color-ramps/lib/types.d.ts +78 -0
- package/build-types/color-ramps/lib/types.d.ts.map +1 -0
- package/build-types/color-ramps/lib/utils.d.ts +38 -0
- package/build-types/color-ramps/lib/utils.d.ts.map +1 -0
- package/build-types/color-ramps/stories/index.story.d.ts +14 -0
- package/build-types/color-ramps/stories/index.story.d.ts.map +1 -0
- package/build-types/color-ramps/stories/ramp-table.d.ts +19 -0
- package/build-types/color-ramps/stories/ramp-table.d.ts.map +1 -0
- package/build-types/context.d.ts +10 -0
- package/build-types/context.d.ts.map +1 -0
- package/build-types/index.d.ts +2 -0
- package/build-types/index.d.ts.map +1 -0
- package/build-types/lock-unlock.d.ts +2 -0
- package/build-types/lock-unlock.d.ts.map +1 -0
- package/build-types/prebuilt/js/design-tokens.d.ts +3 -0
- package/build-types/prebuilt/js/design-tokens.d.ts.map +1 -0
- package/build-types/prebuilt/ts/design-tokens.d.ts +7 -0
- package/build-types/prebuilt/ts/design-tokens.d.ts.map +1 -0
- package/build-types/private-apis.d.ts +2 -0
- package/build-types/private-apis.d.ts.map +1 -0
- package/build-types/stories/index.story.d.ts +15 -0
- package/build-types/stories/index.story.d.ts.map +1 -0
- package/build-types/theme-provider.d.ts +3 -0
- package/build-types/theme-provider.d.ts.map +1 -0
- package/build-types/types.d.ts +42 -0
- package/build-types/types.d.ts.map +1 -0
- package/build-types/use-theme-provider-styles.d.ts +17 -0
- package/build-types/use-theme-provider-styles.d.ts.map +1 -0
- package/docs/ds-tokens.md +283 -0
- package/package.json +58 -0
- package/src/color-ramps/index.ts +155 -0
- package/src/color-ramps/lib/cache-utils.ts +56 -0
- package/src/color-ramps/lib/constants.ts +85 -0
- package/src/color-ramps/lib/find-color-with-constraints.ts +190 -0
- package/src/color-ramps/lib/index.ts +369 -0
- package/src/color-ramps/lib/ramp-configs.ts +309 -0
- package/src/color-ramps/lib/taper-chroma.ts +226 -0
- package/src/color-ramps/lib/types.ts +90 -0
- package/src/color-ramps/lib/utils.ts +161 -0
- package/src/color-ramps/stories/index.story.tsx +264 -0
- package/src/color-ramps/stories/ramp-table.tsx +212 -0
- package/src/color-ramps/test/__snapshots__/index.test.ts.snap +1280 -0
- package/src/color-ramps/test/index.test.ts +94 -0
- package/src/context.ts +19 -0
- package/src/index.ts +2 -0
- package/src/lock-unlock.ts +10 -0
- package/src/prebuilt/css/design-tokens.css +401 -0
- package/src/prebuilt/js/design-tokens.js +116 -0
- package/src/prebuilt/json/figma.json +1317 -0
- package/src/prebuilt/ts/design-tokens.ts +335 -0
- package/src/private-apis.ts +12 -0
- package/src/stories/index.story.tsx +426 -0
- package/src/style.module.css +3 -0
- package/src/theme-provider.tsx +87 -0
- package/src/types/css-modules.d.ts +4 -0
- package/src/types.ts +44 -0
- package/src/use-theme-provider-styles.ts +247 -0
- package/terrazzo.config.ts +102 -0
- package/tokens/border.json +34 -0
- package/tokens/color.json +877 -0
- package/tokens/elevation.json +201 -0
- package/tokens/spacing.json +45 -0
- package/tokens/typography.json +93 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
useEffect,
|
|
11
|
+
useState,
|
|
12
|
+
useRef,
|
|
13
|
+
useId,
|
|
14
|
+
createPortal,
|
|
15
|
+
} from '@wordpress/element';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Internal dependencies
|
|
19
|
+
*/
|
|
20
|
+
import { ThemeProvider } from '../theme-provider';
|
|
21
|
+
|
|
22
|
+
const meta: Meta< typeof ThemeProvider > = {
|
|
23
|
+
title: 'Design System/Theme Provider',
|
|
24
|
+
component: ThemeProvider,
|
|
25
|
+
args: {
|
|
26
|
+
isRoot: true,
|
|
27
|
+
},
|
|
28
|
+
argTypes: {
|
|
29
|
+
children: {
|
|
30
|
+
control: false,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
parameters: {
|
|
34
|
+
controls: { expanded: true },
|
|
35
|
+
docs: { canvas: { sourceState: 'shown' } },
|
|
36
|
+
},
|
|
37
|
+
tags: [ 'status-experimental' ],
|
|
38
|
+
};
|
|
39
|
+
export default meta;
|
|
40
|
+
|
|
41
|
+
function getCSSCustomPropsFromStylesheets() {
|
|
42
|
+
const primitiveProps: Record< string, string > = {};
|
|
43
|
+
const semanticProps: Record< string, string > = {};
|
|
44
|
+
const legacyProps: Record< string, string > = {};
|
|
45
|
+
|
|
46
|
+
for ( const sheet of document.styleSheets ) {
|
|
47
|
+
try {
|
|
48
|
+
for ( const rule of sheet.cssRules || [] ) {
|
|
49
|
+
const ruleStyle = ( rule as CSSStyleRule ).style;
|
|
50
|
+
if ( ruleStyle ) {
|
|
51
|
+
for ( const name of ruleStyle ) {
|
|
52
|
+
if (
|
|
53
|
+
name.startsWith( '--wp-admin-theme' ) ||
|
|
54
|
+
name.startsWith( '--wp-components-color' )
|
|
55
|
+
) {
|
|
56
|
+
legacyProps[ name ] = ruleStyle
|
|
57
|
+
.getPropertyValue( name )
|
|
58
|
+
.trim();
|
|
59
|
+
}
|
|
60
|
+
if ( name.startsWith( '--wpds-color' ) ) {
|
|
61
|
+
if ( name.includes( 'private' ) ) {
|
|
62
|
+
primitiveProps[ name ] = ruleStyle
|
|
63
|
+
.getPropertyValue( name )
|
|
64
|
+
.trim();
|
|
65
|
+
} else {
|
|
66
|
+
semanticProps[ name ] = ruleStyle
|
|
67
|
+
.getPropertyValue( name )
|
|
68
|
+
.trim();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch ( e ) {
|
|
75
|
+
// Avoid security errors from cross-origin stylesheets
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.error( e );
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { primitiveProps, semanticProps, legacyProps };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const ColorTokenTable = ( {
|
|
86
|
+
tokens,
|
|
87
|
+
}: {
|
|
88
|
+
tokens: Record< string, string >;
|
|
89
|
+
} ) => {
|
|
90
|
+
return (
|
|
91
|
+
<ul
|
|
92
|
+
style={ {
|
|
93
|
+
listStyle: 'none',
|
|
94
|
+
display: 'flex',
|
|
95
|
+
flexDirection: 'column',
|
|
96
|
+
gap: '0.5rem',
|
|
97
|
+
} }
|
|
98
|
+
>
|
|
99
|
+
{ Object.entries( tokens ).map( ( [ name ] ) => (
|
|
100
|
+
<li
|
|
101
|
+
key={ name }
|
|
102
|
+
style={ {
|
|
103
|
+
display: 'grid',
|
|
104
|
+
gridTemplateColumns: '80px 1fr',
|
|
105
|
+
alignItems: 'center',
|
|
106
|
+
gap: '0.5rem',
|
|
107
|
+
} }
|
|
108
|
+
>
|
|
109
|
+
<span
|
|
110
|
+
style={ {
|
|
111
|
+
backgroundColor: `var(${ name })`,
|
|
112
|
+
border: '1px solid var(--wpds-color-stroke-surface-neutral)',
|
|
113
|
+
width: '100%',
|
|
114
|
+
aspectRatio: '2/1',
|
|
115
|
+
display: 'block',
|
|
116
|
+
} }
|
|
117
|
+
aria-label={ name }
|
|
118
|
+
></span>
|
|
119
|
+
<code>{ name }</code>
|
|
120
|
+
</li>
|
|
121
|
+
) ) }
|
|
122
|
+
</ul>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const DSTokensList = () => {
|
|
127
|
+
const [ props, setProps ] = useState< {
|
|
128
|
+
semanticProps: Record< string, string >;
|
|
129
|
+
primitiveProps: Record< string, string >;
|
|
130
|
+
legacyProps: Record< string, string >;
|
|
131
|
+
} >( {
|
|
132
|
+
semanticProps: {},
|
|
133
|
+
primitiveProps: {},
|
|
134
|
+
legacyProps: {},
|
|
135
|
+
} );
|
|
136
|
+
|
|
137
|
+
useEffect( () => {
|
|
138
|
+
setProps( getCSSCustomPropsFromStylesheets() );
|
|
139
|
+
}, [] );
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div style={ { color: 'var( --wpds-color-fg-content-neutral )' } }>
|
|
143
|
+
<h1>DS Color tokens</h1>
|
|
144
|
+
<h2>Semantic tokens (can be consumed directly)</h2>
|
|
145
|
+
<ColorTokenTable tokens={ props.semanticProps } />
|
|
146
|
+
<h2>Primitive tokens (should not be consumed directly)</h2>
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Click to expand</summary>
|
|
149
|
+
<ColorTokenTable tokens={ props.primitiveProps } />
|
|
150
|
+
</details>
|
|
151
|
+
<h2>Legacy tokens (should not be consumed directly)</h2>
|
|
152
|
+
<details>
|
|
153
|
+
<summary>Click to expand</summary>
|
|
154
|
+
<ColorTokenTable tokens={ props.legacyProps } />
|
|
155
|
+
</details>
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const Default: StoryObj< typeof ThemeProvider > = {
|
|
161
|
+
args: {
|
|
162
|
+
children: <DSTokensList />,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const WithPicker: StoryObj< typeof ThemeProvider > = {
|
|
167
|
+
render: ( args ) => {
|
|
168
|
+
const id = useId();
|
|
169
|
+
const [ primary, setPrimary ] = useState< undefined | string >();
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<ThemeProvider
|
|
173
|
+
{ ...args }
|
|
174
|
+
color={ {
|
|
175
|
+
primary,
|
|
176
|
+
} }
|
|
177
|
+
>
|
|
178
|
+
<div style={ { position: 'relative' } }>
|
|
179
|
+
<div
|
|
180
|
+
style={ {
|
|
181
|
+
position: 'sticky',
|
|
182
|
+
top: 0,
|
|
183
|
+
right: 0,
|
|
184
|
+
backgroundColor:
|
|
185
|
+
'var(--wpds-color-bg-surface-neutral)',
|
|
186
|
+
color: 'var( --wpds-color-fg-content-neutral )',
|
|
187
|
+
padding: '0.5rem',
|
|
188
|
+
borderRadius: '0.5rem',
|
|
189
|
+
boxShadow: '0 0 0.5rem 0 rgba(0, 0, 0, 0.1)',
|
|
190
|
+
} }
|
|
191
|
+
>
|
|
192
|
+
<div>
|
|
193
|
+
<input
|
|
194
|
+
type="color"
|
|
195
|
+
id={ id }
|
|
196
|
+
name="primary"
|
|
197
|
+
value={ primary }
|
|
198
|
+
onChange={ ( e ) =>
|
|
199
|
+
setPrimary( e.target.value )
|
|
200
|
+
}
|
|
201
|
+
/>
|
|
202
|
+
<label htmlFor={ id }>Pick the primary color</label>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
{ args.children }
|
|
206
|
+
</div>
|
|
207
|
+
</ThemeProvider>
|
|
208
|
+
);
|
|
209
|
+
},
|
|
210
|
+
args: {
|
|
211
|
+
children: <DSTokensList />,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const NestingDebug = ( { bg = '', primary = '' } ) => (
|
|
216
|
+
<div
|
|
217
|
+
style={ {
|
|
218
|
+
padding: '0.25rem',
|
|
219
|
+
color: 'var(--wpds-color-fg-content-neutral)',
|
|
220
|
+
backgroundColor: 'var(--wpds-color-bg-surface-neutral)',
|
|
221
|
+
display: 'flex',
|
|
222
|
+
alignItems: 'center',
|
|
223
|
+
flexWrap: 'wrap',
|
|
224
|
+
gap: '1rem',
|
|
225
|
+
} }
|
|
226
|
+
>
|
|
227
|
+
<pre>
|
|
228
|
+
bg: { bg } | primary: { primary }
|
|
229
|
+
</pre>
|
|
230
|
+
<span
|
|
231
|
+
style={ {
|
|
232
|
+
display: 'inline-block',
|
|
233
|
+
padding: '0.25rem',
|
|
234
|
+
borderRadius: '0.25rem',
|
|
235
|
+
backgroundColor:
|
|
236
|
+
'var(--wpds-color-bg-interactive-brand-strong)',
|
|
237
|
+
color: 'var(--wpds-color-fg-interactive-brand-strong)',
|
|
238
|
+
} }
|
|
239
|
+
>
|
|
240
|
+
primary
|
|
241
|
+
</span>
|
|
242
|
+
<span
|
|
243
|
+
style={ {
|
|
244
|
+
display: 'inline-block',
|
|
245
|
+
marginInlineStart: '0.25rem',
|
|
246
|
+
padding: '0.25rem',
|
|
247
|
+
borderRadius: '0.25rem',
|
|
248
|
+
backgroundColor:
|
|
249
|
+
'var(--wpds-color-bg-interactive-brand-weak-disabled)',
|
|
250
|
+
color: 'var(--wpds-color-fg-content-neutral)',
|
|
251
|
+
} }
|
|
252
|
+
>
|
|
253
|
+
Neutral
|
|
254
|
+
</span>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
export const NestingAndInheriting: StoryObj< typeof ThemeProvider > = {
|
|
259
|
+
render: () => {
|
|
260
|
+
return (
|
|
261
|
+
<ThemeProvider>
|
|
262
|
+
<NestingDebug bg="inherit (root)" primary="inherit (root)" />
|
|
263
|
+
<div style={ { paddingInlineStart: '1rem' } }>
|
|
264
|
+
<ThemeProvider
|
|
265
|
+
color={ {
|
|
266
|
+
bg: '#1e1e1e',
|
|
267
|
+
} }
|
|
268
|
+
>
|
|
269
|
+
<NestingDebug bg="#1e1e1e" primary="inherit (root)" />
|
|
270
|
+
<div style={ { paddingInlineStart: '1rem' } }>
|
|
271
|
+
<ThemeProvider>
|
|
272
|
+
<NestingDebug
|
|
273
|
+
bg="inherit (#1e1e1e)"
|
|
274
|
+
primary="inherit (root)"
|
|
275
|
+
/>
|
|
276
|
+
<div style={ { paddingInlineStart: '1rem' } }>
|
|
277
|
+
<ThemeProvider
|
|
278
|
+
color={ { primary: 'hotpink' } }
|
|
279
|
+
>
|
|
280
|
+
<NestingDebug
|
|
281
|
+
bg="inherit (#1e1e1e)"
|
|
282
|
+
primary="hotpink"
|
|
283
|
+
/>
|
|
284
|
+
<div
|
|
285
|
+
style={ {
|
|
286
|
+
paddingInlineStart: '1rem',
|
|
287
|
+
} }
|
|
288
|
+
>
|
|
289
|
+
<ThemeProvider
|
|
290
|
+
color={ { bg: '#f8f8f8' } }
|
|
291
|
+
>
|
|
292
|
+
<NestingDebug
|
|
293
|
+
bg="#f8f8f8"
|
|
294
|
+
primary="inherit (hotpink)"
|
|
295
|
+
/>
|
|
296
|
+
</ThemeProvider>
|
|
297
|
+
</div>
|
|
298
|
+
</ThemeProvider>
|
|
299
|
+
</div>
|
|
300
|
+
</ThemeProvider>
|
|
301
|
+
</div>
|
|
302
|
+
</ThemeProvider>
|
|
303
|
+
</div>
|
|
304
|
+
</ThemeProvider>
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
function IframeWithClonedTokenStyles( {
|
|
310
|
+
children,
|
|
311
|
+
}: {
|
|
312
|
+
children: React.ReactNode;
|
|
313
|
+
} ) {
|
|
314
|
+
const iframeRef = useRef< HTMLIFrameElement >( null );
|
|
315
|
+
const [ iframeLoaded, setIframeLoaded ] = useState( false );
|
|
316
|
+
|
|
317
|
+
// Copy the stylesheet where the DS tokens are defined to the iframe.
|
|
318
|
+
// While this technique is a bit hacky, it works well enough for the purpose
|
|
319
|
+
// of this demo.
|
|
320
|
+
// Consumers of the DS could instead reference the stylesheet directly.
|
|
321
|
+
useEffect( () => {
|
|
322
|
+
const iframe = iframeRef.current;
|
|
323
|
+
if ( ! iframe || ! iframe.contentDocument ) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const head = iframe.contentDocument.head;
|
|
328
|
+
|
|
329
|
+
// Filter styles associated with a theme provider
|
|
330
|
+
const allStyles = Array.from(
|
|
331
|
+
document.head.querySelectorAll( 'style, link[rel="stylesheet"]' )
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
allStyles.forEach( ( node ) => {
|
|
335
|
+
if ( node.tagName === 'STYLE' ) {
|
|
336
|
+
const text = node.textContent || '';
|
|
337
|
+
if ( text.includes( 'data-wpds-theme-provider-id' ) ) {
|
|
338
|
+
head.appendChild( node.cloneNode( true ) );
|
|
339
|
+
}
|
|
340
|
+
} else if ( node.tagName === 'LINK' ) {
|
|
341
|
+
// Fetch and inspect the stylesheet content
|
|
342
|
+
const href = ( node as HTMLLinkElement ).href;
|
|
343
|
+
fetch( href )
|
|
344
|
+
.then( ( res ) => res.text() )
|
|
345
|
+
.then( ( css ) => {
|
|
346
|
+
if ( css.includes( 'data-wpds-theme-provider-id' ) ) {
|
|
347
|
+
const linkClone = node.cloneNode( true );
|
|
348
|
+
head.appendChild( linkClone );
|
|
349
|
+
}
|
|
350
|
+
} )
|
|
351
|
+
.catch( ( err ) => {
|
|
352
|
+
// eslint-disable-next-line no-console
|
|
353
|
+
console.warn( 'Failed to load stylesheet:', href, err );
|
|
354
|
+
} );
|
|
355
|
+
}
|
|
356
|
+
} );
|
|
357
|
+
|
|
358
|
+
setIframeLoaded( true );
|
|
359
|
+
}, [] );
|
|
360
|
+
|
|
361
|
+
return (
|
|
362
|
+
<iframe
|
|
363
|
+
ref={ iframeRef }
|
|
364
|
+
style={ {
|
|
365
|
+
width: '100%',
|
|
366
|
+
height: '400px',
|
|
367
|
+
border: '1px solid #ccc',
|
|
368
|
+
} }
|
|
369
|
+
title="demo"
|
|
370
|
+
>
|
|
371
|
+
{ iframeLoaded &&
|
|
372
|
+
iframeRef.current?.contentDocument?.body &&
|
|
373
|
+
createPortal(
|
|
374
|
+
children,
|
|
375
|
+
iframeRef.current.contentDocument.body
|
|
376
|
+
) }
|
|
377
|
+
</iframe>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export const AcrossIframes: StoryObj< typeof ThemeProvider > = {
|
|
382
|
+
render: ( args ) => {
|
|
383
|
+
return (
|
|
384
|
+
<ThemeProvider { ...args }>
|
|
385
|
+
{ args.children }
|
|
386
|
+
|
|
387
|
+
<IframeWithClonedTokenStyles>
|
|
388
|
+
<div
|
|
389
|
+
style={ {
|
|
390
|
+
color: 'var(--wpds-color-fg-content-neutral)',
|
|
391
|
+
} }
|
|
392
|
+
>
|
|
393
|
+
In the iframe, but outside of `ThemeProvider`
|
|
394
|
+
</div>
|
|
395
|
+
<ThemeProvider
|
|
396
|
+
{ ...args }
|
|
397
|
+
// Note: the isRoot prop is necessary to apply the DS tokens to any
|
|
398
|
+
// UI rendered outside of the ThemeProvider (including overlays, etc)
|
|
399
|
+
isRoot
|
|
400
|
+
>
|
|
401
|
+
{ args.children }
|
|
402
|
+
</ThemeProvider>
|
|
403
|
+
</IframeWithClonedTokenStyles>
|
|
404
|
+
</ThemeProvider>
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
args: {
|
|
408
|
+
children: (
|
|
409
|
+
<div style={ { color: 'var(--wpds-color-fg-content-neutral)' } }>
|
|
410
|
+
Code is poetry.{ ' ' }
|
|
411
|
+
<span
|
|
412
|
+
style={ {
|
|
413
|
+
display: 'inline-block',
|
|
414
|
+
padding: '0.25rem',
|
|
415
|
+
borderRadius: '0.25rem',
|
|
416
|
+
backgroundColor:
|
|
417
|
+
'var(--wpds-color-bg-interactive-brand-strong)',
|
|
418
|
+
color: 'var(--wpds-color-fg-interactive-brand-strong)',
|
|
419
|
+
} }
|
|
420
|
+
>
|
|
421
|
+
primary
|
|
422
|
+
</span>
|
|
423
|
+
</div>
|
|
424
|
+
),
|
|
425
|
+
},
|
|
426
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { CSSProperties } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { useMemo, useId } from '@wordpress/element';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import { ThemeContext } from './context';
|
|
15
|
+
import { useThemeProviderStyles } from './use-theme-provider-styles';
|
|
16
|
+
import { type ThemeProviderProps } from './types';
|
|
17
|
+
import styles from './style.module.css';
|
|
18
|
+
|
|
19
|
+
function cssObjectToText( values: CSSProperties ) {
|
|
20
|
+
return Object.entries( values )
|
|
21
|
+
.map( ( [ key, value ] ) => `${ key }: ${ value };` )
|
|
22
|
+
.join( '' );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function generateCSSSelector( {
|
|
26
|
+
instanceId,
|
|
27
|
+
isRoot,
|
|
28
|
+
}: {
|
|
29
|
+
instanceId: string;
|
|
30
|
+
isRoot: boolean;
|
|
31
|
+
} ) {
|
|
32
|
+
const rootSel = `[data-wpds-root-provider="true"]`;
|
|
33
|
+
const instanceIdSel = `[data-wpds-theme-provider-id="${ instanceId }"]`;
|
|
34
|
+
|
|
35
|
+
const selectors = [];
|
|
36
|
+
|
|
37
|
+
if ( isRoot ) {
|
|
38
|
+
selectors.push(
|
|
39
|
+
`:root:has(.${ styles.root }${ rootSel }${ instanceIdSel })`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
selectors.push( `.${ styles.root }.${ styles.root }${ instanceIdSel }` );
|
|
44
|
+
|
|
45
|
+
return selectors.join( ',' );
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const ThemeProvider = ( {
|
|
49
|
+
children,
|
|
50
|
+
color = {},
|
|
51
|
+
isRoot = false,
|
|
52
|
+
}: ThemeProviderProps ) => {
|
|
53
|
+
const instanceId = useId();
|
|
54
|
+
|
|
55
|
+
const { themeProviderStyles, resolvedSettings } = useThemeProviderStyles( {
|
|
56
|
+
color,
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
const contextValue = useMemo(
|
|
60
|
+
() => ( {
|
|
61
|
+
resolvedSettings,
|
|
62
|
+
} ),
|
|
63
|
+
[ resolvedSettings ]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
{ themeProviderStyles ? (
|
|
69
|
+
<style>
|
|
70
|
+
{ `${ generateCSSSelector( {
|
|
71
|
+
instanceId,
|
|
72
|
+
isRoot,
|
|
73
|
+
} ) } {${ cssObjectToText( themeProviderStyles ) }}` }
|
|
74
|
+
</style>
|
|
75
|
+
) : null }
|
|
76
|
+
<div
|
|
77
|
+
data-wpds-theme-provider-id={ instanceId }
|
|
78
|
+
data-wpds-root-provider={ isRoot }
|
|
79
|
+
className={ styles.root }
|
|
80
|
+
>
|
|
81
|
+
<ThemeContext.Provider value={ contextValue }>
|
|
82
|
+
{ children }
|
|
83
|
+
</ThemeContext.Provider>
|
|
84
|
+
</div>
|
|
85
|
+
</>
|
|
86
|
+
);
|
|
87
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { type ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
export interface ThemeProviderSettings {
|
|
7
|
+
/**
|
|
8
|
+
* The set of color options to apply to the theme.
|
|
9
|
+
*/
|
|
10
|
+
color?: {
|
|
11
|
+
/**
|
|
12
|
+
* The primary seed color to use for the theme.
|
|
13
|
+
*
|
|
14
|
+
* By default, it inherits from parent `ThemeProvider`,
|
|
15
|
+
* and fallbacks to statically built CSS.
|
|
16
|
+
*/
|
|
17
|
+
primary?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The background seed color to use for the theme.
|
|
20
|
+
*
|
|
21
|
+
* By default, it inherits from parent `ThemeProvider`,
|
|
22
|
+
* and fallbacks to statically built CSS.
|
|
23
|
+
*/
|
|
24
|
+
bg?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ThemeProviderProps extends ThemeProviderSettings {
|
|
29
|
+
/**
|
|
30
|
+
* The children to render.
|
|
31
|
+
*/
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* When a ThemeProvider is the root provider, it will apply its theming
|
|
36
|
+
* settings also to the root document element (e.g. the html element).
|
|
37
|
+
* This is useful, for example, to make sure that the `html` element can
|
|
38
|
+
* consume the right background color, or that overlays rendered inside a
|
|
39
|
+
* portal can inherit the correct color scheme.
|
|
40
|
+
*
|
|
41
|
+
* @default false
|
|
42
|
+
*/
|
|
43
|
+
isRoot?: boolean;
|
|
44
|
+
}
|