@patternfly/design-tokens 1.15.2 → 1.16.1

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.
Files changed (45) hide show
  1. package/build/css/tokens-charts-dark.scss +4 -2
  2. package/build/css/tokens-charts-highcontrast-dark.scss +175 -0
  3. package/build/css/tokens-charts-highcontrast.scss +175 -0
  4. package/build/css/tokens-charts.scss +4 -2
  5. package/build/css/tokens-dark.scss +1 -1
  6. package/build/css/tokens-default.scss +1 -1
  7. package/build/css/tokens-felt-dark.scss +1 -1
  8. package/build/css/tokens-felt-glass-dark.scss +1 -1
  9. package/build/css/tokens-felt-glass.scss +1 -1
  10. package/build/css/tokens-felt-highcontrast-dark.scss +1 -1
  11. package/build/css/tokens-felt-highcontrast.scss +10 -2
  12. package/build/css/tokens-felt.scss +1 -1
  13. package/build/css/tokens-glass-dark.scss +1 -1
  14. package/build/css/tokens-glass.scss +1 -1
  15. package/build/css/tokens-palette.scss +1 -1
  16. package/build/css/tokens-redhat-highcontrast.scss +1 -1
  17. package/build.js +7 -1
  18. package/config.charts.dark.json +1 -1
  19. package/config.charts.highcontrast.dark.json +23 -0
  20. package/config.charts.highcontrast.json +23 -0
  21. package/config.charts.json +1 -1
  22. package/config.felt-highcontrast.json +2 -2
  23. package/config.layers.felt-highcontrast.json +2 -2
  24. package/config.layers.felt.json +2 -2
  25. package/package.json +1 -1
  26. package/patternfly-docs/content/token-layers-felt-dark.json +60 -16
  27. package/patternfly-docs/content/token-layers-felt-highcontrast.json +53027 -0
  28. package/patternfly-docs/content/token-layers-felt.json +65461 -0
  29. package/patternfly-docs/content/tokensTable.css +178 -0
  30. package/patternfly-docs/content/tokensTable.js +1078 -180
  31. package/patternfly-docs/content/tokensToolbar.js +240 -11
  32. package/patternfly-docs/generated/foundations-and-styles/design-tokens/all-design-tokens/design-tokens.js +37 -3
  33. package/patternfly-docs/generated/index.js +1 -0
  34. package/plugins/export-patternfly-tokens/dist/ui.html +9 -9
  35. package/plugins/export-patternfly-tokens/src/ui.tsx +8 -8
  36. package/tokens/default/dark/charts.dark.json +11 -3
  37. package/tokens/default/dark/charts.highcontrast.dark.json +11 -3
  38. package/tokens/default/light/charts.highcontrast.json +11 -3
  39. package/tokens/default/light/charts.json +11 -3
  40. package/tokens/felt/dark/felt.color.dark.json +41 -41
  41. package/tokens/felt/glass/felt.color.glass.json +9 -9
  42. package/tokens/felt/glass-dark/felt.color.glass.dark.json +41 -41
  43. package/tokens/felt/highcontrast/felt.color.highcontrast.json +9 -9
  44. package/tokens/felt/highcontrast-dark/felt.color.highcontrast.dark.json +9 -9
  45. package/tokens/felt/light/felt.color.json +9 -9
@@ -1,17 +1,148 @@
1
1
  import React from 'react';
2
2
  import {
3
+ Button,
4
+ Divider,
3
5
  MenuToggle,
4
6
  SearchInput,
5
7
  Select,
8
+ SelectGroup,
6
9
  SelectList,
7
10
  SelectOption,
8
11
  Toolbar,
9
12
  ToolbarItem,
10
13
  ToolbarContent,
14
+ Tooltip,
11
15
  capitalize
12
16
  } from '@patternfly/react-core';
13
17
  import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
14
18
 
