@apify/ui-library 0.71.1-featcolortokens-178953.63 → 0.71.1-featcolortokens-178953.67

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 (86) hide show
  1. package/dist/tsconfig.build.tsbuildinfo +1 -1
  2. package/package.json +3 -2
  3. package/src/codemods/generate_typograpy_tokens_files.mjs +137 -0
  4. package/src/components/action_link.tsx +60 -0
  5. package/src/components/actor_template_card.tsx +116 -0
  6. package/src/components/badge.tsx +148 -0
  7. package/src/components/banner.tsx +94 -0
  8. package/src/components/blog_article.tsx +85 -0
  9. package/src/components/box.tsx +127 -0
  10. package/src/components/button.tsx +305 -0
  11. package/src/components/chip.tsx +128 -0
  12. package/src/components/code/action_button.tsx +96 -0
  13. package/src/components/code/code_block/code_block.styled.tsx +180 -0
  14. package/src/components/code/code_block/code_block.tsx +224 -0
  15. package/src/components/code/code_block/code_block_with_tabs.tsx +257 -0
  16. package/src/components/code/code_block/utils.tsx +67 -0
  17. package/src/components/code/index.ts +5 -0
  18. package/src/components/code/inline_code/inline_code.tsx +62 -0
  19. package/src/components/code/one_line_code/one_line_code.tsx +228 -0
  20. package/src/components/code/prism_highlighter.tsx +180 -0
  21. package/src/components/color_wheel_gradient.tsx +31 -0
  22. package/src/components/floating/index.ts +3 -0
  23. package/src/components/floating/menu.tsx +189 -0
  24. package/src/components/floating/menu_common.tsx +31 -0
  25. package/src/components/floating/menu_components.tsx +99 -0
  26. package/src/components/image.tsx +24 -0
  27. package/src/components/index.ts +22 -0
  28. package/src/components/link.tsx +114 -0
  29. package/src/components/message.tsx +153 -0
  30. package/src/components/rating.tsx +106 -0
  31. package/src/components/readme_renderer/index.ts +3 -0
  32. package/src/components/readme_renderer/pythonize_value.ts +76 -0
  33. package/src/components/readme_renderer/table_of_contents.tsx +272 -0
  34. package/src/components/readme_renderer/utils.tsx +46 -0
  35. package/src/components/simple_markdown/index.ts +2 -0
  36. package/src/components/simple_markdown/simple_markdown.tsx +214 -0
  37. package/src/components/simple_markdown/simple_markdown_components.tsx +293 -0
  38. package/src/components/tabs/index.ts +2 -0
  39. package/src/components/tabs/tab.tsx +217 -0
  40. package/src/components/tabs/tabs.tsx +169 -0
  41. package/src/components/tag.tsx +196 -0
  42. package/src/components/text/heading_content.tsx +56 -0
  43. package/src/components/text/heading_marketing.tsx +55 -0
  44. package/src/components/text/heading_shared.tsx +55 -0
  45. package/src/components/text/index.ts +19 -0
  46. package/src/components/text/text_base.tsx +52 -0
  47. package/src/components/text/text_content.tsx +104 -0
  48. package/src/components/text/text_marketing.tsx +152 -0
  49. package/src/components/text/text_shared.tsx +95 -0
  50. package/src/components/tile/horizontal_tile.tsx +77 -0
  51. package/src/components/tile/index.ts +2 -0
  52. package/src/components/tile/shared.ts +27 -0
  53. package/src/components/tile/vertical_tile.tsx +59 -0
  54. package/src/components/to_consolidate/card.tsx +141 -0
  55. package/src/components/to_consolidate/index.ts +4 -0
  56. package/src/components/to_consolidate/markdown.tsx +609 -0
  57. package/src/components/to_consolidate/pagination.tsx +136 -0
  58. package/src/components/to_consolidate/tab_number_chip.tsx +31 -0
  59. package/src/design_system/colors/build_color_tokens.js +183 -0
  60. package/src/design_system/colors/figma_color_tokens.dark.json +886 -0
  61. package/src/design_system/colors/figma_color_tokens.light.json +886 -0
  62. package/src/design_system/colors/generated/colors_theme.dark.ts +110 -0
  63. package/src/design_system/colors/generated/colors_theme.light.ts +110 -0
  64. package/src/design_system/colors/generated/css_variables.dark.ts +147 -0
  65. package/src/design_system/colors/generated/css_variables.light.ts +147 -0
  66. package/src/design_system/colors/generated/css_variables_palette.dark.ts +74 -0
  67. package/src/design_system/colors/generated/css_variables_palette.light.ts +74 -0
  68. package/src/design_system/colors/generated/properties_theme.ts +179 -0
  69. package/src/design_system/colors/index.ts +7 -0
  70. package/src/design_system/supernova_typography_tokens.json +657 -0
  71. package/src/design_system/theme.ts +25 -0
  72. package/src/design_system/tokens/index.ts +5 -0
  73. package/src/design_system/tokens/layouts.ts +29 -0
  74. package/src/design_system/tokens/radiuses.ts +22 -0
  75. package/src/design_system/tokens/shadows.ts +22 -0
  76. package/src/design_system/tokens/spaces.ts +15 -0
  77. package/src/design_system/tokens/transitions.ts +19 -0
  78. package/src/design_system/typography_theme.ts +197 -0
  79. package/src/index.ts +8 -0
  80. package/src/type_utils.ts +7 -0
  81. package/src/ui_dependency_provider.tsx +58 -0
  82. package/src/utils/copy_to_clipboard.ts +24 -0
  83. package/src/utils/image_color.ts +42 -0
  84. package/src/utils/index.ts +4 -0
  85. package/src/utils/resize_observer.ts +18 -0
  86. package/src/utils/sanitization.ts +14 -0
