@gitlab/ui 72.12.4 → 72.14.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [72.14.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v72.13.0...v72.14.0) (2024-01-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * **tailwindcss:** extend config with colors and spacing scale ([2c7a006](https://gitlab.com/gitlab-org/gitlab-ui/commit/2c7a006e4c605c58a753d8bfbc8ceb0bdc101cf8))
7
+
8
+ # [72.13.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v72.12.4...v72.13.0) (2024-01-30)
9
+
10
+
11
+ ### Features
12
+
13
+ * **GlDuoChat:** slash commands for chat as a prop ([af86eb8](https://gitlab.com/gitlab-org/gitlab-ui/commit/af86eb883fad3b63b012c25fbe19f4cfa773f8ba))
14
+
1
15
  ## [72.12.4](https://gitlab.com/gitlab-org/gitlab-ui/compare/v72.12.3...v72.12.4) (2024-01-26)
2
16
 
3
17
 
@@ -30,27 +30,13 @@ const i18n = {
30
30
  CHAT_LEGAL_DISCLAIMER: "May provide inappropriate responses not representative of GitLab's views. Do not input personal data.",
31
31
  CHAT_DEFAULT_PREDEFINED_PROMPTS: ['How do I change my password in GitLab?', 'How do I fork a project?', 'How do I clone a repository?', 'How do I create a template?']
32
32
  };
33
- const slashCommands = [{
34
- name: '/reset',
35
- shouldSubmit: true,
36
- description: 'Reset conversation, ignore the previous messages.'
37
- }, {
38
- name: '/tests',
39
- shouldSubmit: false,
40
- description: 'Write tests for the selected snippet.'
41
- }, {
42
- name: '/refactor',
43
- shouldSubmit: false,
44
- description: 'Refactor the selected snippet.'
45
- }, {
46
- name: '/explain',
47
- shouldSubmit: false,
48
- description: 'Explain the selected snippet.'
49
- }];
50
33
  const isMessage = item => Boolean(item) && (item === null || item === void 0 ? void 0 : item.role);
34
+ const isSlashCommand = command => Boolean(command) && (command === null || command === void 0 ? void 0 : command.name) && command.description;
51
35
 
52
36
  // eslint-disable-next-line unicorn/no-array-callback-reference
53
37
  const itemsValidator = items => items.every(isMessage);
38
+ // eslint-disable-next-line unicorn/no-array-callback-reference
39
+ const slashCommandsValidator = commands => commands.every(isSlashCommand);
54
40
  var script = {
55
41
  name: 'GlDuoChat',
56
42
  components: {
@@ -147,12 +133,13 @@ var script = {
147
133
  default: i18n.CHAT_DEFAULT_TITLE
148
134
  },
149
135
  /**
150
- * Whether the slash commands should be available to user when typing the prompt.
136
+ * Array of slash commands to display in the chat.
151
137
  */
152
- withSlashCommands: {
153
- type: Boolean,
138
+ slashCommands: {
139
+ type: Array,
154
140
  required: false,
155
- default: false
141
+ default: () => [],
142
+ validator: slashCommandsValidator
156
143
  }
157
144
  },
158
145
  data() {
@@ -164,6 +151,9 @@ var script = {
164
151
  };
165
152
  },
166
153
  computed: {
154
+ withSlashCommands() {
155
+ return this.slashCommands.length > 0;
156
+ },
167
157
  hasMessages() {
168
158
  var _this$messages;
169
159
  return ((_this$messages = this.messages) === null || _this$messages === void 0 ? void 0 : _this$messages.length) > 0;
@@ -188,13 +178,13 @@ var script = {
188
178
  },
189
179
  filteredSlashCommands() {
190
180
  const caseInsensitivePrompt = this.prompt.toLowerCase();
191
- return slashCommands.filter(c => c.name.toLowerCase().startsWith(caseInsensitivePrompt));
181
+ return this.slashCommands.filter(c => c.name.toLowerCase().startsWith(caseInsensitivePrompt));
192
182
  },
193
183
  shouldShowSlashCommands() {
194
184
  if (!this.withSlashCommands) return false;
195
185
  const caseInsensitivePrompt = this.prompt.toLowerCase();
196
186
  const startsWithSlash = caseInsensitivePrompt.startsWith('/');
197
- const startsWithSlashCommand = slashCommands.some(c => caseInsensitivePrompt.startsWith(c.name));
187
+ const startsWithSlashCommand = this.slashCommands.some(c => caseInsensitivePrompt.startsWith(c.name));
198
188
  return startsWithSlash && this.filteredSlashCommands.length && !startsWithSlashCommand;
199
189
  },
200
190
  inputPlaceholder() {
@@ -356,4 +346,4 @@ var __vue_staticRenderFns__ = [];
356
346
  );
357
347
 
358
348
  export default __vue_component__;
359
- export { i18n, slashCommands };
349
+ export { i18n };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 26 Jan 2024 18:13:36 GMT
3
+ * Generated on Tue, 30 Jan 2024 18:03:42 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 26 Jan 2024 18:13:37 GMT
3
+ * Generated on Tue, 30 Jan 2024 18:03:42 GMT
4
4
  */
5
5
 
6
6
  :root.gl-dark {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 26 Jan 2024 18:13:37 GMT
3
+ * Generated on Tue, 30 Jan 2024 18:03:42 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#133a03";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Fri, 26 Jan 2024 18:13:36 GMT
3
+ * Generated on Tue, 30 Jan 2024 18:03:42 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#ddfab7";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Fri, 26 Jan 2024 18:13:37 GMT
3
+ // Generated on Tue, 30 Jan 2024 18:03:43 GMT
4
4
 
5
5
  $red-950: #fff4f3;
6
6
  $red-900: #fcf1ef;
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Fri, 26 Jan 2024 18:13:37 GMT
3
+ // Generated on Tue, 30 Jan 2024 18:03:42 GMT
4
4
 
5
5
  $gl-line-height-52: 3.25rem;
6
6
  $gl-line-height-44: 2.75rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "72.12.4",
3
+ "version": "72.14.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "build-tokens": "node ./bin/build_tokens.js",
31
31
  "clean": "rm -r dist storybook src/scss/utilities.scss",
32
32
  "cy:a11y": "cypress run --browser chrome --env grepTags=@a11y",
33
- "cy:edge": "cypress run --browser edge --env grepTags=-@a11y",
33
+ "cy:edge": "cypress run --browser edge --env grepTags=-@a11y+-@storybook",
34
34
  "cy:run": "cypress run --browser firefox --env grepTags=-@a11y",
35
35
  "start": "yarn storybook",
36
36
  "storybook": "yarn storybook-prep && storybook dev --ci --host ${STORYBOOK_HOST:-localhost} --port 9001 -c .storybook",
@@ -91,16 +91,16 @@
91
91
  },
92
92
  "devDependencies": {
93
93
  "@arkweid/lefthook": "0.7.7",
94
- "@babel/core": "^7.23.7",
94
+ "@babel/core": "^7.23.9",
95
95
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
96
96
  "@babel/plugin-proposal-optional-chaining": "^7.21.0",
97
- "@babel/preset-env": "^7.23.8",
97
+ "@babel/preset-env": "^7.23.9",
98
98
  "@babel/preset-react": "^7.23.3",
99
99
  "@cypress/grep": "^4.0.1",
100
100
  "@gitlab/eslint-plugin": "19.4.0",
101
101
  "@gitlab/fonts": "^1.3.0",
102
102
  "@gitlab/stylelint-config": "5.0.1",
103
- "@gitlab/svgs": "3.75.0",
103
+ "@gitlab/svgs": "3.79.0",
104
104
  "@rollup/plugin-commonjs": "^11.1.0",
105
105
  "@rollup/plugin-node-resolve": "^7.1.3",
106
106
  "@rollup/plugin-replace": "^2.3.2",
@@ -18,6 +18,7 @@ consumer component.
18
18
  :predefined-prompts="predefinedPrompts"
19
19
  :badge-help-page-url="badgeHelpPageUrl"
20
20
  :tool-name="toolName"
21
+ :slash-commands="slashCommands"
21
22
  @chat-hidden="onChatHidden"
22
23
  @send-chat-prompt="onSendChatPrompt"
23
24
  @track-feedback="onTrackFeedback"
@@ -141,3 +142,34 @@ With this template in place, consumer is left with the following things to imple
141
142
 
142
143
  - Send the new user's prompt. For Duo Chat, we rely on GraphQL mutation for this purpose.
143
144
  - Send user feedback to the telemetry of your choice when `track-feedback` event arrives.
145
+
146
+ ## Slash commands
147
+
148
+ One of the props accepted by the component is the `slashCommands`. This is an `Array` of
149
+ the commands, shown to user when they start typing the prompt with a slash (`/`)
150
+ character.
151
+
152
+ ```javascript
153
+ <script>
154
+ const slashCommands = [
155
+ {
156
+ name: '/mycommand', // This is the exact name of my command as it will be submitted
157
+ shouldSubmit: true, // If the command should be submitted right away without free text
158
+ description: 'The description of my super-duper command.',
159
+ },
160
+ ...
161
+ ];
162
+ export default {
163
+ ...
164
+ options: {
165
+ slashCommands
166
+ }
167
+ }
168
+ <script>
169
+ <template>
170
+ <gl-duo-chat
171
+ ...
172
+ :slash-commands="slashCommands"
173
+ />
174
+ </template>
175
+ ```
@@ -7,10 +7,42 @@ import GlDropdownItem from '../../../base/dropdown/dropdown_item.vue';
7
7
  import DuoChatLoader from './components/duo_chat_loader/duo_chat_loader.vue';
8
8
  import DuoChatPredefinedPrompts from './components/duo_chat_predefined_prompts/duo_chat_predefined_prompts.vue';
9
9
  import DuoChatConversation from './components/duo_chat_conversation/duo_chat_conversation.vue';
10
- import GlDuoChat, { slashCommands } from './duo_chat.vue';
10
+ import GlDuoChat from './duo_chat.vue';
11
11
 
12
12
  import { MESSAGE_MODEL_ROLES, CHAT_RESET_MESSAGE } from './constants';
13
13
 
14
+ const slashCommands = [
15
+ {
16
+ name: '/reset',
17
+ shouldSubmit: true,
18
+ description: 'Reset conversation, ignore the previous messages.',
19
+ },
20
+ {
21
+ name: '/tests',
22
+ description: 'Write tests for the selected snippet.',
23
+ },
24
+ {
25
+ name: '/refactor',
26
+ description: 'Refactor the selected snippet.',
27
+ },
28
+ {
29
+ name: '/explain',
30
+ description: 'Explain the selected snippet.',
31
+ },
32
+ ];
33
+
34
+ const invalidSlashCommands = [
35
+ {
36
+ name: '/foo',
37
+ },
38
+ {
39
+ description: '/bar',
40
+ },
41
+ {
42
+ shouldSubmit: true,
43
+ },
44
+ ];
45
+
14
46
  const generatePartialSlashCommands = () => {
15
47
  const res = [];
16
48
  slashCommands.forEach((command) => {
@@ -421,21 +453,14 @@ describe('GlDuoChat', () => {
421
453
  slashCommandsNames.filter((name) => commands.includes(name));
422
454
 
423
455
  describe('rendering', () => {
424
- describe('without the `withSlashCommands` enabled', () => {
456
+ describe('without slash commands', () => {
425
457
  it('does not render slash commands by default', () => {
426
- createComponent({
427
- propsData: {
428
- withSlashCommands: false,
429
- },
430
- });
458
+ createComponent();
431
459
  expect(findSlashCommandsCard().exists()).toBe(false);
432
460
  });
433
461
 
434
462
  it('does not render slash commands when prompt is "/"', async () => {
435
463
  createComponent({
436
- propsData: {
437
- withSlashCommands: false,
438
- },
439
464
  data: {
440
465
  prompt: '/',
441
466
  },
@@ -446,20 +471,16 @@ describe('GlDuoChat', () => {
446
471
  });
447
472
 
448
473
  it('shows default placeholder in the chat input', () => {
449
- createComponent({
450
- propsData: {
451
- withSlashCommands: false,
452
- },
453
- });
474
+ createComponent();
454
475
  expect(findChatInput().attributes('placeholder')).toBe('GitLab Duo Chat');
455
476
  });
456
477
  });
457
478
 
458
- describe('with the `withSlashCommands` enabled', () => {
479
+ describe('with slash commands', () => {
459
480
  it('does not render slash commands by default', async () => {
460
481
  createComponent({
461
482
  propsData: {
462
- withSlashCommands: true,
483
+ slashCommands,
463
484
  },
464
485
  });
465
486
 
@@ -470,7 +491,7 @@ describe('GlDuoChat', () => {
470
491
  it('renders all slash commands when prompt is "/"', async () => {
471
492
  createComponent({
472
493
  propsData: {
473
- withSlashCommands: true,
494
+ slashCommands,
474
495
  },
475
496
  data: {
476
497
  prompt: '/',
@@ -490,19 +511,29 @@ describe('GlDuoChat', () => {
490
511
  it('shows the correct placeholder in the chat input', () => {
491
512
  createComponent({
492
513
  propsData: {
493
- withSlashCommands: true,
514
+ slashCommands,
494
515
  },
495
516
  });
496
517
  expect(findChatInput().attributes('placeholder')).toBe('Type "/" for slash commands');
497
518
  });
498
519
 
520
+ it('prevents passing down invalid slash commands', () => {
521
+ expect(() => {
522
+ wrapper = shallowMount(GlDuoChat, {
523
+ propsData: {
524
+ slashCommands: [...slashCommands, ...invalidSlashCommands],
525
+ },
526
+ });
527
+ }).toHaveLength(0);
528
+ });
529
+
499
530
  describe('when the prompt includes the "/" character or no characters', () => {
500
531
  it.each(['', '//', '\\', 'foo', '/foo'])(
501
532
  'does not render the slash commands if prompt is "$prompt"',
502
533
  async (prompt) => {
503
534
  createComponent({
504
535
  propsData: {
505
- withSlashCommands: true,
536
+ slashCommands,
506
537
  },
507
538
  data: {
508
539
  prompt,
@@ -521,7 +552,7 @@ describe('GlDuoChat', () => {
521
552
  async (prompt) => {
522
553
  createComponent({
523
554
  propsData: {
524
- withSlashCommands: true,
555
+ slashCommands,
525
556
  },
526
557
  data: {
527
558
  prompt,
@@ -540,7 +571,7 @@ describe('GlDuoChat', () => {
540
571
  async (prompt) => {
541
572
  createComponent({
542
573
  propsData: {
543
- withSlashCommands: true,
574
+ slashCommands,
544
575
  },
545
576
  data: {
546
577
  prompt,
@@ -575,7 +606,7 @@ describe('GlDuoChat', () => {
575
606
  async ({ prompt, expectedCommands } = {}) => {
576
607
  createComponent({
577
608
  propsData: {
578
- withSlashCommands: true,
609
+ slashCommands,
579
610
  },
580
611
  data: {
581
612
  prompt,
@@ -595,7 +626,7 @@ describe('GlDuoChat', () => {
595
626
  beforeEach(() => {
596
627
  createComponent({
597
628
  propsData: {
598
- withSlashCommands: true,
629
+ slashCommands,
599
630
  messages,
600
631
  },
601
632
  data: {
@@ -664,7 +695,7 @@ describe('GlDuoChat', () => {
664
695
  beforeEach(() => {
665
696
  createComponent({
666
697
  propsData: {
667
- withSlashCommands: true,
698
+ slashCommands,
668
699
  messages,
669
700
  },
670
701
  data: {
@@ -12,6 +12,26 @@ import {
12
12
  renderMarkdown,
13
13
  } from './mock_data';
14
14
 
15
+ const slashCommands = [
16
+ {
17
+ name: '/reset',
18
+ shouldSubmit: true,
19
+ description: 'Reset conversation, ignore the previous messages.',
20
+ },
21
+ {
22
+ name: '/tests',
23
+ description: 'Write tests for the selected snippet.',
24
+ },
25
+ {
26
+ name: '/refactor',
27
+ description: 'Refactor the selected snippet.',
28
+ },
29
+ {
30
+ name: '/explain',
31
+ description: 'Explain the selected snippet.',
32
+ },
33
+ ];
34
+
15
35
  const defaultValue = (prop) =>
16
36
  typeof GlDuoChat.props[prop].default === 'function'
17
37
  ? GlDuoChat.props[prop].default()
@@ -27,7 +47,6 @@ const generateProps = ({
27
47
  badgeHelpPageUrl = defaultValue('badgeHelpPageUrl'),
28
48
  badgeType = defaultValue('badgeType'),
29
49
  toolName = defaultValue('toolName'),
30
- withSlashCommands = defaultValue('withSlashCommands'),
31
50
  } = {}) => ({
32
51
  title,
33
52
  messages,
@@ -38,7 +57,7 @@ const generateProps = ({
38
57
  badgeHelpPageUrl,
39
58
  badgeType,
40
59
  toolName,
41
- withSlashCommands,
60
+ slashCommands,
42
61
  });
43
62
 
44
63
  export const Default = (args, { argTypes }) => ({
@@ -59,7 +78,6 @@ export const Default = (args, { argTypes }) => ({
59
78
  :badge-help-page-url="badgeHelpPageUrl"
60
79
  :badge-type="badgeType"
61
80
  :tool-name="toolName"
62
- :with-slash-commands="withSlashCommands"
63
81
  />`,
64
82
  });
65
83
  Default.args = generateProps({
@@ -154,7 +172,6 @@ export const Interactive = (args, { argTypes }) => ({
154
172
  :badge-help-page-url="badgeHelpPageUrl"
155
173
  :badge-type="badgeType"
156
174
  :tool-name="toolName"
157
- :with-slash-commands="withSlashCommands"
158
175
  @send-chat-prompt="onSendChatPrompt"
159
176
  @chat-hidden="onChatHidden"
160
177
  />
@@ -180,8 +197,7 @@ export const Slots = (args, { argTypes }) => ({
180
197
  :predefined-prompts="predefinedPrompts"
181
198
  :badge-help-page-url="badgeHelpPageUrl"
182
199
  :badge-type="badgeType"
183
- :tool-name="toolName"
184
- :with-slash-commands="withSlashCommands">
200
+ :tool-name="toolName">
185
201
 
186
202
  <template #hero>
187
203
  <pre class="code-block rounded code highlight gl-border-b gl-rounded-0! gl-mb-0 gl-overflow-y-auto solarized-light" style="max-height: 20rem; overflow-y: auto;">
@@ -37,33 +37,13 @@ export const i18n = {
37
37
  ],
38
38
  };
39
39
 
40
- export const slashCommands = [
41
- {
42
- name: '/reset',
43
- shouldSubmit: true,
44
- description: 'Reset conversation, ignore the previous messages.',
45
- },
46
- {
47
- name: '/tests',
48
- shouldSubmit: false,
49
- description: 'Write tests for the selected snippet.',
50
- },
51
- {
52
- name: '/refactor',
53
- shouldSubmit: false,
54
- description: 'Refactor the selected snippet.',
55
- },
56
- {
57
- name: '/explain',
58
- shouldSubmit: false,
59
- description: 'Explain the selected snippet.',
60
- },
61
- ];
62
-
63
40
  const isMessage = (item) => Boolean(item) && item?.role;
41
+ const isSlashCommand = (command) => Boolean(command) && command?.name && command.description;
64
42
 
65
43
  // eslint-disable-next-line unicorn/no-array-callback-reference
66
44
  const itemsValidator = (items) => items.every(isMessage);
45
+ // eslint-disable-next-line unicorn/no-array-callback-reference
46
+ const slashCommandsValidator = (commands) => commands.every(isSlashCommand);
67
47
 
68
48
  export default {
69
49
  name: 'GlDuoChat',
@@ -161,12 +141,13 @@ export default {
161
141
  default: i18n.CHAT_DEFAULT_TITLE,
162
142
  },
163
143
  /**
164
- * Whether the slash commands should be available to user when typing the prompt.
144
+ * Array of slash commands to display in the chat.
165
145
  */
166
- withSlashCommands: {
167
- type: Boolean,
146
+ slashCommands: {
147
+ type: Array,
168
148
  required: false,
169
- default: false,
149
+ default: () => [],
150
+ validator: slashCommandsValidator,
170
151
  },
171
152
  },
172
153
  data() {
@@ -178,6 +159,9 @@ export default {
178
159
  };
179
160
  },
180
161
  computed: {
162
+ withSlashCommands() {
163
+ return this.slashCommands.length > 0;
164
+ },
181
165
  hasMessages() {
182
166
  return this.messages?.length > 0;
183
167
  },
@@ -206,13 +190,15 @@ export default {
206
190
  },
207
191
  filteredSlashCommands() {
208
192
  const caseInsensitivePrompt = this.prompt.toLowerCase();
209
- return slashCommands.filter((c) => c.name.toLowerCase().startsWith(caseInsensitivePrompt));
193
+ return this.slashCommands.filter((c) =>
194
+ c.name.toLowerCase().startsWith(caseInsensitivePrompt)
195
+ );
210
196
  },
211
197
  shouldShowSlashCommands() {
212
198
  if (!this.withSlashCommands) return false;
213
199
  const caseInsensitivePrompt = this.prompt.toLowerCase();
214
200
  const startsWithSlash = caseInsensitivePrompt.startsWith('/');
215
- const startsWithSlashCommand = slashCommands.some((c) =>
201
+ const startsWithSlashCommand = this.slashCommands.some((c) =>
216
202
  caseInsensitivePrompt.startsWith(c.name)
217
203
  );
218
204
  return startsWithSlash && this.filteredSlashCommands.length && !startsWithSlashCommand;
@@ -1,4 +1,4 @@
1
- import securityDashboardEmptyStateSvg from '@gitlab/svgs/dist/illustrations/security-dashboard-empty-state.svg';
1
+ import productivityAnalyticsEmptyStateSvg from '@gitlab/svgs/dist/illustrations/productivity-analytics-empty-state.svg';
2
2
  import issuesSvg from '@gitlab/svgs/dist/illustrations/rocket-launch-md.svg';
3
3
  import GlButton from '../../base/button/button.vue';
4
4
  import GlEmptyState from './empty_state.vue';
@@ -28,7 +28,7 @@ const Template = (args) => ({
28
28
 
29
29
  const generateProps = ({
30
30
  title = 'This state is empty',
31
- svgPath = securityDashboardEmptyStateSvg,
31
+ svgPath = productivityAnalyticsEmptyStateSvg,
32
32
  svgHeight = 144,
33
33
  description = 'The title and message should be clear, concise, and explain why the user is seeing this screen.',
34
34
  primaryButtonText = 'Something actionable',
@@ -1,5 +1,68 @@
1
+ const baseColorTokens = require('./src/tokens/color.tokens.json');
2
+ const themeColorTokens = require('./src/tokens/color.theme.tokens.json');
3
+
4
+ const baseColors = ['blue', 'gray', 'green', 'orange', 'purple', 'red'].reduce((acc, color) => {
5
+ acc[color] = {};
6
+ Object.entries(baseColorTokens[color]).forEach(([shade, { $value }]) => {
7
+ acc[color][shade] = $value;
8
+ });
9
+ return acc;
10
+ }, {});
11
+
12
+ const themeColors = Object.entries(themeColorTokens.theme).reduce((acc, [color, shades]) => {
13
+ const colorKey = `theme-${color}`;
14
+ acc[colorKey] = {};
15
+ Object.entries(shades).forEach(([shade, { $value }]) => {
16
+ acc[colorKey][shade] = $value;
17
+ });
18
+ return acc;
19
+ }, {});
20
+
21
+ const gridSize = 0.5; // rem
22
+ const spacing = Object.fromEntries(
23
+ Object.entries({
24
+ 0: 0,
25
+ 1: 0.25,
26
+ 2: 0.5,
27
+ 3: 1,
28
+ 4: 1.5,
29
+ 5: 2,
30
+ 6: 3,
31
+ 7: 4,
32
+ 8: 5,
33
+ 9: 6,
34
+ 10: 7,
35
+ 11: 8,
36
+ '11-5': 9,
37
+ 12: 10,
38
+ 13: 12,
39
+ 15: 15,
40
+ 20: 20,
41
+ 26: 26,
42
+ 28: 28,
43
+ 30: 30,
44
+ 31: 31,
45
+ 34: 34,
46
+ 48: 48,
47
+ 62: 62,
48
+ 75: 75,
49
+ 80: 80,
50
+ 88: 88,
51
+ }).map(([scale, factor]) => {
52
+ return [scale, `${factor * gridSize}rem`];
53
+ })
54
+ );
55
+
1
56
  /** @type {import('tailwindcss').Config} */
2
57
  module.exports = {
3
58
  prefix: 'gl-',
4
- corePlugins: [],
59
+ theme: {
60
+ colors: {
61
+ white: baseColorTokens.white.$value,
62
+ black: baseColorTokens.black.$value,
63
+ ...baseColors,
64
+ ...themeColors,
65
+ },
66
+ spacing,
67
+ },
5
68
  };