19
+ /**
20
+ * Theme configuration constants
21
+ * Structure: [theme family, light/dark mode, contrast variant]
22
+ */
23
+ const THEME_LABEL_PARTS = {
24
+ default: ['Default theme', 'Light', 'Default contrast'],
25
+ dark: ['Default theme', 'Dark', 'Default contrast'],
26
+ glass: ['Default theme', 'Light', 'Glass'],
27
+ 'glass-dark': ['Default theme', 'Dark', 'Glass'],
28
+ highcontrast: ['Default theme', 'Light', 'High contrast'],
29
+ 'highcontrast-dark': ['Default theme', 'Dark', 'High contrast'],
30
+ redhat: ['Unified theme', 'Light', 'Default contrast'],
31
+ 'redhat-dark': ['Unified theme', 'Dark', 'Default contrast'],
32
+ 'redhat-glass': ['Unified theme', 'Light', 'Glass'],
33
+ 'redhat-glass-dark': ['Unified theme', 'Dark', 'Glass'],
34
+ 'redhat-highcontrast': ['Unified theme', 'Light', 'High contrast'],
35
+ 'redhat-highcontrast-dark': ['Unified theme', 'Dark', 'High contrast']
36
+ };
37
+
38
+ /** Full segment → acronym mapping */
39
+ const THEME_SEGMENT_ABBREV = {
40
+ 'Default theme': 'DT',
41
+ 'Unified theme': 'UT',
42
+ Light: 'Lt',
43
+ Dark: 'Dk',
44
+ 'Default contrast': 'DC',
45
+ 'High contrast': 'HC',
46
+ Glass: 'Gl'
47
+ };
48
+
49
+ // Pre-compute static values to avoid recalculating on every render
50
+ const THEME_LABEL_ROWS = Object.values(THEME_LABEL_PARTS);
51
+ const THEME_LABEL_COL_WIDTHS = (() => {
52
+ const rows = THEME_LABEL_ROWS;
53
+ return [
54
+ Math.max(...rows.map((r) => r[0].length)),
55
+ Math.max(...rows.map((r) => r[1].length)),
56
+ Math.max(...rows.map((r) => r[2].length))
57
+ ];
58
+ })();
59
+
60
+ const toAbbrevParts = (fullParts) => fullParts.map((p) => THEME_SEGMENT_ABBREV[p] || p);
61
+
62
+ const THEME_ACRONYM_PARTS = Object.fromEntries(
63
+ Object.entries(THEME_LABEL_PARTS).map(([key, parts]) => [key, toAbbrevParts(parts)])
64
+ );
65
+
66
+ const THEME_ACRONYM_ROWS = Object.values(THEME_ACRONYM_PARTS);
67
+ const THEME_ACRONYM_COL_WIDTHS = (() => {
68
+ const rows = THEME_ACRONYM_ROWS;
69
+ return [
70
+ Math.max(...rows.map((r) => r[0].length)),
71
+ Math.max(...rows.map((r) => r[1].length)),
72
+ Math.max(...rows.map((r) => r[2].length))
73
+ ];
74
+ })();
75
+
76
+ const padThemeCol = (s, w) => s.padEnd(w, ' ');
77
+
78
+ const formatAlignedThemeParts = (parts) => {
79
+ const [w0, w1, w2] = THEME_LABEL_COL_WIDTHS;
80
+ return `${padThemeCol(parts[0], w0)} | ${padThemeCol(parts[1], w1)} | ${padThemeCol(parts[2], w2)}`;
81
+ };
82
+
83
+ export const getThemeDisplayName = (themeName) => {
84
+ if (themeName === 'all') {
85
+ return 'All themes';
86
+ }
87
+ const parts = THEME_LABEL_PARTS[themeName];
88
+ if (parts) {
89
+ return formatAlignedThemeParts(parts);
90
+ }
91
+ return themeName.split('-').map((part) => capitalize(part)).join(' ');
92
+ };
93
+
94
+ export const ThemeLabelAbbrevContext = React.createContext(false);
95
+
96
+ /**
97
+ * Abbreviated theme row with single tooltip showing full theme name.
98
+ * Improves accessibility by avoiding multiple tab stops.
99
+ */
100
+ const ThemeAbbrevSegments = ({ themeName }) => {
101
+ const abbrParts = THEME_ACRONYM_PARTS[themeName];
102
+ const fullParts = THEME_LABEL_PARTS[themeName];
103
+ const [w0, w1, w2] = THEME_ACRONYM_COL_WIDTHS;
104
+ const paddedCols = [
105
+ padThemeCol(abbrParts[0], w0),
106
+ padThemeCol(abbrParts[1], w1),
107
+ padThemeCol(abbrParts[2], w2)
108
+ ];
109
+
110
+ const fullLabel = formatAlignedThemeParts(fullParts);
111
+ const abbreviatedText = `${paddedCols[0]} | ${paddedCols[1]} | ${paddedCols[2]}`;
112
+
113
+ return (
114
+ <Tooltip content={fullLabel} position="top">
115
+ <Button variant="plain" className="ws-theme-display-label ws-theme-abbr-trigger" tabIndex={-1}>
116
+ {abbreviatedText}
117
+ </Button>
118
+ </Tooltip>
119
+ );
120
+ };
121
+
122
+ /** Renders theme text; known themes use monospace + padded spaces so `|` columns line up */
123
+ export const ThemeDisplayLabel = ({ themeName, fullLabel = false }) => {
124
+ const contextAbbrev = React.useContext(ThemeLabelAbbrevContext);
125
+ const abbreviated = fullLabel ? false : contextAbbrev;
126
+ const parts = THEME_LABEL_PARTS[themeName];
127
+
128
+ if (themeName === 'all') {
129
+ return <span>All themes</span>;
130
+ }
131
+ if (parts) {
132
+ if (abbreviated) {
133
+ return <ThemeAbbrevSegments themeName={themeName} />;
134
+ }
135
+ return <span className="ws-theme-display-label">{formatAlignedThemeParts(parts)}</span>;
136
+ }
137
+ return <span>{themeName.split('-').map((p) => capitalize(p)).join(' ')}</span>;
138
+ };
139
+
140
+
141
+ // Category configuration constants
142
+ const TOKEN_CATEGORY_GROUP_PRIMARY = new Set(['chart', 'semantic']);
143
+ // Only palette tokens are truly theme-invariant (base tokens CAN vary, e.g., high contrast)
144
+ const THEME_INVARIANT_CATEGORIES = new Set(['palette']);
145
+
15
146
  const TokensToolbarSelect = ({ selectedCategory, setSelectedCategory, categories }) => {
16
147
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
17
148
  const handleSelect = (_e, category) => {
@@ -21,6 +152,10 @@ const TokensToolbarSelect = ({ selectedCategory, setSelectedCategory, categories
21
152
  setIsSelectOpen(!isSelectOpen);
22
153
  };
23
154
 
155
+ const primaryCategories = categories.filter((c) => TOKEN_CATEGORY_GROUP_PRIMARY.has(c));
156
+ const secondaryCategories = categories.filter((c) => !TOKEN_CATEGORY_GROUP_PRIMARY.has(c));
157
+ const showGroupDivider = primaryCategories.length > 0 && secondaryCategories.length > 0;
158
+
24
159
  const SelectToggle = (toggleRef) => (
25
160
  <MenuToggle
26
161
  icon={<FilterIcon />}
@@ -32,6 +167,13 @@ const TokensToolbarSelect = ({ selectedCategory, setSelectedCategory, categories
32
167
  </MenuToggle>
33
168
  );
34
169
 
170
+ const renderCategoryOptions = (list) =>
171
+ list.map((category, idx) => (
172
+ <SelectOption key={category} value={category} isSelected={selectedCategory === category}>
173
+ {capitalize(category)} tokens
174
+ </SelectOption>
175
+ ));
176
+
35
177
  return (
36
178
  <Select
37
179
  aria-label="Select Input"
@@ -41,32 +183,107 @@ const TokensToolbarSelect = ({ selectedCategory, setSelectedCategory, categories
41
183
  onOpenChange={(isOpen) => setIsSelectOpen(isOpen)}
42
184
  onSelect={handleSelect}
43
185
  >
44
- <SelectList>
45
- {categories.map((category, idx) => (
46
- <SelectOption key={idx} value={category} isSelected={selectedCategory === category}>
47
- {capitalize(category)} tokens
48
- </SelectOption>
49
- ))}
50
- </SelectList>
186
+ {primaryCategories.length > 0 && (
187
+ <SelectGroup>
188
+ <SelectList>{renderCategoryOptions(primaryCategories)}</SelectList>
189
+ </SelectGroup>
190
+ )}
191
+ {showGroupDivider && <Divider />}
192
+ {secondaryCategories.length > 0 && (
193
+ <SelectGroup>
194
+ <SelectList>{renderCategoryOptions(secondaryCategories)}</SelectList>
195
+ </SelectGroup>
196
+ )}
51
197
  </Select>
52
198
  );
53
199
  };
54
200
 
55
- export const TokensToolbar = ({
201
+ const TokensThemeSelect = ({ selectedTheme, setSelectedTheme, themeOptions, selectedCategory }) => {
202
+ const [isSelectOpen, setIsSelectOpen] = React.useState(false);
203
+ const handleSelect = (_e, themeName) => {
204
+ if (!(selectedTheme === themeName)) {
205
+ setSelectedTheme(themeName);
206
+ }
207
+ setIsSelectOpen(!isSelectOpen);
208
+ };
209
+
210
+ // Theme selector is disabled for categories that don't have theme variants
211
+ const isDisabled = THEME_INVARIANT_CATEGORIES.has(selectedCategory);
212
+
213
+ const ariaSelectedTheme = getThemeDisplayName(selectedTheme)
214
+ .replace(/\|/g, ' ')
215
+ .replace(/\s+/g, ' ')
216
+ .trim();
217
+
218
+ const SelectToggle = (toggleRef) => (
219
+ <MenuToggle
220
+ aria-label={`${ariaSelectedTheme}, Filter by theme`}
221
+ ref={toggleRef}
222
+ onClick={() => !isDisabled && setIsSelectOpen(!isSelectOpen)}
223
+ isExpanded={isSelectOpen}
224
+ isDisabled={isDisabled}
225
+ >
226
+ <ThemeDisplayLabel themeName={selectedTheme} fullLabel />
227
+ </MenuToggle>
228
+ );
229
+
230
+ const themesAfterAll = themeOptions.slice(1);
231
+ const defaultThemeOptions = themesAfterAll.filter((name) => !name.startsWith('redhat'));
232
+ const unifiedThemeOptions = themesAfterAll.filter((name) => name.startsWith('redhat'));
233
+ const showUnifiedDivider = defaultThemeOptions.length > 0 && unifiedThemeOptions.length > 0;
234
+
235
+ const renderThemeOption = (themeName) => (
236
+ <SelectOption key={themeName} value={themeName} isSelected={selectedTheme === themeName}>
237
+ <ThemeDisplayLabel themeName={themeName} fullLabel />
238
+ </SelectOption>
239
+ );
240
+
241
+ return (
242
+ <Select
243
+ aria-label="Select theme"
244
+ toggle={SelectToggle}
245
+ isOpen={isSelectOpen}
246
+ onOpenChange={(isOpen) => setIsSelectOpen(isOpen)}
247
+ onSelect={handleSelect}
248
+ isDisabled={isDisabled}
249
+ >
250
+ <SelectGroup label={null}>
251
+ <SelectList>{renderThemeOption('all')}</SelectList>
252
+ </SelectGroup>
253
+ <Divider />
254
+ {defaultThemeOptions.length > 0 && (
255
+ <SelectGroup label={null}>
256
+ <SelectList>{defaultThemeOptions.map(renderThemeOption)}</SelectList>
257
+ </SelectGroup>
258
+ )}
259
+ {showUnifiedDivider && <Divider />}
260
+ {unifiedThemeOptions.length > 0 && (
261
+ <SelectGroup label={null}>
262
+ <SelectList>{unifiedThemeOptions.map(renderThemeOption)}</SelectList>
263
+ </SelectGroup>
264
+ )}
265
+ </Select>
266
+ );
267
+ };
268
+
269
+ const TokensToolbarComponent = ({
56
270
  selectedCategory,
57
271
  setSelectedCategory,
272
+ selectedTheme,
273
+ setSelectedTheme,
58
274
  searchValue,
59
275
  setSearchValue,
60
276
  resultsCount,
61
- categories
277
+ categories,
278
+ themeOptions
62
279
  }) => {
63
280
  return (
64
281
  <Toolbar>
65
282
  <ToolbarContent>
66
283
  <ToolbarItem variant="search-filter">
67
284
  <SearchInput
68
- aria-label="Search all tokens"
69
- placeholder="Search all tokens"
285
+ aria-label="Search for a token"
286
+ placeholder="Search for a token"
70
287
  value={searchValue}
71
288
  onChange={(_event, value) => setSearchValue(value)}
72
289
  onClear={() => setSearchValue('')}
@@ -74,6 +291,15 @@ export const TokensToolbar = ({
74
291
  />
75
292
  </ToolbarItem>
76
293
 
294
+ <ToolbarItem>
295
+ <TokensThemeSelect
296
+ selectedTheme={selectedTheme}
297
+ setSelectedTheme={setSelectedTheme}
298
+ themeOptions={themeOptions}
299
+ selectedCategory={selectedCategory}
300
+ />
301
+ </ToolbarItem>
302
+
77
303
  <ToolbarItem>
78
304
  <TokensToolbarSelect
79
305
  selectedCategory={selectedCategory}
@@ -85,3 +311,6 @@ export const TokensToolbar = ({
85
311
  </Toolbar>
86
312
  );
87
313
  };
314
+
315
+ // Memoize the toolbar to prevent unnecessary re-renders that cause SearchInput to lose focus
316
+ export const TokensToolbar = React.memo(TokensToolbarComponent);
@@ -2,6 +2,16 @@ import React from 'react';
2
2
  import { AutoLinkHeader, Example, Link as PatternflyThemeLink } from '@patternfly/documentation-framework/components';
3
3
  import * as defaultTokens from '../../../../content/./token-layers-default.json';
4
4
  import * as darkTokens from '../../../../content/./token-layers-dark.json';
5
+ import * as glassTokens from '../../../../content/./token-layers-glass.json';
6
+ import * as glassDarkTokens from '../../../../content/./token-layers-glass-dark.json';
7
+ import * as highcontrastTokens from '../../../../content/./token-layers-highcontrast.json';
8
+ import * as highcontrastDarkTokens from '../../../../content/./token-layers-highcontrast-dark.json';
9
+ import * as redhatTokens from '../../../../content/./token-layers-redhat.json';
10
+ import * as redhatDarkTokens from '../../../../content/./token-layers-redhat-dark.json';
11
+ import * as redhatGlassTokens from '../../../../content/./token-layers-redhat-glass.json';
12
+ import * as redhatGlassDarkTokens from '../../../../content/./token-layers-redhat-glass-dark.json';
13
+ import * as redhatHighcontrastTokens from '../../../../content/./token-layers-redhat-highcontrast.json';
14
+ import * as redhatHighcontrastDarkTokens from '../../../../content/./token-layers-redhat-highcontrast-dark.json';
5
15
  import { TokensTable } from '../../../../content/./tokensTable.js';
6
16
  const pageData = {
7
17
  "id": "All design tokens",
@@ -16,21 +26,45 @@ const pageData = {
16
26
  "tabName": null,
17
27
  "slug": "/foundations-and-styles/design-tokens/all-design-tokens/design-tokens",
18
28
  "sourceLink": "https://github.com/patternfly/patternfly-org/blob/main/packages/module/patternfly-docs/content/all-patternfly-tokens.md",
19
- "relPath": "packages/module/patternfly-docs/content/all-patternfly-tokens.md"
29
+ "relPath": "packages/module/patternfly-docs/content/all-patternfly-tokens.md",
30
+ "sortValue": 5
20
31
  };
21
32
  pageData.liveContext = {
22
33
  defaultTokens,
23
34
  darkTokens,
35
+ glassTokens,
36
+ glassDarkTokens,
37
+ highcontrastTokens,
38
+ highcontrastDarkTokens,
39
+ redhatTokens,
40
+ redhatDarkTokens,
41
+ redhatGlassTokens,
42
+ redhatGlassDarkTokens,
43
+ redhatHighcontrastTokens,
44
+ redhatHighcontrastDarkTokens,
24
45
  TokensTable
25
46
  };
26
- pageData.relativeImports = "import * as defaultTokens from 'content/./token-layers-default.json';,import * as darkTokens from 'content/./token-layers-dark.json';,import { TokensTable } from 'content/./tokensTable.js';"
47
+ pageData.relativeImports = "import * as defaultTokens from 'content/./token-layers-default.json';,import * as darkTokens from 'content/./token-layers-dark.json';,import * as glassTokens from 'content/./token-layers-glass.json';,import * as glassDarkTokens from 'content/./token-layers-glass-dark.json';,import * as highcontrastTokens from 'content/./token-layers-highcontrast.json';,import * as highcontrastDarkTokens from 'content/./token-layers-highcontrast-dark.json';,import * as redhatTokens from 'content/./token-layers-redhat.json';,import * as redhatDarkTokens from 'content/./token-layers-redhat-dark.json';,import * as redhatGlassTokens from 'content/./token-layers-redhat-glass.json';,import * as redhatGlassDarkTokens from 'content/./token-layers-redhat-glass-dark.json';,import * as redhatHighcontrastTokens from 'content/./token-layers-redhat-highcontrast.json';,import * as redhatHighcontrastDarkTokens from 'content/./token-layers-redhat-highcontrast-dark.json';,import { TokensTable } from 'content/./tokensTable.js';"
27
48
  pageData.examples = {
28
49
 
29
50
  };
30
51
 
31
52
  const Component = () => (
32
53
  <React.Fragment>
33
- <TokensTable tokenJson={{default: defaultTokens, dark: darkTokens}}/>
54
+ <TokensTable tokenJson={{
55
+ default: defaultTokens,
56
+ dark: darkTokens,
57
+ glass: glassTokens,
58
+ 'glass-dark': glassDarkTokens,
59
+ highcontrast: highcontrastTokens,
60
+ 'highcontrast-dark': highcontrastDarkTokens,
61
+ redhat: redhatTokens,
62
+ 'redhat-dark': redhatDarkTokens,
63
+ 'redhat-glass': redhatGlassTokens,
64
+ 'redhat-glass-dark': redhatGlassDarkTokens,
65
+ 'redhat-highcontrast': redhatHighcontrastTokens,
66
+ 'redhat-highcontrast-dark': redhatHighcontrastDarkTokens
67
+ }}/>
34
68
  </React.Fragment>
35
69
  );
36
70
  Component.displayName = 'FoundationsAndStylesDesignTokensAllDesignTokensDesignTokensDocs';
@@ -7,6 +7,7 @@ module.exports = {
7
7
  subsection: "design-tokens",
8
8
  source: "design-tokens",
9
9
  tabName: null,
10
+ sortValue: 5,
10
11
  Component: () => import(/* webpackChunkName: "foundations-and-styles/design-tokens/all-design-tokens/design-tokens/index" */ './foundations-and-styles/design-tokens/all-design-tokens/design-tokens')
11
12
  }
12
13
  };