@hubspot/cms-component-library 0.3.8 → 0.3.10
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/components/componentLibrary/Accordion/AccordionTitle/AccordionTitleBase.tsx +45 -0
- package/components/componentLibrary/Accordion/AccordionTitle/index.tsx +17 -30
- package/components/componentLibrary/Accordion/AccordionTitle/islands/AccordionTitleIsland.tsx +29 -0
- package/components/componentLibrary/Button/StyleFields.tsx +8 -8
- package/components/componentLibrary/Button/index.module.scss +24 -27
- package/components/componentLibrary/Button/index.tsx +4 -4
- package/components/componentLibrary/Button/llm.txt +51 -64
- package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +2 -2
- package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +2 -2
- package/components/componentLibrary/Button/stories/ButtonDecorator.module.scss +19 -23
- package/components/componentLibrary/Button/types.ts +2 -2
- package/components/componentLibrary/Card/StyleFields.tsx +9 -14
- package/components/componentLibrary/Card/index.module.scss +7 -7
- package/components/componentLibrary/Card/index.tsx +8 -13
- package/components/componentLibrary/Card/llm.txt +22 -43
- package/components/componentLibrary/Card/stories/Card.stories.tsx +28 -20
- package/components/componentLibrary/Card/stories/CardDecorator.module.scss +28 -5
- package/components/componentLibrary/Card/types.ts +8 -5
- package/components/componentLibrary/Form/StyleFields.tsx +19 -0
- package/components/componentLibrary/Form/index.tsx +7 -1
- package/components/componentLibrary/Form/islands/FormIsland.tsx +3 -1
- package/components/componentLibrary/Form/islands/LegacyFormIsland.tsx +2 -1
- package/components/componentLibrary/Form/islands/legacyForm.module.css +251 -0
- package/components/componentLibrary/Form/islands/v4Form.module.css +95 -0
- package/components/componentLibrary/Form/llm.txt +184 -0
- package/components/componentLibrary/Form/types.ts +6 -0
- package/components/componentLibrary/Link/ContentFields.tsx +2 -2
- package/components/componentLibrary/Link/StyleFields.tsx +10 -17
- package/components/componentLibrary/Link/index.module.scss +9 -9
- package/components/componentLibrary/Link/index.tsx +3 -8
- package/components/componentLibrary/Link/llm.txt +29 -85
- package/components/componentLibrary/Link/stories/Link.stories.tsx +4 -11
- package/components/componentLibrary/Link/stories/LinkDecorator.module.scss +15 -0
- package/components/componentLibrary/Link/stories/LinkDecorator.tsx +2 -11
- package/components/componentLibrary/Link/types.ts +11 -8
- package/components/componentLibrary/Video/ContentFields.tsx +112 -0
- package/components/componentLibrary/Video/StyleFields.tsx +19 -0
- package/components/componentLibrary/Video/index.tsx +47 -0
- package/components/componentLibrary/Video/islands/HSVideoIsland.tsx +53 -0
- package/components/componentLibrary/Video/serverUtils.ts +41 -0
- package/components/componentLibrary/Video/types.ts +74 -0
- package/components/componentLibrary/_patterns/README.md +11 -7
- package/components/componentLibrary/_patterns/checklist-and-examples.md +8 -0
- package/components/componentLibrary/_patterns/component-structure.md +5 -1
- package/components/componentLibrary/_patterns/field-patterns.md +46 -0
- package/components/componentLibrary/_patterns/island-patterns.md +136 -0
- package/components/componentLibrary/utils/index.ts +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Text from '../../Text/index.js';
|
|
2
|
+
import styles from './index.module.scss';
|
|
3
|
+
import cx from '../../utils/classname.js';
|
|
4
|
+
import { ChevronIcon, PlusIcon, MinusIcon, CaretIcon } from './icons.js';
|
|
5
|
+
import type { AccordionTitleProps } from './types.js';
|
|
6
|
+
|
|
7
|
+
const AccordionTitleBase = ({
|
|
8
|
+
icon = 'chevron',
|
|
9
|
+
fieldPath,
|
|
10
|
+
className = '',
|
|
11
|
+
style = {},
|
|
12
|
+
children,
|
|
13
|
+
}: AccordionTitleProps) => {
|
|
14
|
+
const combinedClasses = cx(styles.accordionTitle, className);
|
|
15
|
+
|
|
16
|
+
const renderIcon = () => {
|
|
17
|
+
if (icon === 'chevron') return <ChevronIcon />;
|
|
18
|
+
|
|
19
|
+
if (icon === 'plus') {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<PlusIcon />
|
|
23
|
+
<MinusIcon />
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (icon === 'caret') return <CaretIcon />;
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<summary className={combinedClasses} style={style}>
|
|
35
|
+
{fieldPath ? (
|
|
36
|
+
<Text fieldPath={fieldPath} className={styles.accordionTitleText} />
|
|
37
|
+
) : (
|
|
38
|
+
<span className={styles.accordionTitleText}>{children}</span>
|
|
39
|
+
)}
|
|
40
|
+
{renderIcon()}
|
|
41
|
+
</summary>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default AccordionTitleBase;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { useEditorVariableChecks, Island } from '@hubspot/cms-components';
|
|
2
|
+
// @ts-expect-error -- ?island not typed
|
|
3
|
+
import AccordionTitleIslandModule from './islands/AccordionTitleIsland.js?island';
|
|
3
4
|
import ContentFields from './ContentFields.js';
|
|
4
5
|
import StyleFields from './StyleFields.js';
|
|
5
|
-
import cx from '../../utils/classname.js';
|
|
6
6
|
import { AccordionTitleProps } from './types.js';
|
|
7
|
-
import
|
|
7
|
+
import AccordionTitleBase from './AccordionTitleBase.js';
|
|
8
8
|
|
|
9
9
|
const AccordionTitleComponent = ({
|
|
10
10
|
icon = 'chevron',
|
|
@@ -13,34 +13,21 @@ const AccordionTitleComponent = ({
|
|
|
13
13
|
style = {},
|
|
14
14
|
children,
|
|
15
15
|
}: AccordionTitleProps) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const renderIcon = () => {
|
|
19
|
-
if (icon === 'chevron') return <ChevronIcon />;
|
|
20
|
-
|
|
21
|
-
if (icon === 'plus') {
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<PlusIcon />
|
|
25
|
-
<MinusIcon />
|
|
26
|
-
</>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (icon === 'caret') return <CaretIcon />;
|
|
31
|
-
|
|
32
|
-
return null;
|
|
33
|
-
};
|
|
16
|
+
const editorChecks = useEditorVariableChecks();
|
|
17
|
+
const isInEditor = editorChecks.is_in_editor && !!fieldPath;
|
|
34
18
|
|
|
35
19
|
return (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
20
|
+
<>
|
|
21
|
+
<AccordionTitleBase
|
|
22
|
+
icon={icon}
|
|
23
|
+
fieldPath={fieldPath}
|
|
24
|
+
className={className}
|
|
25
|
+
style={style}
|
|
26
|
+
>
|
|
27
|
+
{children}
|
|
28
|
+
</AccordionTitleBase>
|
|
29
|
+
{isInEditor && <Island module={AccordionTitleIslandModule} />}
|
|
30
|
+
</>
|
|
44
31
|
);
|
|
45
32
|
};
|
|
46
33
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const AccordionTitleIsland = () => {
|
|
4
|
+
const markerRef = useRef<HTMLSpanElement>(null);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const marker = markerRef.current;
|
|
8
|
+
if (!marker) return;
|
|
9
|
+
|
|
10
|
+
const details = marker.closest('details');
|
|
11
|
+
const summary = details?.querySelector('summary');
|
|
12
|
+
if (!summary) return;
|
|
13
|
+
|
|
14
|
+
const handleClick = (e: MouseEvent) => {
|
|
15
|
+
const target = e.target as HTMLElement;
|
|
16
|
+
const isIconClick = target.closest('svg');
|
|
17
|
+
if (!isIconClick) {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
summary.addEventListener('click', handleClick);
|
|
23
|
+
return () => summary.removeEventListener('click', handleClick);
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return <span ref={markerRef} style={{ display: 'none' }} />;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default AccordionTitleIsland;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ChoiceField,
|
|
3
|
+
VariantSelectionField,
|
|
4
|
+
Visibility,
|
|
5
|
+
} from '@hubspot/cms-components/fields';
|
|
2
6
|
import { StyleFieldsProps } from './types.js';
|
|
3
7
|
|
|
4
8
|
const StyleFields = ({
|
|
5
9
|
buttonVariantLabel = 'Button variant',
|
|
6
10
|
buttonVariantName = 'buttonVariant',
|
|
7
|
-
buttonVariantDefault = '
|
|
11
|
+
buttonVariantDefault = { variant_name: 'primaryButton' },
|
|
8
12
|
buttonIconPositionLabel = 'Icon position',
|
|
9
13
|
buttonIconPositionName = 'buttonIconPosition',
|
|
10
14
|
buttonIconPositionDefault = 'right',
|
|
@@ -18,14 +22,10 @@ const StyleFields = ({
|
|
|
18
22
|
|
|
19
23
|
return (
|
|
20
24
|
<>
|
|
21
|
-
<
|
|
25
|
+
<VariantSelectionField
|
|
22
26
|
label={buttonVariantLabel}
|
|
23
27
|
name={buttonVariantName}
|
|
24
|
-
|
|
25
|
-
['primary', 'Primary'],
|
|
26
|
-
['secondary', 'Secondary'],
|
|
27
|
-
['tertiary', 'Tertiary'],
|
|
28
|
-
]}
|
|
28
|
+
variantDefinitionName="button"
|
|
29
29
|
default={buttonVariantDefault}
|
|
30
30
|
/>
|
|
31
31
|
<ChoiceField
|
|
@@ -1,48 +1,45 @@
|
|
|
1
|
-
// CSS variables
|
|
1
|
+
// CSS variables provided via theme settings variant system (scoped via data-buttons-variant)
|
|
2
2
|
.button {
|
|
3
3
|
display: inline-flex;
|
|
4
4
|
flex-direction: row;
|
|
5
5
|
align-items: center;
|
|
6
6
|
justify-content: center;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
border
|
|
13
|
-
|
|
14
|
-
font-
|
|
15
|
-
font-
|
|
7
|
+
gap: 8px;
|
|
8
|
+
background-color: var(--hs-button-backgroundColor);
|
|
9
|
+
color: var(--hs-button-color);
|
|
10
|
+
padding: 12px 24px;
|
|
11
|
+
border-radius: var(--hs-button-borderRadius);
|
|
12
|
+
border: var(--hs-button-border);
|
|
13
|
+
font-size: var(--hs-button-fontSize);
|
|
14
|
+
font-family: var(--hs-button-fontFamily);
|
|
15
|
+
font-style: var(--hs-button-fontStyle);
|
|
16
|
+
font-weight: var(--hs-button-fontWeight);
|
|
17
|
+
text-decoration: var(--hs-button-textDecoration);
|
|
16
18
|
cursor: pointer;
|
|
17
19
|
transition: all 0.2s ease;
|
|
18
|
-
text-decoration: none;
|
|
19
20
|
|
|
20
21
|
@media (prefers-reduced-motion: reduce) {
|
|
21
22
|
transition: none;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
&:hover {
|
|
25
|
-
background-color: var(--
|
|
26
|
-
color: var(--
|
|
27
|
-
border
|
|
28
|
-
border-color: var(--hscl-button-borderColor-hover, var(--hscl-button-borderColor));
|
|
29
|
-
text-decoration: none;
|
|
26
|
+
background-color: var(--hs-button-backgroundColor-hover, var(--hs-button-backgroundColor));
|
|
27
|
+
color: var(--hs-button-color-hover, var(--hs-button-color));
|
|
28
|
+
border: var(--hs-button-border-hover, var(--hs-button-border));
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
&:focus-visible {
|
|
33
|
-
background-color: var(--
|
|
34
|
-
color: var(--
|
|
35
|
-
border
|
|
36
|
-
|
|
37
|
-
outline:
|
|
38
|
-
outline-offset: var(--hscl-button-outlineOffset-focus, 2px);
|
|
32
|
+
background-color: var(--hs-button-backgroundColor-focus, var(--hs-button-backgroundColor));
|
|
33
|
+
color: var(--hs-button-color-focus, var(--hs-button-color));
|
|
34
|
+
border: var(--hs-button-border-focus, var(--hs-button-border));
|
|
35
|
+
outline: 2px solid;
|
|
36
|
+
outline-offset: 2px;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
&:active {
|
|
42
|
-
background-color: var(--
|
|
43
|
-
color: var(--
|
|
44
|
-
border
|
|
45
|
-
border-color: var(--hscl-button-borderColor-active, var(--hscl-button-borderColor));
|
|
40
|
+
background-color: var(--hs-button-backgroundColor-active, var(--hs-button-backgroundColor));
|
|
41
|
+
color: var(--hs-button-color-active, var(--hs-button-color));
|
|
42
|
+
border: var(--hs-button-border-active, var(--hs-button-border));
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
&:disabled {
|
|
@@ -54,6 +51,6 @@
|
|
|
54
51
|
display: inline-flex;
|
|
55
52
|
align-items: center;
|
|
56
53
|
justify-content: center;
|
|
57
|
-
fill:
|
|
54
|
+
fill: currentColor;
|
|
58
55
|
}
|
|
59
56
|
}
|
|
@@ -13,16 +13,16 @@ import {
|
|
|
13
13
|
|
|
14
14
|
const ButtonComponent = ({
|
|
15
15
|
buttonType = 'link',
|
|
16
|
-
variant = '
|
|
16
|
+
variant = 'primaryButton',
|
|
17
17
|
className = '',
|
|
18
18
|
style = {},
|
|
19
19
|
...rest
|
|
20
20
|
}: ButtonProps) => {
|
|
21
|
-
const
|
|
22
|
-
const defaultClasses = cx(styles.button, variantClass);
|
|
21
|
+
const defaultClasses = cx(styles.button, className);
|
|
23
22
|
const sharedProps = {
|
|
24
|
-
className:
|
|
23
|
+
className: defaultClasses,
|
|
25
24
|
style: style,
|
|
25
|
+
'data-button-variant': variant,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const RenderWithIcon = ({
|
|
@@ -37,7 +37,7 @@ Button/
|
|
|
37
37
|
**Base Props (shared by both types):**
|
|
38
38
|
```tsx
|
|
39
39
|
{
|
|
40
|
-
variant?: '
|
|
40
|
+
variant?: 'primaryButton' | 'secondaryButton' | 'tertiaryButton' | 'accentButton'; // Visual style variant (connected to theme settings)
|
|
41
41
|
className?: string; // Additional CSS classes
|
|
42
42
|
style?: React.CSSProperties; // Inline styles
|
|
43
43
|
children?: React.ReactNode; // Button text/content
|
|
@@ -108,7 +108,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
108
108
|
```tsx
|
|
109
109
|
<Button
|
|
110
110
|
buttonType="link"
|
|
111
|
-
variant="
|
|
111
|
+
variant="primaryButton"
|
|
112
112
|
href="https://www.hubspot.com"
|
|
113
113
|
target="_self"
|
|
114
114
|
>
|
|
@@ -121,7 +121,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
121
121
|
```tsx
|
|
122
122
|
<Button
|
|
123
123
|
buttonType="link"
|
|
124
|
-
variant="
|
|
124
|
+
variant="primaryButton"
|
|
125
125
|
href="https://www.hubspot.com"
|
|
126
126
|
target="_blank"
|
|
127
127
|
rel="noopener noreferrer"
|
|
@@ -135,7 +135,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
135
135
|
```tsx
|
|
136
136
|
<Button
|
|
137
137
|
buttonType="button"
|
|
138
|
-
variant="
|
|
138
|
+
variant="primaryButton"
|
|
139
139
|
onClick={handleClick}
|
|
140
140
|
showIcon={true}
|
|
141
141
|
iconFieldPath="icon"
|
|
@@ -152,7 +152,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
152
152
|
```tsx
|
|
153
153
|
<Button
|
|
154
154
|
buttonType="link"
|
|
155
|
-
variant="
|
|
155
|
+
variant="secondaryButton"
|
|
156
156
|
href="/back"
|
|
157
157
|
showIcon={true}
|
|
158
158
|
iconFieldPath="icon"
|
|
@@ -170,7 +170,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
170
170
|
```tsx
|
|
171
171
|
<Button
|
|
172
172
|
buttonType="button"
|
|
173
|
-
variant="
|
|
173
|
+
variant="primaryButton"
|
|
174
174
|
disabled={true}
|
|
175
175
|
onClick={handleClick}
|
|
176
176
|
>
|
|
@@ -183,7 +183,7 @@ import { Island } from '@hubspot/cms-components'
|
|
|
183
183
|
```tsx
|
|
184
184
|
<Button
|
|
185
185
|
buttonType="link"
|
|
186
|
-
variant="
|
|
186
|
+
variant="primaryButton"
|
|
187
187
|
href="/custom"
|
|
188
188
|
className="custom-button-class"
|
|
189
189
|
style={{ maxWidth: '300px' }}
|
|
@@ -240,7 +240,7 @@ Configurable props for variant and icon position:
|
|
|
240
240
|
<Button.StyleFields
|
|
241
241
|
buttonVariantLabel="Button variant"
|
|
242
242
|
buttonVariantName="buttonVariant"
|
|
243
|
-
buttonVariantDefault=
|
|
243
|
+
buttonVariantDefault={{ variant_name: 'primaryButton' }}
|
|
244
244
|
buttonIconPositionLabel="Icon position"
|
|
245
245
|
buttonIconPositionName="buttonIconPosition"
|
|
246
246
|
buttonIconPositionDefault="right"
|
|
@@ -248,7 +248,7 @@ Configurable props for variant and icon position:
|
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
**Fields:**
|
|
251
|
-
- `buttonVariant`:
|
|
251
|
+
- `buttonVariant`: VariantSelectionField connected to theme settings (primaryButton, secondaryButton, tertiaryButton, accentButton)
|
|
252
252
|
- `buttonIconPosition`: ChoiceField for selecting icon placement (left, right). This field has a hardcoded visibility rule — it is only shown when the icon toggle is enabled (`iconComponentShowIcon === true`), which corresponds to the `BooleanField` id hardcoded in `Icon.ContentFields`.
|
|
253
253
|
|
|
254
254
|
### Module Usage Example
|
|
@@ -276,57 +276,44 @@ export default function CTAModule({ fieldValues }) {
|
|
|
276
276
|
|
|
277
277
|
## Styling
|
|
278
278
|
|
|
279
|
-
### CSS Variables
|
|
280
|
-
|
|
281
|
-
The Button component uses CSS variables for theming and customization:
|
|
282
|
-
|
|
283
|
-
**Base Styles:**
|
|
284
|
-
- `--hscl-button-gap`: Space between text and icon (default: 8px)
|
|
285
|
-
- `--hscl-button-backgroundColor`: Background color
|
|
286
|
-
- `--hscl-button-color`: Text color
|
|
287
|
-
- `--hscl-button-paddingBlock`: Vertical padding
|
|
288
|
-
- `--hscl-button-paddingInline`: Horizontal padding
|
|
289
|
-
- `--hscl-button-borderRadius`: Border radius
|
|
290
|
-
- `--hscl-button-borderWidth`: Border width
|
|
291
|
-
- `--hscl-button-borderColor`: Border color
|
|
292
|
-
- `--hscl-button-fontSize`: Font size
|
|
293
|
-
- `--hscl-button-fontWeight`: Font weight
|
|
294
|
-
|
|
295
|
-
**Hover States:**
|
|
296
|
-
- `--hscl-button-backgroundColor-hover`: Hover background color
|
|
297
|
-
- `--hscl-button-color-hover`: Hover text color
|
|
298
|
-
- `--hscl-button-borderWidth-hover`: Hover border width
|
|
299
|
-
- `--hscl-button-borderColor-hover`: Hover border color
|
|
300
|
-
|
|
301
|
-
**Focus States:**
|
|
302
|
-
- `--hscl-button-backgroundColor-focus`: Focus background color
|
|
303
|
-
- `--hscl-button-color-focus`: Focus text color
|
|
304
|
-
- `--hscl-button-borderWidth-focus`: Focus border width
|
|
305
|
-
- `--hscl-button-borderColor-focus`: Focus border color
|
|
306
|
-
- `--hscl-button-outlineWidth-focus`: Focus outline width
|
|
307
|
-
- `--hscl-button-outlineColor-focus`: Focus outline color
|
|
308
|
-
- `--hscl-button-outlineOffset-focus`: Focus outline offset
|
|
309
|
-
|
|
310
|
-
**Icon:**
|
|
311
|
-
- `--hscl-button-icon-fill`: Icon fill color
|
|
312
|
-
|
|
313
|
-
### Custom Styling Example
|
|
279
|
+
### Theme CSS Variables
|
|
314
280
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
281
|
+
The Button component is themed via `--hs-button-*` CSS variables, which are provided by the theme settings variant system (scoped via the variant prop and `data-buttons-variant` attribute).
|
|
282
|
+
|
|
283
|
+
**Base Styles (from theme):**
|
|
284
|
+
- `--hs-button-backgroundColor`: Background color
|
|
285
|
+
- `--hs-button-color`: Text color
|
|
286
|
+
- `--hs-button-borderRadius`: Border radius
|
|
287
|
+
- `--hs-button-border`: Border shorthand
|
|
288
|
+
- `--hs-button-fontSize`: Font size
|
|
289
|
+
- `--hs-button-fontFamily`: Font family
|
|
290
|
+
- `--hs-button-fontStyle`: Font style
|
|
291
|
+
- `--hs-button-fontWeight`: Font weight
|
|
292
|
+
- `--hs-button-textDecoration`: Text decoration
|
|
293
|
+
|
|
294
|
+
**Hover States (from theme):**
|
|
295
|
+
- `--hs-button-backgroundColor-hover`: Hover background (falls back to base)
|
|
296
|
+
- `--hs-button-color-hover`: Hover text color (falls back to base)
|
|
297
|
+
- `--hs-button-border-hover`: Hover border (falls back to base)
|
|
298
|
+
|
|
299
|
+
**Focus States (from theme):**
|
|
300
|
+
- `--hs-button-backgroundColor-focus`: Focus background (falls back to base)
|
|
301
|
+
- `--hs-button-color-focus`: Focus text color (falls back to base)
|
|
302
|
+
- `--hs-button-border-focus`: Focus border (falls back to base)
|
|
303
|
+
|
|
304
|
+
**Active States (from theme):**
|
|
305
|
+
- `--hs-button-backgroundColor-active`: Active background (falls back to base)
|
|
306
|
+
- `--hs-button-color-active`: Active text color (falls back to base)
|
|
307
|
+
- `--hs-button-border-active`: Active border (falls back to base)
|
|
308
|
+
|
|
309
|
+
### Hardcoded Layout Values
|
|
310
|
+
|
|
311
|
+
The following properties use fixed values and are not configurable via CSS variables:
|
|
312
|
+
|
|
313
|
+
- **Gap:** 8px (space between text and icon)
|
|
314
|
+
- **Padding:** 12px vertical, 24px horizontal
|
|
315
|
+
- **Focus outline:** 2px solid currentColor, offset 2px
|
|
316
|
+
- **Icon fill:** currentColor
|
|
330
317
|
|
|
331
318
|
## Accessibility
|
|
332
319
|
|
|
@@ -359,7 +346,7 @@ The Button component follows accessibility best practices:
|
|
|
359
346
|
- **Icon purpose**: Use `SEMANTIC` when the icon adds meaning, `DECORATIVE` when it's purely visual
|
|
360
347
|
- **Disabled state**: Only use with `buttonType="button"`, not applicable to links
|
|
361
348
|
- **CSS Variables**: Override design tokens using CSS variables rather than hardcoding values
|
|
362
|
-
- **Variant system**:
|
|
349
|
+
- **Variant system**: Variants are connected to theme settings via `VariantSelectionField` and `data-variant-name` attribute. CSS variables are scoped automatically by the ContentUILib variant system.
|
|
363
350
|
|
|
364
351
|
## Common Patterns
|
|
365
352
|
|
|
@@ -368,7 +355,7 @@ The Button component follows accessibility best practices:
|
|
|
368
355
|
```tsx
|
|
369
356
|
<Button
|
|
370
357
|
buttonType="link"
|
|
371
|
-
variant="
|
|
358
|
+
variant="primaryButton"
|
|
372
359
|
href="/signup"
|
|
373
360
|
showIcon={true}
|
|
374
361
|
iconFieldPath="icon"
|
|
@@ -383,7 +370,7 @@ The Button component follows accessibility best practices:
|
|
|
383
370
|
```tsx
|
|
384
371
|
<Button
|
|
385
372
|
buttonType="button"
|
|
386
|
-
variant="
|
|
373
|
+
variant="primaryButton"
|
|
387
374
|
onClick={handleSubmit}
|
|
388
375
|
disabled={isSubmitting}
|
|
389
376
|
>
|
|
@@ -396,7 +383,7 @@ The Button component follows accessibility best practices:
|
|
|
396
383
|
```tsx
|
|
397
384
|
<Button
|
|
398
385
|
buttonType="link"
|
|
399
|
-
variant="
|
|
386
|
+
variant="secondaryButton"
|
|
400
387
|
href="/dashboard"
|
|
401
388
|
showIcon={true}
|
|
402
389
|
iconFieldPath="icon"
|
|
@@ -411,7 +398,7 @@ The Button component follows accessibility best practices:
|
|
|
411
398
|
```tsx
|
|
412
399
|
<Button
|
|
413
400
|
buttonType="link"
|
|
414
|
-
variant="
|
|
401
|
+
variant="tertiaryButton"
|
|
415
402
|
href="https://docs.hubspot.com"
|
|
416
403
|
target="_blank"
|
|
417
404
|
rel="noopener noreferrer"
|
|
@@ -26,7 +26,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
26
26
|
|
|
27
27
|
export const Default: Story = {
|
|
28
28
|
args: {
|
|
29
|
-
variant: '
|
|
29
|
+
variant: 'primaryButton',
|
|
30
30
|
children: 'Click me',
|
|
31
31
|
onClick: () => alert('Button clicked!'),
|
|
32
32
|
},
|
|
@@ -34,7 +34,7 @@ export const Default: Story = {
|
|
|
34
34
|
|
|
35
35
|
export const WithIcon: Story = {
|
|
36
36
|
args: {
|
|
37
|
-
variant: '
|
|
37
|
+
variant: 'primaryButton',
|
|
38
38
|
children: 'Click me',
|
|
39
39
|
onClick: () => alert('Button clicked!'),
|
|
40
40
|
showIcon: true,
|
|
@@ -26,7 +26,7 @@ type Story = StoryObj<typeof meta>;
|
|
|
26
26
|
|
|
27
27
|
export const Default: Story = {
|
|
28
28
|
args: {
|
|
29
|
-
variant: '
|
|
29
|
+
variant: 'primaryButton',
|
|
30
30
|
children: 'Click me',
|
|
31
31
|
href: 'https://www.hubspot.com',
|
|
32
32
|
},
|
|
@@ -34,7 +34,7 @@ export const Default: Story = {
|
|
|
34
34
|
|
|
35
35
|
export const WithIcon: Story = {
|
|
36
36
|
args: {
|
|
37
|
-
variant: '
|
|
37
|
+
variant: 'primaryButton',
|
|
38
38
|
children: 'Click me',
|
|
39
39
|
href: 'https://www.hubspot.com',
|
|
40
40
|
showIcon: true,
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
.buttonContainer {
|
|
2
|
-
padding:
|
|
3
|
-
--
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
--
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
--
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--hscl-button-outlineWidth-focus: 2px;
|
|
22
|
-
--hscl-button-outlineColor-focus: #0066cc;
|
|
23
|
-
--hscl-button-outlineOffset-focus: 2px;
|
|
24
|
-
--hscl-button-icon-fill: currentColor;
|
|
2
|
+
padding: 20px;
|
|
3
|
+
--hs-button-backgroundColor: #0066cc;
|
|
4
|
+
--hs-button-color: #ffffff;
|
|
5
|
+
--hs-button-borderRadius: 4px;
|
|
6
|
+
--hs-button-border: 1px solid transparent;
|
|
7
|
+
--hs-button-fontSize: 14px;
|
|
8
|
+
--hs-button-fontFamily: inherit;
|
|
9
|
+
--hs-button-fontStyle: normal;
|
|
10
|
+
--hs-button-fontWeight: 500;
|
|
11
|
+
--hs-button-textDecoration: none;
|
|
12
|
+
--hs-button-backgroundColor-hover: #0052a3;
|
|
13
|
+
--hs-button-color-hover: #ffffff;
|
|
14
|
+
--hs-button-border-hover: 1px solid transparent;
|
|
15
|
+
--hs-button-backgroundColor-focus: #0066cc;
|
|
16
|
+
--hs-button-color-focus: #ffffff;
|
|
17
|
+
--hs-button-border-focus: 1px solid transparent;
|
|
18
|
+
--hs-button-backgroundColor-active: #004080;
|
|
19
|
+
--hs-button-color-active: #ffffff;
|
|
20
|
+
--hs-button-border-active: 1px solid transparent;
|
|
25
21
|
}
|
|
@@ -8,7 +8,7 @@ export type IconPositionHorizontal = 'left' | 'right';
|
|
|
8
8
|
|
|
9
9
|
// Base props shared by all variants
|
|
10
10
|
export type BaseButtonProps = {
|
|
11
|
-
variant?: '
|
|
11
|
+
variant?: 'primaryButton' | 'secondaryButton' | 'tertiaryButton' | 'accentButton';
|
|
12
12
|
className?: string;
|
|
13
13
|
style?: React.CSSProperties;
|
|
14
14
|
children?: React.ReactNode;
|
|
@@ -57,7 +57,7 @@ export type ContentFieldsProps = Omit<
|
|
|
57
57
|
export type StyleFieldsProps = {
|
|
58
58
|
buttonVariantLabel?: string;
|
|
59
59
|
buttonVariantName?: string;
|
|
60
|
-
buttonVariantDefault?: string;
|
|
60
|
+
buttonVariantDefault?: { variant_name: string };
|
|
61
61
|
buttonIconPositionLabel?: string;
|
|
62
62
|
buttonIconPositionName?: string;
|
|
63
63
|
buttonIconPositionDefault?: IconPositionHorizontal;
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { VariantSelectionField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import { StyleFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
4
|
const StyleFields = ({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
cardVariantLabel = 'Card variant',
|
|
6
|
+
cardVariantName = 'cardVariant',
|
|
7
|
+
cardVariantDefault = { variantName: 'cardStyle1' },
|
|
8
8
|
}: StyleFieldsProps) => {
|
|
9
9
|
return (
|
|
10
|
-
<
|
|
11
|
-
label={
|
|
12
|
-
name={
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
['elevated', 'Elevated (with shadow)'],
|
|
16
|
-
['outlined', 'Outlined (with border)'],
|
|
17
|
-
['filled', 'Filled (solid background)'],
|
|
18
|
-
]}
|
|
19
|
-
default={variantDefault}
|
|
10
|
+
<VariantSelectionField
|
|
11
|
+
label={cardVariantLabel}
|
|
12
|
+
name={cardVariantName}
|
|
13
|
+
variantDefinitionName="card"
|
|
14
|
+
default={cardVariantDefault}
|
|
20
15
|
/>
|
|
21
16
|
);
|
|
22
17
|
};
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
4
|
box-sizing: border-box;
|
|
5
|
-
padding:
|
|
6
|
-
border-radius: var(--
|
|
7
|
-
background-color: var(--
|
|
8
|
-
|
|
9
|
-
border: var(--
|
|
5
|
+
padding: 24px;
|
|
6
|
+
border-radius: var(--hs-card-borderRadius, 8px);
|
|
7
|
+
background-color: var(--hs-card-backgroundColor, #ffffff);
|
|
8
|
+
color: var(--hs-card-color, inherit);
|
|
9
|
+
border: var(--hs-card-border, none);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
h1,h2,h3,h4,h5,h6,p,span {
|
|
12
|
+
color: inherit;
|
|
13
13
|
}
|
|
14
14
|
}
|