@@ -0,0 +1,217 @@
1
+ import clsx from 'clsx';
2
+ import { createPath } from 'history';
3
+ import React from 'react';
4
+ import type { FlattenSimpleInterpolation } from 'styled-components';
5
+ import styled, { css } from 'styled-components';
6
+
7
+ import type { IconComponent } from '@apify/ui-icons';
8
+
9
+ import { theme } from '../../design_system/theme.js';
10
+ import type { WithTransientProps } from '../../type_utils.js';
11
+ import type { BadgeSize } from '../badge.js';
12
+ import { Badge } from '../badge.js';
13
+ import type { MarginSpacingProps, RegularBoxProps } from '../box.js';
14
+ import type { RegularLinkProps } from '../link.js';
15
+ import { Link } from '../link.js';
16
+ import { Text } from '../text/index.js';
17
+ import type { SharedTextSize } from '../text/text_shared.js';
18
+
19
+ type SharedTabProps = Omit<RegularBoxProps, 'as' | 'onClick'> & MarginSpacingProps & Omit<RegularLinkProps, 'hideExternalIcon' | 'showExternalIcon'>;
20
+
21
+ export type TabVariant = 'default' | 'boxed' | 'buttoned';
22
+
23
+ export type TabData = SharedTabProps & {
24
+ id: string;
25
+ title: string;
26
+ Icon?: IconComponent;
27
+ chip?: number | string;
28
+ rollout?: 'alpha' | 'beta';
29
+ disabled?: boolean;
30
+ };
31
+
32
+ export type TabProps = TabData & {
33
+ variant?: TabVariant;
34
+ active?: boolean;
35
+ onSelect?: (data: { id: string, href: string, event: React.MouseEvent }) => void;
36
+ };
37
+
38
+ const tabVariantTextSize = {
39
+ default: 'regular',
40
+ boxed: 'regular',
41
+ buttoned: 'small',
42
+ } satisfies Record<TabVariant, SharedTextSize>;
43
+
44
+ const tabVariantBadgeSize = {
45
+ default: 'small',
46
+ boxed: 'small',
47
+ buttoned: 'extra_small',
48
+ } satisfies Record<TabVariant, BadgeSize>;
49
+
50
+ const tabVariantStyle = {
51
+ default: css`
52
+ height: 3.6rem;
53
+ padding: 0 ${theme.space.space8};
54
+ color: ${theme.color.neutral.textSubtle};
55
+
56
+ &:hover {
57
+ color: ${theme.color.primaryBlack.actionHover}
58
+ }
59
+
60
+ &.active {
61
+ color: ${theme.color.neutral.text};
62
+
63
+ &::after {
64
+ bottom: -${theme.space.space4};
65
+ right: 0;
66
+ left: 0;
67
+ height: 2px;
68
+ background-color: ${theme.color.primaryBlack.action};
69
+ border-radius: ${theme.radius.radiusFull};
70
+ content: '';
71
+ display: block;
72
+ pointer-events: none;
73
+ position: absolute;
74
+ }
75
+
76
+ &.disabled::after {
77
+ background-color: ${theme.color.neutral.textDisabled};
78
+ }
79
+ }
80
+ `,
81
+ boxed: css`
82
+ height: 3.6rem;
83
+ border: 1px solid transparent;
84
+ border-top-right-radius: ${theme.radius.radius6};
85
+ border-top-left-radius: ${theme.radius.radius6};
86
+ padding: 0 ${theme.space.space12};
87
+ color: ${theme.color.neutral.textMuted};
88
+
89
+ &::after {
90
+ inset: ${theme.space.space4};
91
+ background-color: transparent;
92
+ border-radius: ${theme.radius.radius6};
93
+ content: '';
94
+ display: block;
95
+ pointer-events: none;
96
+ position: absolute;
97
+ transition: background-color ${theme.transition.fastEaseOut};
98
+ z-index: -1;
99
+ }
100
+
101
+ &:hover {
102
+ color: ${theme.color.neutral.text};
103
+
104
+ &::after {
105
+ background-color: ${theme.color.neutral.hover};
106
+ }
107
+ }
108
+
109
+ &.active {
110
+ color: ${theme.color.neutral.text};
111
+ border-color: ${theme.color.neutral.border};
112
+ border-bottom-color: ${theme.color.neutral.background};
113
+
114
+ &::after {
115
+ background-color: transparent;
116
+ }
117
+ }
118
+ `,
119
+ buttoned: css`
120
+ height: 3.2rem;
121
+ margin: 0 ${theme.space.space4};
122
+ padding: 0 ${theme.space.space8};
123
+ background-color: transparent;
124
+ border: 1px solid transparent;
125
+ border-radius: ${theme.radius.radius8};
126
+ color: ${theme.color.neutral.textMuted};
127
+
128
+ &:hover {
129
+ background-color: ${theme.color.neutral.hover};
130
+ color: ${theme.color.neutral.text};
131
+ }
132
+
133
+ &.active {
134
+ background-color: transparent;
135
+ border-color: ${theme.color.neutral.border};
136
+ color: ${theme.color.neutral.text};
137
+ }
138
+ `,
139
+ } satisfies Record<TabVariant, FlattenSimpleInterpolation>;
140
+
141
+ type TabWrapperProps = WithTransientProps<Required<Pick<TabProps, 'variant'>>> & {
142
+ role?: React.AriaRole;
143
+ };
144
+
145
+ export const TAB_CLASSNAMES = {
146
+ ICON: 'Tab-icon',
147
+ TITLE: 'Tab-title',
148
+ BADGE: 'Tab-badge',
149
+ };
150
+
151
+ const TabWrapper = styled(Link)<TabWrapperProps>`
152
+ display: inline-flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ gap: ${theme.space.space4};
156
+ flex-shrink: 0;
157
+ cursor: pointer;
158
+ position: relative;
159
+ transition: background-color ${theme.transition.fastEaseOut}, border-color ${theme.transition.fastEaseOut}, color ${theme.transition.fastEaseOut};
160
+ z-index: 1;
161
+
162
+
163
+ ${({ $variant }) => tabVariantStyle[$variant]};
164
+
165
+ &.disabled {
166
+ color: ${theme.color.neutral.textDisabled};
167
+ pointer-events: none;
168
+ }
169
+
170
+ .${TAB_CLASSNAMES.ICON} {
171
+ color: inherit;
172
+ flex-shrink: 0;
173
+ transition: color ${theme.transition.fastEaseOut};
174
+ }
175
+
176
+ .${TAB_CLASSNAMES.TITLE} {
177
+ overflow: hidden;
178
+ text-overflow: ellipsis;
179
+ white-space: nowrap;
180
+ }
181
+
182
+ .${TAB_CLASSNAMES.BADGE} {
183
+ flex-shrink: 0;
184
+ text-transform: uppercase;
185
+ }
186
+ `;
187
+
188
+ export const Tab = ({ variant = 'default', id, to, Icon, title, chip, rollout, className, onSelect, active = false, disabled = false, ...props }: TabProps) => {
189
+ const href = typeof (to) === 'string' ? to : createPath(to);
190
+
191
+ return (
192
+ <TabWrapper
193
+ {...props}
194
+ id={id}
195
+ to={to}
196
+ role="tab"
197
+ data-test="tab"
198
+ data-test-url={href}
199
+ className={clsx(className, { active, disabled })}
200
+ onClick={onSelect ? (event) => onSelect({ id, href, event }) : undefined}
201
+ $variant={variant}
202
+ >
203
+ {Icon && <Icon size="16" className={TAB_CLASSNAMES.ICON} />}
204
+ <Text size={tabVariantTextSize[variant]} className={TAB_CLASSNAMES.ICON} weight="bold" as="div">{title}</Text>
205
+ {chip && (
206
+ <Badge
207
+ size={tabVariantBadgeSize[variant]}
208
+ variant={active ? 'primary_black' : 'neutral_subtle'}
209
+ className={TAB_CLASSNAMES.ICON}
210
+ >
211
+ {chip}
212
+ </Badge>
213
+ )}
214
+ {rollout && <Badge size={tabVariantBadgeSize[variant]} variant="primary_blue" className={TAB_CLASSNAMES.ICON}>{rollout}</Badge>}
215
+ </TabWrapper>
216
+ );
217
+ };
@@ -0,0 +1,169 @@
1
+ import React, { useCallback, useRef, useState } from 'react';
2
+ import type { FlattenSimpleInterpolation } from 'styled-components';
3
+ import styled, { css } from 'styled-components';
4
+
5
+ import { theme } from '../../design_system/theme.js';
6
+ import type { WithTransientProps } from '../../type_utils.js';
7
+ import { useResizeObserverSsr } from '../../utils/resize_observer.js';
8
+ import type { MarginSpacingProps, RegularBoxProps } from '../box.js';
9
+ import { Box } from '../box.js';
10
+ import type { TabData, TabVariant } from './tab.js';
11
+ import { Tab } from './tab.js';
12
+
13
+ type SharedTabsProps = Omit<RegularBoxProps, 'as' | 'onClick'> & MarginSpacingProps;
14
+ type TabsProps = SharedTabsProps & {
15
+ variant?: TabVariant;
16
+ tabs: TabData[];
17
+ activeTab?: string;
18
+ onSelect?: (data: { id: string, href: string, event: React.MouseEvent }) => void;
19
+ };
20
+
21
+ const tabsVariantStyle = {
22
+ default: css`
23
+ align-items: flex-start;
24
+ gap: ${theme.space.space8};
25
+
26
+ &::after {
27
+ bottom: 0;
28
+ right: 0;
29
+ left: 0;
30
+ height: 1px;
31
+ background-color: ${theme.color.neutral.separatorSubtle};
32
+ border-radius: ${theme.radius.radiusFull};
33
+ content: '';
34
+ display: block;
35
+ pointer-events: none;
36
+ position: absolute;
37
+ }
38
+ `,
39
+ boxed: css`
40
+ align-items: flex-end;
41
+
42
+ &::after {
43
+ bottom: 0;
44
+ right: 0;
45
+ left: 0;
46
+ height: 1px;
47
+ background-color: ${theme.color.neutral.border};
48
+ content: '';
49
+ display: block;
50
+ pointer-events: none;
51
+ position: absolute;
52
+ }
53
+ `,
54
+ buttoned: css`
55
+ align-items: center;
56
+ `,
57
+ } satisfies Record<TabVariant, FlattenSimpleInterpolation>;
58
+
59
+ type TabsWrapperProps = WithTransientProps<Required<Pick<TabsProps, 'variant'>>> & {
60
+ role?: React.AriaRole;
61
+ };
62
+
63
+ const TabsWrapper = styled(Box)<TabsWrapperProps>`
64
+ height: 4rem;
65
+ display: flex;
66
+ position: relative;
67
+
68
+ [role='tabpanel'] {
69
+ min-width: 0;
70
+ display: flex;
71
+ flex-grow: 1;
72
+ overflow-x: auto;
73
+ ${({ $variant }) => tabsVariantStyle[$variant]};
74
+
75
+ /* Scrollbar */
76
+ -ms-overflow-style: none;
77
+ scrollbar-width: none;
78
+
79
+ &::-webkit-scrollbar {
80
+ display: none; /* Chrome, Safari, Opera */
81
+ }
82
+ }
83
+
84
+ & > [role="cell"] {
85
+ position: sticky;
86
+ top: 0;
87
+ height: 100%;
88
+ width: 0;
89
+ margin-left: auto;
90
+ opacity: 0;
91
+ transition: opacity ${theme.transition.fastEaseOut};
92
+ z-index: 2;
93
+
94
+ &[aria-hidden="false"] {
95
+ opacity: 1;
96
+ }
97
+
98
+ &::after {
99
+ height: 100%;
100
+ width: ${theme.space.space32};
101
+ content: ' ';
102
+ pointer-events: none;
103
+ position: absolute;
104
+ }
105
+
106
+ &:first-of-type {
107
+ left: 0;
108
+
109
+ &::after {
110
+ left: 0;
111
+ background: linear-gradient(90deg, ${theme.color.neutral.background} 0%, rgba(255, 255, 255, 0) 100%);
112
+ }
113
+ }
114
+
115
+ &:last-of-type {
116
+ right: 0;
117
+
118
+ &::after {
119
+ right: 0;
120
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, ${theme.color.neutral.background} 100%);
121
+ }
122
+ }
123
+ }
124
+ `;
125
+
126
+ type TabOverflowState = {
127
+ right: boolean;
128
+ left: boolean;
129
+ }
130
+
131
+ const isTabsOverflowing = (node: HTMLDivElement): TabOverflowState => {
132
+ if (node.scrollWidth > node.clientWidth) {
133
+ return {
134
+ right: node.clientWidth + node.scrollLeft < node.scrollWidth,
135
+ left: node.scrollLeft > 0,
136
+ };
137
+ }
138
+
139
+ return {
140
+ right: false,
141
+ left: false,
142
+ };
143
+ };
144
+
145
+ export const Tabs = ({ variant = 'default', tabs, activeTab, onSelect, ...props }: TabsProps) => {
146
+ const ref = useRef<HTMLDivElement>(null);
147
+ const [overflowState, setOveflowState] = useState<TabOverflowState>();
148
+
149
+ useResizeObserverSsr(ref, (entry) => setOveflowState(isTabsOverflowing(entry.target as HTMLDivElement)));
150
+ const scrollHandler = useCallback((event: React.SyntheticEvent<HTMLDivElement>) => {
151
+ setOveflowState(isTabsOverflowing(event.currentTarget as HTMLDivElement));
152
+ }, []);
153
+
154
+ return <TabsWrapper {...props} role="tablist" data-test="tabs-wrapper" $variant={variant}>
155
+ <div role="cell" aria-hidden={!overflowState?.left} />
156
+ <div ref={ref} role="tabpanel" onScroll={scrollHandler}>
157
+ {tabs.map((tab) => (
158
+ <Tab
159
+ {...tab}
160
+ key={tab.id}
161
+ variant={variant}
162
+ active={tab.id === activeTab}
163
+ onSelect={onSelect}
164
+ />
165
+ ))}
166
+ </div>
167
+ <div role="cell" aria-hidden={!overflowState?.right} />
168
+ </TabsWrapper>;
169
+ };
@@ -0,0 +1,196 @@
1
+ import React, { type ForwardedRef, forwardRef } from 'react';
2
+ import styled, { css, type FlattenSimpleInterpolation } from 'styled-components';
3
+
4
+ import type { IconComponent, IconSize } from '@apify/ui-icons';
5
+
6
+ import { theme } from '../design_system/theme.js';
7
+ import type { WithTransientProps } from '../type_utils.js';
8
+ import { Box, type MarginSpacingProps, type RegularBoxProps } from './box.js';
9
+ import type { RegularButtonProps } from './button.js';
10
+ import { Link, type RegularLinkProps } from './link.js';
11
+ import { Text } from './text/index.js';
12
+ import { type SharedTextSize, type SharedTextType } from './text/text_shared.js';
13
+
14
+ type TagSize = SharedTextSize;
15
+ export const TAG_SIZES: TagSize[] = ['big', 'regular', 'small'];
16
+
17
+ const TAG_ICON_SIZES = {
18
+ big: '20',
19
+ regular: '16',
20
+ small: '12',
21
+ } satisfies Record<TagSize, IconSize>;
22
+
23
+ export const TAG_VARIANTS = ['primary', 'secondary', 'subtle', 'accent', 'success', 'warning', 'error'] as const;
24
+ type TagVariant = typeof TAG_VARIANTS[number];
25
+
26
+ type TagNodeType = Extract<React.ElementType, 'a' | 'button'>;
27
+ type TagNodePropsMap = {
28
+ a: {
29
+ element: HTMLAnchorElement;
30
+ props: RegularLinkProps;
31
+ };
32
+ button: {
33
+ element: HTMLButtonElement;
34
+ props: RegularButtonProps;
35
+ };
36
+ }
37
+
38
+ type SharedTagProps = Omit<RegularBoxProps, 'as'> & MarginSpacingProps;
39
+ export type TagProps<T extends TagNodeType> = SharedTagProps & ({
40
+ as?: T;
41
+ size?: TagSize;
42
+ type?: SharedTextType;
43
+ variant?: TagVariant;
44
+ LeadingIcon?: IconComponent;
45
+ TrailingIcon?: IconComponent;
46
+ } & Omit<TagNodePropsMap[T]['props'], 'size'>);
47
+
48
+ const tagSizeStyle = {
49
+ big: css`
50
+ height: 4rem;
51
+ padding: ${theme.space.space8} ${theme.space.space12};
52
+ border-radius: ${theme.radius.radius12};
53
+ `,
54
+ regular: css`
55
+ height: 2.8rem;
56
+ padding: ${theme.space.space4} ${theme.space.space8};
57
+ border-radius: ${theme.radius.radius8};
58
+ `,
59
+ small: css`
60
+ height: 2rem;
61
+ padding: ${theme.space.space2} ${theme.space.space6};
62
+ border-radius: ${theme.radius.radius4};
63
+
64
+ &:has(> svg:last-child:nth-child(1)) {
65
+ padding: ${theme.space.space2} ${theme.space.space4};
66
+ }
67
+ `,
68
+ } satisfies Record<TagSize, FlattenSimpleInterpolation>;
69
+
70
+ const tagVariantStyle = {
71
+ primary: css`
72
+ color: ${theme.color.neutral.text};
73
+ fill: ${theme.color.neutral.icon};
74
+ background: transparent;
75
+ box-shadow: inset 0 0 0 1px ${theme.color.neutral.border};
76
+
77
+ &:hover {
78
+ background: ${theme.color.neutral.hover};
79
+ }
80
+ `,
81
+ secondary: css`
82
+ color: ${theme.color.neutral.text};
83
+ fill: ${theme.color.neutral.icon};
84
+ background: transparent;
85
+
86
+ &:hover {
87
+ background: ${theme.color.neutral.hover};
88
+ }
89
+ `,
90
+ subtle: css`
91
+ color: ${theme.color.neutral.text};
92
+ fill: ${theme.color.neutral.icon};
93
+ background: ${theme.color.neutral.backgroundSubtle};
94
+
95
+ &:hover {
96
+ background: ${theme.color.neutral.hover};
97
+ }
98
+ `,
99
+ accent: css`
100
+ color: ${theme.color.primary.chipText};
101
+ fill: ${theme.color.primary.chipText};
102
+ background: ${theme.color.primary.background};
103
+
104
+ &:hover {
105
+ background: ${theme.color.primary.backgroundHover};
106
+ }
107
+ `,
108
+ success: css`
109
+ color: ${theme.color.success.chipText};
110
+ fill: ${theme.color.success.chipText};
111
+ background: ${theme.color.success.background};
112
+
113
+ &:hover {
114
+ background: ${theme.color.success.backgroundHover};
115
+ }
116
+ `,
117
+ warning: css`
118
+ color: ${theme.color.warning.chipText};
119
+ fill: ${theme.color.warning.chipText};
120
+ background: ${theme.color.warning.background};
121
+
122
+ &:hover {
123
+ background: ${theme.color.warning.backgroundHover};
124
+ }
125
+ `,
126
+ error: css`
127
+ color: ${theme.color.danger.chipText};
128
+ fill: ${theme.color.danger.chipText};
129
+ background: ${theme.color.danger.background};
130
+
131
+ &:hover {
132
+ background: ${theme.color.danger.backgroundHover};
133
+ }
134
+ `,
135
+ } satisfies Record<TagVariant, FlattenSimpleInterpolation>;
136
+
137
+ type StyledTagProps = WithTransientProps<
138
+ Required<Pick<TagProps<TagNodeType>, 'size' | 'variant'>>
139
+ >;
140
+
141
+ const StyledTag = styled(Box)<StyledTagProps>`
142
+ ${({ $size }) => tagSizeStyle[$size]};
143
+ ${({ $variant }) => tagVariantStyle[$variant]};
144
+
145
+ /* Static styles */
146
+ width: fit-content;
147
+ border: none;
148
+ outline: none;
149
+ display: flex;
150
+ flex-direction: row;
151
+ justify-content: center;
152
+ align-items: center;
153
+ white-space: nowrap;
154
+ gap: ${theme.space.space4};
155
+ cursor: pointer;
156
+ transition: background ${theme.transition.fastEaseOut};
157
+ `;
158
+
159
+ /**
160
+ * Component for displaying inherenly interactive chip descendant
161
+ */
162
+ function TagWrapper<T extends TagNodeType>({
163
+ as,
164
+ size = 'small',
165
+ type = 'body',
166
+ variant = 'primary',
167
+ LeadingIcon,
168
+ TrailingIcon,
169
+ children,
170
+ ...props
171
+ }: TagProps<T>,
172
+ ref: ForwardedRef<TagNodePropsMap[T]['element']>,
173
+ ) {
174
+ const component: React.ElementType = (as === 'a' ? Link : as ?? 'button');
175
+
176
+ return (
177
+ <StyledTag
178
+ as={component}
179
+ ref={ref}
180
+ $variant={variant}
181
+ $size={size}
182
+ {...props}
183
+ >
184
+ {LeadingIcon && <LeadingIcon size={TAG_ICON_SIZES[size]} />}
185
+ {children && (<Text size={size} type={type} weight="medium">{children}</Text>)}
186
+ {TrailingIcon && <TrailingIcon size={TAG_ICON_SIZES[size]} />}
187
+ </StyledTag>
188
+ );
189
+ }
190
+ TagWrapper.displayName = 'Tag';
191
+
192
+ // `forwardRef` can't handle generic types, should be fixable with React 19
193
+ export const Tag = forwardRef(TagWrapper) as <T extends TagNodeType>(
194
+ // eslint-disable-next-line no-use-before-define -- see comment above 👆
195
+ props: TagProps<T> & { ref?: ForwardedRef<TagNodePropsMap[T]['element']> },
196
+ ) => ReturnType<typeof TagWrapper>;
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import styled, { css } from 'styled-components';
3
+
4
+ import { theme } from '../../design_system/theme.js';
5
+ import type { WithTransientProps } from '../../type_utils.js';
6
+ import type { TextBaseProps } from './text_base.js';
7
+ import { TextBaseComponent } from './text_base.js';
8
+
9
+ type HeadingContentType = 'heading1' | 'heading2' | 'heading3' | 'heading4' | 'heading5' | 'heading6';
10
+
11
+ interface HeadingContentTransientProps {
12
+ type?: HeadingContentType,
13
+ }
14
+
15
+ const getContentHeadingStyles = (headingType: HeadingContentType) => css`
16
+ ${theme.typography.content.mobile[headingType]}
17
+
18
+ @media (min-width: ${theme.layout.tablet}) {
19
+ ${theme.typography.content.tablet[headingType]}
20
+ }
21
+
22
+ @media (min-width: ${theme.layout.desktop}) {
23
+ ${theme.typography.content.desktop[headingType]}
24
+ }
25
+ `;
26
+
27
+ type StyledHeadingContentProps = WithTransientProps<HeadingContentTransientProps>;
28
+ export type HeadingContentProps = HeadingContentTransientProps & TextBaseProps;
29
+
30
+ const HEADING_CONTENT_DEFAULT_ELEMENTS = {
31
+ heading1: 'h1',
32
+ heading2: 'h2',
33
+ heading3: 'h3',
34
+ heading4: 'h4',
35
+ heading5: 'h5',
36
+ heading6: 'h6',
37
+ } as const;
38
+
39
+ const StyledHeadingContent = styled(TextBaseComponent)<StyledHeadingContentProps>`
40
+ ${(props) => getContentHeadingStyles(props.$type || 'heading1')}
41
+ `;
42
+
43
+ export const HeadingContent: React.FC<HeadingContentProps> = ({
44
+ type,
45
+ as,
46
+ ...rest
47
+ }) => {
48
+ return (
49
+ <StyledHeadingContent
50
+ $type={type}
51
+ forwardedAs={as || HEADING_CONTENT_DEFAULT_ELEMENTS[type || 'heading1']}
52
+ m='none'
53
+ {...rest}
54
+ />
55
+ );
56
+ };
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { theme } from '../../design_system/theme.js';
5
+ import type { WithTransientProps } from '../../type_utils.js';
6
+ import type { TextBaseProps } from './text_base.js';
7
+ import { TextBaseComponent } from './text_base.js';
8
+
9
+ type HeadingMarketingType = 'titleXs' | 'titleS' | 'titleM' | 'titleL' | 'titleXl' | 'title2xl' | 'title3xl';
10
+
11
+ interface HeadingMarketingTransientProps {
12
+ type?: HeadingMarketingType,
13
+ }
14
+
15
+ type StyledHeadingMarketingProps = WithTransientProps<HeadingMarketingTransientProps>;
16
+ export type HeadingMarketingProps = HeadingMarketingTransientProps & TextBaseProps;
17
+
18
+ const HEADING_MARKETING_DEFAULT_ELEMENTS: { [Type in HeadingMarketingType]: string } = {
19
+ titleXs: 'h6',
20
+ titleS: 'h5',
21
+ titleM: 'h4',
22
+ titleL: 'h3',
23
+ titleXl: 'h2',
24
+ title2xl: 'h1',
25
+ title3xl: 'h1',
26
+ };
27
+
28
+ const StyledHeadingMarketing = styled(TextBaseComponent)<StyledHeadingMarketingProps>`
29
+ ${(props) => `
30
+ ${theme.typography.marketing.mobile[props.$type || 'titleL']}
31
+
32
+ @media (${theme.device.tablet}) {
33
+ ${theme.typography.marketing.tablet[props.$type || 'titleL']}
34
+ }
35
+
36
+ @media (${theme.device.desktop}) {
37
+ ${theme.typography.marketing.desktop[props.$type || 'titleL']}
38
+ }
39
+ `}
40
+ `;
41
+
42
+ export const HeadingMarketing: React.FC<HeadingMarketingProps> = ({
43
+ type,
44
+ as,
45
+ ...rest
46
+ }) => {
47
+ return (
48
+ <StyledHeadingMarketing
49
+ $type={type}
50
+ forwardedAs={as || HEADING_MARKETING_DEFAULT_ELEMENTS[type || 'titleL']}
51
+ m='none'
52
+ {...rest}
53
+ />
54
+ );
55
+ };